From 26d94db6618a92f1c741810df2352b50f4b4c025 Mon Sep 17 00:00:00 2001
From: Adam Pocock <craigacp@gmail.com>
Date: Sat, 4 Apr 2020 21:39:00 -0400
Subject: [PATCH 001/586] Update Mage.Client, Mage.Server.Console, and
 AbilityPicker LaF to use the supported class name.

---
 Mage.Client/src/main/java/mage/client/MageFrame.java       | 2 +-
 .../src/main/java/mage/client/components/MageJDesktop.java | 7 +------
 .../java/mage/client/components/ability/AbilityPicker.java | 2 +-
 .../src/main/java/mage/server/console/ConsoleFrame.java    | 2 +-
 4 files changed, 4 insertions(+), 9 deletions(-)

diff --git a/Mage.Client/src/main/java/mage/client/MageFrame.java b/Mage.Client/src/main/java/mage/client/MageFrame.java
index 0117e7e325..520014b62d 100644
--- a/Mage.Client/src/main/java/mage/client/MageFrame.java
+++ b/Mage.Client/src/main/java/mage/client/MageFrame.java
@@ -202,7 +202,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
         config.setAccessPreference(FsAccessOption.STORE, true);
         try {
             UIManager.put("desktop", new Color(0, 0, 0, 0));
-            UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
+            UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
             //UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
             // stop JSplitPane from eating F6 and F8 or any other function keys
             {
diff --git a/Mage.Client/src/main/java/mage/client/components/MageJDesktop.java b/Mage.Client/src/main/java/mage/client/components/MageJDesktop.java
index ca74f6196f..5d6ee1df71 100644
--- a/Mage.Client/src/main/java/mage/client/components/MageJDesktop.java
+++ b/Mage.Client/src/main/java/mage/client/components/MageJDesktop.java
@@ -1,10 +1,5 @@
 package mage.client.components;
 
-//import com.sun.java.swing.Painter;
-
-import java.awt.Color;
-import java.awt.Graphics2D;
-
 import javax.swing.JDesktopPane;
 import javax.swing.UIDefaults;
 import javax.swing.UIManager;
@@ -22,7 +17,7 @@ public class MageJDesktop extends JDesktopPane {
     public void updateUI() {
         if ("Nimbus".equals(UIManager.getLookAndFeel().getName())) {
             UIDefaults map = new UIDefaults();
-            Painter painter = (g, c, w, h) -> {
+            Painter<?> painter = (g, c, w, h) -> {
                 g.setColor( UIManager.getDefaults().getColor("desktop") );
                 g.fillRect(0,0,w,h);
             };
diff --git a/Mage.Client/src/main/java/mage/client/components/ability/AbilityPicker.java b/Mage.Client/src/main/java/mage/client/components/ability/AbilityPicker.java
index 7dc263ddc4..df03dc72fc 100644
--- a/Mage.Client/src/main/java/mage/client/components/ability/AbilityPicker.java
+++ b/Mage.Client/src/main/java/mage/client/components/ability/AbilityPicker.java
@@ -383,7 +383,7 @@ public class AbilityPicker extends JXPanel implements MouseWheelListener {
 
     public static void main(String[] argv) {
         try {
-            UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
+            UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
         } catch (Exception ex) {
         }
 
diff --git a/Mage.Server.Console/src/main/java/mage/server/console/ConsoleFrame.java b/Mage.Server.Console/src/main/java/mage/server/console/ConsoleFrame.java
index df46fb279e..4ed3d543a1 100644
--- a/Mage.Server.Console/src/main/java/mage/server/console/ConsoleFrame.java
+++ b/Mage.Server.Console/src/main/java/mage/server/console/ConsoleFrame.java
@@ -60,7 +60,7 @@ public class ConsoleFrame extends javax.swing.JFrame implements MageClient {
 
         initComponents();
         try {
-            UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
+            UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
             session = new SessionImpl(this);
             connectDialog = new ConnectDialog();
         } catch (Exception ex) {

From 325d96f6db74a4b979bd2b83e24c6a2da2ba2375 Mon Sep 17 00:00:00 2001
From: SpeedProg <speedprogde@googlemail.com>
Date: Fri, 1 May 2020 17:47:00 +0200
Subject: [PATCH 002/586] display ToolTipMarkTags in card renders in blue
 instead of leaving the closing </font> tag in the text

---
 .../org/mage/card/arcane/TextboxRule.java     | 20 +++++++++++++++++++
 .../mage/card/arcane/TextboxRuleParser.java   |  6 ++++++
 2 files changed, 26 insertions(+)

diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/TextboxRule.java b/Mage.Client/src/main/java/org/mage/card/arcane/TextboxRule.java
index 9dafbcb32b..371c8158e2 100644
--- a/Mage.Client/src/main/java/org/mage/card/arcane/TextboxRule.java
+++ b/Mage.Client/src/main/java/org/mage/card/arcane/TextboxRule.java
@@ -7,6 +7,7 @@ package org.mage.card.arcane;
 
 import java.awt.Font;
 import java.awt.Image;
+import java.awt.Paint;
 import java.awt.font.GraphicAttribute;
 import java.awt.font.ImageGraphicAttribute;
 import java.awt.font.TextAttribute;
@@ -47,6 +48,25 @@ public class TextboxRule {
         }
     }
 
+    public static class ColorRegion implements AttributeRegion {
+
+        ColorRegion(int start, int end, Paint color) {
+            this.start = start;
+            this.end = end;
+            this.color = color;
+        }
+        private final int start;
+        private final int end;
+        private final Paint color;
+
+        @Override
+        public void applyToAttributedString(AttributedString str, Font normal, Font italic) {
+            if (end > start + 1) {
+                str.addAttribute(TextAttribute.FOREGROUND, color, start, end);
+            }
+        }
+    }
+
     // A special symbol embedded at some point in a string
     public static class EmbeddedSymbol implements AttributeRegion {
 
diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/TextboxRuleParser.java b/Mage.Client/src/main/java/org/mage/card/arcane/TextboxRuleParser.java
index e89873a670..a9ba60ed4f 100644
--- a/Mage.Client/src/main/java/org/mage/card/arcane/TextboxRuleParser.java
+++ b/Mage.Client/src/main/java/org/mage/card/arcane/TextboxRuleParser.java
@@ -23,6 +23,7 @@ public final class TextboxRuleParser {
     private static final Pattern LevelAbilityPattern = Pattern.compile("Level (\\d+)-?(\\d*)(\\+?)");
     private static final Pattern LoyaltyAbilityPattern = Pattern.compile("^(\\+|\\-)(\\d+|X): ");
     private static final Pattern SimpleKeywordPattern = Pattern.compile("^(\\w+( \\w+)?)\\s*(\\([^\\)]*\\))?\\s*$");
+    private static final Pattern FontColorValuePattern = Pattern.compile("color\\s*=\\s*[\"'](\\w+)[\"']");
 
     // Parse a given rule (given as a string) into a TextboxRule, replacing
     // symbol annotations, italics, etc, parsing out information such as
@@ -200,6 +201,11 @@ public final class TextboxRuleParser {
                                         }
                                     }
                                     break;
+                                case "/font":
+                                    // Font it is an additional info of a card
+                                    // lets make it blue like it is in tooltip
+                                    regions.add(new TextboxRule.ColorRegion(openingIndex, outputIndex, Color.BLUE));
+                                    break;
                                 default:
                                     // Unknown
                                     build.append('<').append(tag).append('>');

From 9597e95b8dc4582f84c35c48aab830126a26b06b Mon Sep 17 00:00:00 2001
From: SpeedProg <speedprogde@googlemail.com>
Date: Sat, 2 May 2020 16:34:04 +0200
Subject: [PATCH 003/586] fixed TargetCardInOpponentsGraveyard not working
 properly fixed ignoring allFromOneOpponent in canChoose fixed including own
 graveyard in canChoose fixed including own graveyard for possibleTargets
 fixed not overriding one canTarget at all which allowed targeting cards in
 different graveyards should also fix #6472

---
 .../TargetCardInOpponentsGraveyard.java       | 54 +++++++++++++++++++
 1 file changed, 54 insertions(+)

diff --git a/Mage/src/main/java/mage/target/common/TargetCardInOpponentsGraveyard.java b/Mage/src/main/java/mage/target/common/TargetCardInOpponentsGraveyard.java
index 4ebbfa167f..84d34020f8 100644
--- a/Mage/src/main/java/mage/target/common/TargetCardInOpponentsGraveyard.java
+++ b/Mage/src/main/java/mage/target/common/TargetCardInOpponentsGraveyard.java
@@ -1,8 +1,11 @@
 package mage.target.common;
 
+import java.util.HashSet;
+import java.util.Set;
 import java.util.UUID;
 import mage.abilities.Ability;
 import mage.cards.Card;
+import mage.cards.Cards;
 import mage.constants.Zone;
 import mage.filter.FilterCard;
 import mage.game.Game;
@@ -33,6 +36,23 @@ public class TargetCardInOpponentsGraveyard extends TargetCard {
         this.allFromOneOpponent = target.allFromOneOpponent;
     }
 
+    @Override
+    public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) {
+        Card card = game.getCard(id);
+        if (card != null && zone.match(game.getState().getZone(id))) {
+            if (game.getPlayer(source.getControllerId()).hasOpponent(card.getOwnerId(), game)) {
+                if (allFromOneOpponent && !targets.isEmpty()) {
+                    Card firstCard = game.getCard(targets.keySet().iterator().next());
+                    if (firstCard != null && !card.isOwnedBy(firstCard.getOwnerId())) {
+                        return false;
+                    }
+                }
+                return filter.match(card, source.getId(), playerId, game);
+            }
+        }
+        return false;
+    }
+
     @Override
     public boolean canTarget(UUID id, Ability source, Game game) {
         Card card = game.getCard(id);
@@ -69,7 +89,14 @@ public class TargetCardInOpponentsGraveyard extends TargetCard {
         if (getNumberOfTargets() == 0) { // if 0 target is valid, the canChoose is always true
             return true;
         }
+        Player sourceController = game.getPlayer(sourceControllerId);
         for (UUID playerId: game.getState().getPlayersInRange(sourceControllerId, game)) {
+            if (!sourceController.hasOpponent(playerId, game)) {
+                continue;
+            }
+            if (this.allFromOneOpponent) {
+                possibleTargets = 0;
+            }
             if (!playerId.equals(sourceControllerId)) {
                 Player player = game.getPlayer(playerId);
                 if (player != null) {
@@ -86,6 +113,33 @@ public class TargetCardInOpponentsGraveyard extends TargetCard {
         }
         return false;
     }
+
+    @Override
+    public Set<UUID> possibleTargets(UUID sourceId, UUID sourceControllerId, Game game) {
+        Set<UUID> possibleTargets = new HashSet<>();
+        Player sourceController = game.getPlayer(sourceControllerId);
+        for (UUID playerId : game.getState().getPlayersInRange(sourceControllerId, game)) {
+            if (!sourceController.hasOpponent(playerId, game)) {
+                continue;
+            }
+            Player player = game.getPlayer(playerId);
+            if (player != null) {
+                Set<UUID> targetsInThisGraveyeard = new HashSet<>();
+                for (Card card : player.getGraveyard().getCards(filter, sourceId, sourceControllerId, game)) {
+                    if (sourceId == null || isNotTarget() || !game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.TARGET, card.getId(), sourceId, sourceControllerId))) {
+                        targetsInThisGraveyeard.add(card.getId());
+                    }
+                }
+                // if there is not enough possible targets, the can't be any
+                if (this.allFromOneOpponent && targetsInThisGraveyeard.size() < this.minNumberOfTargets) {
+                    continue;
+                }
+                possibleTargets.addAll(targetsInThisGraveyeard);
+            }
+        }
+        return possibleTargets;
+    }
+
     @Override
     public TargetCardInOpponentsGraveyard copy() {
         return new TargetCardInOpponentsGraveyard(this);

From eba1980a9abe24ee06485ac4e221d22f1c9f1d56 Mon Sep 17 00:00:00 2001
From: gp66 <garyprince66@hotmail.com>
Date: Sat, 2 May 2020 21:52:33 -0500
Subject: [PATCH 004/586] added IKO tokens

---
 .../card/dl/sources/ScryfallImageSupportTokens.java  | 12 ++++++++++++
 Mage.Client/src/main/resources/card-pictures-tok.txt | 12 +++++++++++-
 Mage.Sets/src/mage/cards/q/QuartzwoodCrasher.java    |  3 ++-
 3 files changed, 25 insertions(+), 2 deletions(-)

diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java
index 242acb7306..14ece56096 100644
--- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java
@@ -350,6 +350,18 @@ public class ScryfallImageSupportTokens {
             put("THB/Wolf", "https://api.scryfall.com/cards/tthb/11/en?format=image");
             put("THB/Zombie", "https://api.scryfall.com/cards/tthb/7/en?format=image");
 
+            // IKO
+            put("IKO/Emblem Narset Of The Ancient Way", "https://api.scryfall.com/cards/tiko/12/en?format=image");
+            put("IKO/Beast", "https://api.scryfall.com/cards/tiko/10/en?format=image");
+            put("IKO/Cat Bird", "https://api.scryfall.com/cards/tiko/2/en?format=image");
+            put("IKO/Cat", "https://api.scryfall.com/cards/tiko/1/en?format=image");
+            put("IKO/Dinosaur Beast", "https://api.scryfall.com/cards/tiko/11/en?format=image");
+            put("IKO/Dinosaur", "https://api.scryfall.com/cards/tiko/8/en?format=image");
+            put("IKO/Feather", "https://api.scryfall.com/cards/tiko/9/en?format=image");
+            put("IKO/Human Soldier", "https://api.scryfall.com/cards/tiko/4/en?format=image");
+            put("IKO/Kraken", "https://api.scryfall.com/cards/tiko/6/en?format=image");
+            put("IKO/Shark", "https://api.scryfall.com/cards/tiko/7/en?format=image");
+
             // PCA (planes)
             put("PCA/Eldrazi", "https://api.scryfall.com/cards/tpca/1/en?format=image");
             put("PCA/Plane - Academy at Tolaria West", "https://api.scryfall.com/cards/opca/9/en?format=image");
diff --git a/Mage.Client/src/main/resources/card-pictures-tok.txt b/Mage.Client/src/main/resources/card-pictures-tok.txt
index 9d1c954caf..b327471863 100644
--- a/Mage.Client/src/main/resources/card-pictures-tok.txt
+++ b/Mage.Client/src/main/resources/card-pictures-tok.txt
@@ -101,6 +101,7 @@
 |Generate|EMBLEM:DOM|Teferi, Hero of Dominaria||Emblem Teferi|TeferiHeroOfDominariaEmblem|
 |Generate|EMBLEM:AER|Tezzeret the Schemer||Emblem Tezzeret|TezzeretTheSchemerEmblem|
 |Generate|EMBLEM:ELD|Garruk, Cursed Huntsman||Emblem Garruk|GarrukCursedHuntsmanEmblem|
+|Generate|EMBLEM:IKO|Narset Of The Ancient Way||Emblem Narset|NarsetOfTheAncientWayEmblem|
 |Generate|PLANE:PCA|Plane - Academy at Tolaria West|||AcademyAtTolariaWestPlane|
 |Generate|PLANE:PCA|Plane - Agyrem|||AgyremPlane|
 |Generate|PLANE:PCA|Plane - Akoum|||AkoumPlane|
@@ -1357,4 +1358,13 @@
 |Generate|TOK:THB|Wolf|||WolfToken|
 |Generate|TOK:THB|Nightmare|||AshiokNightmareMuseToken|
 |Generate|TOK:THB|GoldToken|||Gold|
-|Generate|TOK:THB|ArtifactWallToken|||Wall|
\ No newline at end of file
+|Generate|TOK:THB|ArtifactWallToken|||Wall|
+|Generate|TOK:IKO|Beast|||BeastToken|
+|Generate|TOK:IKO|Cat Bird|||CatBirdToken|
+|Generate|TOK:IKO|Cat|||CatToken|
+|Generate|TOK:IKO|Dinosaur Beast|||DinosaurBeastToken|
+|Generate|TOK:IKO|Dinosaur|||DinosaurHasteToken|
+|Generate|TOK:IKO|Feather|||FeatherToken|
+|Generate|TOK:IKO|Human Soldier|||HumanSoldierToken|
+|Generate|TOK:IKO|Kraken|||KrakenToken|
+|Generate|TOK:IKO|Shark|||SharkToken|
diff --git a/Mage.Sets/src/mage/cards/q/QuartzwoodCrasher.java b/Mage.Sets/src/mage/cards/q/QuartzwoodCrasher.java
index 3316de6703..aa00632951 100644
--- a/Mage.Sets/src/mage/cards/q/QuartzwoodCrasher.java
+++ b/Mage.Sets/src/mage/cards/q/QuartzwoodCrasher.java
@@ -13,6 +13,7 @@ import mage.game.events.DamagedPlayerEvent;
 import mage.game.events.GameEvent;
 import mage.game.permanent.Permanent;
 import mage.game.permanent.token.BeastXToken;
+import mage.game.permanent.token.DinosaurBeastToken;
 import mage.target.targetpointer.FixedTarget;
 import mage.watchers.Watcher;
 
@@ -119,7 +120,7 @@ class QuartzwoodCrasherEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         QuartzwoodCrasherWatcher watcher = game.getState().getWatcher(QuartzwoodCrasherWatcher.class);
-        return watcher != null && new BeastXToken(
+        return watcher != null && new DinosaurBeastToken(
                 watcher.getDamage(targetPointer.getFirst(game, source), source.getControllerId())
         ).putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId());
     }

From 8bea930a69145c93b1c7e03d758a2d0a8ae65147 Mon Sep 17 00:00:00 2001
From: gp66 <garyprince66@hotmail.com>
Date: Sat, 2 May 2020 21:59:22 -0500
Subject: [PATCH 005/586] added IKO tokens

---
 .../permanent/token/DinosaurBeastToken.java   | 33 +++++++++++++++++++
 1 file changed, 33 insertions(+)
 create mode 100644 Mage/src/main/java/mage/game/permanent/token/DinosaurBeastToken.java

diff --git a/Mage/src/main/java/mage/game/permanent/token/DinosaurBeastToken.java b/Mage/src/main/java/mage/game/permanent/token/DinosaurBeastToken.java
new file mode 100644
index 0000000000..6c0ab5730b
--- /dev/null
+++ b/Mage/src/main/java/mage/game/permanent/token/DinosaurBeastToken.java
@@ -0,0 +1,33 @@
+
+package mage.game.permanent.token;
+
+import mage.constants.CardType;
+import mage.MageInt;
+import mage.abilities.keyword.TrampleAbility;
+import mage.constants.SubType;
+
+/**
+ *
+ * @author TheElk801
+ */
+public final class DinosaurBeastToken extends TokenImpl {
+
+    public DinosaurBeastToken(int xValue) {
+        super("Dinosaur Beast", "X/X green Dinosaur Beast creature token with trample");
+        cardType.add(CardType.CREATURE);
+        color.setGreen(true);
+        subtype.add(SubType.DINOSAUR);
+        subtype.add(SubType.BEAST);
+        power = new MageInt(xValue);
+        toughness = new MageInt(xValue);
+        addAbility(TrampleAbility.getInstance());
+    }
+
+    public DinosaurBeastToken(final DinosaurBeastToken token) {
+        super(token);
+    }
+
+    public DinosaurBeastToken copy() {
+        return new DinosaurBeastToken(this);
+    }
+}

From 724287360c2efd157a829afb4292ed9ecaf1fb75 Mon Sep 17 00:00:00 2001
From: SpeedProg <speedprogde@googlemail.com>
Date: Thu, 30 Apr 2020 12:52:47 +0200
Subject: [PATCH 006/586] Multiplayer test and fix for Angel of Serenity leaves
 battlefied trigger when controlled by other then owner and owner leaves game

---
 .../test/multiplayer/AngelOfSerenityTest.java | 69 +++++++++++++++++++
 Mage/src/main/java/mage/game/GameImpl.java    |  3 +
 .../main/java/mage/players/PlayerImpl.java    |  2 +-
 3 files changed, 73 insertions(+), 1 deletion(-)
 create mode 100644 Mage.Tests/src/test/java/org/mage/test/multiplayer/AngelOfSerenityTest.java

diff --git a/Mage.Tests/src/test/java/org/mage/test/multiplayer/AngelOfSerenityTest.java b/Mage.Tests/src/test/java/org/mage/test/multiplayer/AngelOfSerenityTest.java
new file mode 100644
index 0000000000..e6ac798929
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/multiplayer/AngelOfSerenityTest.java
@@ -0,0 +1,69 @@
+package org.mage.test.multiplayer;
+
+import mage.constants.MultiplayerAttackOption;
+import mage.constants.PhaseStep;
+import mage.constants.RangeOfInfluence;
+import mage.constants.Zone;
+import mage.game.FreeForAll;
+import mage.game.Game;
+import mage.game.GameException;
+import mage.game.mulligan.MulliganType;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestMultiPlayerBase;
+
+import java.io.FileNotFoundException;
+
+public class AngelOfSerenityTest extends CardTestMultiPlayerBase {
+    @Override
+    protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
+        // Start Life = 2
+        Game game = new FreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, MulliganType.GAME_DEFAULT.getMulligan(0), 2);
+        // Player order: A -> D -> C -> B
+        playerA = createPlayer(game, playerA, "PlayerA");
+        playerB = createPlayer(game, playerB, "PlayerB");
+        playerC = createPlayer(game, playerC, "PlayerC");
+        playerD = createPlayer(game, playerD, "PlayerD");
+        return game;
+    }
+
+    /**
+     * In a multiplayer game when Angel of Serenity leaves the Battlefield because the OWNER leaves the game 800.4a,
+     * it should return the exiled creatures if Angel of Serenity is controlled by a player that does not leave,
+     * since then 800.4d does not apply.
+     * 20200417 - 800.4a/800.4d
+     */
+    @Test
+    public void TestAngelOfSerenityTakeoverExileReturn() {
+
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 7); // add mana to cast card to take over Angel
+        addCard(Zone.HAND, playerA, "Agent of Treachery");
+
+
+        addCard(Zone.BATTLEFIELD, playerB, "Plains", 3);
+        addCard(Zone.BATTLEFIELD, playerB, "Dromoka Dunecaster", 3);
+
+        addCard(Zone.BATTLEFIELD, playerC, "Plains", 7);
+        addCard(Zone.HAND, playerC, "Angel of Serenity"); // {4}{W}{W}{W}
+
+
+        // player C turn is 3
+        castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerC, "Angel of Serenity");
+        addTarget(playerC, "Dromoka Dunecaster^Dromoka Dunecaster^Dromoka Dunecaster");
+
+        // player A 2nd turn is 5
+        castSpell(5, PhaseStep.PRECOMBAT_MAIN, playerA, "Agent of Treachery");
+        addTarget(playerA, "Angel of Serenity");
+
+        concede(5, PhaseStep.POSTCOMBAT_MAIN, playerC);
+
+        setStopAt(6, PhaseStep.UPKEEP);
+        execute();
+        assertAllCommandsUsed();
+
+        Assert.assertFalse("Player of Angel of Serenity did not leave the game", playerC.isInGame());
+        assertPermanentCount(playerA, 8);
+        assertPermanentCount(playerB, 3);
+        assertHandCount(playerB, 4);
+    }
+}
diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java
index e367d7f763..b5470ba96f 100644
--- a/Mage/src/main/java/mage/game/GameImpl.java
+++ b/Mage/src/main/java/mage/game/GameImpl.java
@@ -2581,6 +2581,9 @@ public abstract class GameImpl implements Game, Serializable {
                 }
             }
         }
+        for(Card card : toOutside) {
+            rememberLKI(card.getId(), Zone.BATTLEFIELD, card);
+        }
         // needed to send event that permanent leaves the battlefield to allow non stack effects to execute
         player.moveCards(toOutside, Zone.OUTSIDE, null, this);
         // triggered abilities that don't use the stack have to be executed
diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java
index 75fc352ec5..1bc9a3b339 100644
--- a/Mage/src/main/java/mage/players/PlayerImpl.java
+++ b/Mage/src/main/java/mage/players/PlayerImpl.java
@@ -3987,7 +3987,7 @@ public abstract class PlayerImpl implements Player, Serializable {
                 for (Card card : cards) {
                     if (card instanceof Permanent) {
                         game.getBattlefield().removePermanent(card.getId());
-                        ZoneChangeEvent event = new ZoneChangeEvent(card.getId(),
+                        ZoneChangeEvent event = new ZoneChangeEvent((Permanent)card,
                                 (source == null ? null : source.getSourceId()),
                                 byOwner ? card.getOwnerId() : getId(), Zone.BATTLEFIELD, Zone.OUTSIDE, appliedEffects);
                         game.fireEvent(event);

From 111114e338613dbfa05a18adf33ff51eb9e93bf0 Mon Sep 17 00:00:00 2001
From: emerald000 <emerald000@hotmail.com>
Date: Sun, 3 May 2020 09:42:16 -0400
Subject: [PATCH 007/586] Refactor and add hint for "Ability resolved X times"
 Affects Ashling the Pilgrim, Inner-Flame Igniter and Soulbright Flamekin.

---
 .../src/mage/cards/a/AshlingThePilgrim.java   | 30 +++++------
 .../src/mage/cards/i/InnerFlameIgniter.java   | 46 +++-------------
 .../src/mage/cards/s/SoulbrightFlamekin.java  | 20 ++++---
 .../common/AbilityResolutionCount.java        | 39 ++++++++++++++
 .../IfAbilityHasResolvedXTimesEffect.java     | 52 +++++++++++++++++++
 .../common/AbilityResolutionCountHint.java    | 26 ++++++++++
 .../main/java/mage/game/events/GameEvent.java |  3 +-
 .../java/mage/game/stack/StackAbility.java    |  1 +
 .../common/AbilityResolvedWatcher.java        | 15 +++---
 9 files changed, 156 insertions(+), 76 deletions(-)
 create mode 100644 Mage/src/main/java/mage/abilities/dynamicvalue/common/AbilityResolutionCount.java
 create mode 100644 Mage/src/main/java/mage/abilities/effects/common/IfAbilityHasResolvedXTimesEffect.java
 create mode 100644 Mage/src/main/java/mage/abilities/hint/common/AbilityResolutionCountHint.java

diff --git a/Mage.Sets/src/mage/cards/a/AshlingThePilgrim.java b/Mage.Sets/src/mage/cards/a/AshlingThePilgrim.java
index edf24e4da5..9188c0d57e 100644
--- a/Mage.Sets/src/mage/cards/a/AshlingThePilgrim.java
+++ b/Mage.Sets/src/mage/cards/a/AshlingThePilgrim.java
@@ -6,7 +6,9 @@ import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.DamageEverythingEffect;
+import mage.abilities.effects.common.IfAbilityHasResolvedXTimesEffect;
 import mage.abilities.effects.common.counter.AddCountersSourceEffect;
+import mage.abilities.hint.common.AbilityResolutionCountHint;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
@@ -17,7 +19,6 @@ import mage.counters.CounterType;
 import mage.filter.StaticFilters;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
-import mage.players.Player;
 import mage.watchers.common.AbilityResolvedWatcher;
 
 import java.util.UUID;
@@ -40,7 +41,8 @@ public final class AshlingThePilgrim extends CardImpl {
         Ability ability = new SimpleActivatedAbility(
                 new AddCountersSourceEffect(CounterType.P1P1.createInstance()), new ManaCostsImpl("{1}{R}")
         );
-        ability.addEffect(new AshlingThePilgrimEffect());
+        ability.addEffect(new IfAbilityHasResolvedXTimesEffect(Outcome.Damage, 3, new AshlingThePilgrimEffect()));
+        ability.addHint(AbilityResolutionCountHint.instance);
         this.addAbility(ability, new AbilityResolvedWatcher());
     }
 
@@ -58,8 +60,7 @@ class AshlingThePilgrimEffect extends OneShotEffect {
 
     AshlingThePilgrimEffect() {
         super(Outcome.Damage);
-        this.staticText = "If this is the third time this ability has resolved this turn, " +
-                "remove all +1/+1 counters from {this}, and it deals that much damage to each creature and each player";
+        this.staticText = "remove all +1/+1 counters from {this}, and it deals that much damage to each creature and each player";
     }
 
     private AshlingThePilgrimEffect(final AshlingThePilgrimEffect effect) {
@@ -73,20 +74,15 @@ class AshlingThePilgrimEffect extends OneShotEffect {
 
     @Override
     public boolean apply(Game game, Ability source) {
-        Player controller = game.getPlayer(source.getControllerId());
         Permanent sourcePermanent = game.getPermanent(source.getSourceId());
-        AbilityResolvedWatcher watcher = game.getState().getWatcher(AbilityResolvedWatcher.class);
-        if (controller == null
-                || sourcePermanent == null
-                || watcher == null
-                || !watcher.checkActivations(source, game)) {
-            return false;
+        if (sourcePermanent != null) {
+            int counters = sourcePermanent.getCounters(game).getCount(CounterType.P1P1);
+            if (counters < 1) {
+                return false;
+            }
+            sourcePermanent.removeCounters(CounterType.P1P1.createInstance(counters), game);
+            return new DamageEverythingEffect(counters, StaticFilters.FILTER_PERMANENT_CREATURE).apply(game, source);
         }
-        int counters = sourcePermanent.getCounters(game).getCount(CounterType.P1P1);
-        if (counters < 1) {
-            return false;
-        }
-        sourcePermanent.removeCounters(CounterType.P1P1.createInstance(counters), game);
-        return new DamageEverythingEffect(counters, StaticFilters.FILTER_PERMANENT_CREATURE).apply(game, source);
+        return true;
     }
 }
diff --git a/Mage.Sets/src/mage/cards/i/InnerFlameIgniter.java b/Mage.Sets/src/mage/cards/i/InnerFlameIgniter.java
index 86ff0ed941..19b6a685f4 100644
--- a/Mage.Sets/src/mage/cards/i/InnerFlameIgniter.java
+++ b/Mage.Sets/src/mage/cards/i/InnerFlameIgniter.java
@@ -4,9 +4,11 @@ import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
-import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.ContinuousEffect;
+import mage.abilities.effects.common.IfAbilityHasResolvedXTimesEffect;
 import mage.abilities.effects.common.continuous.BoostControlledEffect;
 import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
+import mage.abilities.hint.common.AbilityResolutionCountHint;
 import mage.abilities.keyword.FirstStrikeAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -15,8 +17,6 @@ import mage.constants.Duration;
 import mage.constants.Outcome;
 import mage.constants.SubType;
 import mage.filter.StaticFilters;
-import mage.game.Game;
-import mage.players.Player;
 import mage.watchers.common.AbilityResolvedWatcher;
 
 import java.util.UUID;
@@ -37,7 +37,9 @@ public final class InnerFlameIgniter extends CardImpl {
         Ability ability = new SimpleActivatedAbility(
                 new BoostControlledEffect(1, 0, Duration.EndOfTurn), new ManaCostsImpl("{2}{R}")
         );
-        ability.addEffect(new InnerFlameIgniterEffect());
+        ContinuousEffect effectIf3rdResolution = new GainAbilityControlledEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURES);
+        ability.addEffect(new IfAbilityHasResolvedXTimesEffect(Outcome.AddAbility, 3, effectIf3rdResolution));
+        ability.addHint(AbilityResolutionCountHint.instance);
         this.addAbility(ability, new AbilityResolvedWatcher());
     }
 
@@ -49,38 +51,4 @@ public final class InnerFlameIgniter extends CardImpl {
     public InnerFlameIgniter copy() {
         return new InnerFlameIgniter(this);
     }
-}
-
-class InnerFlameIgniterEffect extends OneShotEffect {
-
-    InnerFlameIgniterEffect() {
-        super(Outcome.AddAbility);
-        this.staticText = "If this is the third time this ability has resolved this turn, " +
-                "creatures you control gain first strike until end of turn";
-    }
-
-    private InnerFlameIgniterEffect(final InnerFlameIgniterEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public InnerFlameIgniterEffect copy() {
-        return new InnerFlameIgniterEffect(this);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        Player controller = game.getPlayer(source.getControllerId());
-        AbilityResolvedWatcher watcher = game.getState().getWatcher(AbilityResolvedWatcher.class);
-        if (controller == null
-                || watcher == null
-                || !watcher.checkActivations(source, game)) {
-            return false;
-        }
-        game.addEffect(new GainAbilityControlledEffect(
-                FirstStrikeAbility.getInstance(), Duration.EndOfTurn,
-                StaticFilters.FILTER_PERMANENT_CREATURE
-        ), source);
-        return true;
-    }
-}
+}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/s/SoulbrightFlamekin.java b/Mage.Sets/src/mage/cards/s/SoulbrightFlamekin.java
index c4fa17cb3a..95f3198e03 100644
--- a/Mage.Sets/src/mage/cards/s/SoulbrightFlamekin.java
+++ b/Mage.Sets/src/mage/cards/s/SoulbrightFlamekin.java
@@ -6,7 +6,9 @@ import mage.abilities.Ability;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.IfAbilityHasResolvedXTimesEffect;
 import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
+import mage.abilities.hint.common.AbilityResolutionCountHint;
 import mage.abilities.keyword.TrampleAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -37,8 +39,9 @@ public final class SoulbrightFlamekin extends CardImpl {
         Ability ability = new SimpleActivatedAbility(new GainAbilityTargetEffect(
                 TrampleAbility.getInstance(), Duration.EndOfTurn
         ), new ManaCostsImpl("{2}"));
-        ability.addEffect(new SoulbrightFlamekinEffect());
+        ability.addEffect(new IfAbilityHasResolvedXTimesEffect(Outcome.PutManaInPool, 3, new SoulbrightFlamekinEffect()));
         ability.addTarget(new TargetCreaturePermanent());
+        ability.addHint(AbilityResolutionCountHint.instance);
         this.addAbility(ability, new AbilityResolvedWatcher());
     }
 
@@ -56,8 +59,7 @@ class SoulbrightFlamekinEffect extends OneShotEffect {
 
     SoulbrightFlamekinEffect() {
         super(Outcome.Damage);
-        this.staticText = "If this is the third time this ability has resolved this turn, " +
-                "you may add {R}{R}{R}{R}{R}{R}{R}{R}";
+        this.staticText = "you may add {R}{R}{R}{R}{R}{R}{R}{R}";
     }
 
     private SoulbrightFlamekinEffect(final SoulbrightFlamekinEffect effect) {
@@ -72,14 +74,10 @@ class SoulbrightFlamekinEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player controller = game.getPlayer(source.getControllerId());
-        AbilityResolvedWatcher watcher = game.getState().getWatcher(AbilityResolvedWatcher.class);
-        if (controller == null
-                || watcher == null
-                || !watcher.checkActivations(source, game)
-                || !controller.chooseUse(Outcome.PutManaInPool, "Add {R}{R}{R}{R}{R}{R}{R}{R}}?", source, game)) {
-            return false;
+        if (controller != null && controller.chooseUse(Outcome.PutManaInPool, "Add {R}{R}{R}{R}{R}{R}{R}{R}?", source, game)) {
+            controller.getManaPool().addMana(Mana.RedMana(8), game, source);
+            return true;
         }
-        controller.getManaPool().addMana(Mana.RedMana(8), game, source);
-        return true;
+        return false;
     }
 }
diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/AbilityResolutionCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/AbilityResolutionCount.java
new file mode 100644
index 0000000000..b2fcb80e22
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/AbilityResolutionCount.java
@@ -0,0 +1,39 @@
+package mage.abilities.dynamicvalue.common;
+
+import mage.abilities.Ability;
+import mage.abilities.dynamicvalue.DynamicValue;
+import mage.abilities.effects.Effect;
+import mage.game.Game;
+import mage.watchers.common.AbilityResolvedWatcher;
+
+/**
+ * @author emerald000
+ */
+public enum AbilityResolutionCount implements DynamicValue {
+
+    instance;
+
+    @Override
+    public int calculate(Game game, Ability sourceAbility, Effect effect) {
+        AbilityResolvedWatcher watcher = game.getState().getWatcher(AbilityResolvedWatcher.class);
+        if (watcher != null) {
+            return watcher.getResolutionCount(game, sourceAbility);
+        }
+        return 0;
+    }
+
+    @Override
+    public AbilityResolutionCount copy() {
+        return instance;
+    }
+
+    @Override
+    public String toString() {
+        return "X";
+    }
+
+    @Override
+    public String getMessage() {
+        return "permanents you control";
+    }
+}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/IfAbilityHasResolvedXTimesEffect.java b/Mage/src/main/java/mage/abilities/effects/common/IfAbilityHasResolvedXTimesEffect.java
new file mode 100644
index 0000000000..21814f17ec
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/effects/common/IfAbilityHasResolvedXTimesEffect.java
@@ -0,0 +1,52 @@
+package mage.abilities.effects.common;
+
+import mage.abilities.Ability;
+import mage.abilities.effects.ContinuousEffect;
+import mage.abilities.effects.Effect;
+import mage.abilities.effects.OneShotEffect;
+import mage.constants.Outcome;
+import mage.game.Game;
+import mage.util.CardUtil;
+import mage.watchers.common.AbilityResolvedWatcher;
+
+/**
+ * @author emerald000
+ */
+public class IfAbilityHasResolvedXTimesEffect extends OneShotEffect {
+
+    private final int resolutionNumber;
+    private final Effect effect;
+
+    public IfAbilityHasResolvedXTimesEffect(Outcome outcome, int resolutionNumber, Effect effect) {
+        super(outcome);
+        this.resolutionNumber = resolutionNumber;
+        this.effect = effect;
+        this.staticText = "If this is the " + CardUtil.numberToOrdinalText(resolutionNumber) + " time this ability has resolved this turn, " +
+                effect.getText(null);
+    }
+
+    private IfAbilityHasResolvedXTimesEffect(final IfAbilityHasResolvedXTimesEffect effect) {
+        super(effect);
+        this.resolutionNumber = effect.resolutionNumber;
+        this.effect = effect.effect;
+    }
+
+    @Override
+    public IfAbilityHasResolvedXTimesEffect copy() {
+        return new IfAbilityHasResolvedXTimesEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        AbilityResolvedWatcher watcher = game.getState().getWatcher(AbilityResolvedWatcher.class);
+        if (watcher != null && watcher.getResolutionCount(game, source) == resolutionNumber) {
+            if (effect instanceof OneShotEffect) {
+                return effect.apply(game, source);
+            } else {
+                game.addEffect((ContinuousEffect) effect, source);
+                return true;
+            }
+        }
+        return true;
+    }
+}
diff --git a/Mage/src/main/java/mage/abilities/hint/common/AbilityResolutionCountHint.java b/Mage/src/main/java/mage/abilities/hint/common/AbilityResolutionCountHint.java
new file mode 100644
index 0000000000..5c44331381
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/hint/common/AbilityResolutionCountHint.java
@@ -0,0 +1,26 @@
+package mage.abilities.hint.common;
+
+import mage.abilities.Ability;
+import mage.abilities.dynamicvalue.common.AbilityResolutionCount;
+import mage.abilities.hint.Hint;
+import mage.abilities.hint.ValueHint;
+import mage.game.Game;
+
+/**
+ * @author emerald000
+ */
+public enum AbilityResolutionCountHint implements Hint {
+
+    instance;
+    private static final Hint hint = new ValueHint("Resolution count:", AbilityResolutionCount.instance);
+
+    @Override
+    public String getText(Game game, Ability ability) {
+        return hint.getText(game, ability);
+    }
+
+    @Override
+    public Hint copy() {
+        return instance;
+    }
+}
diff --git a/Mage/src/main/java/mage/game/events/GameEvent.java b/Mage/src/main/java/mage/game/events/GameEvent.java
index cad89d63a9..171da25007 100644
--- a/Mage/src/main/java/mage/game/events/GameEvent.java
+++ b/Mage/src/main/java/mage/game/events/GameEvent.java
@@ -150,7 +150,8 @@ public class GameEvent implements Serializable {
         SPELL_CAST,
         ACTIVATE_ABILITY, ACTIVATED_ABILITY,
         TRIGGERED_ABILITY,
-        COPY_STACKOBJECT,COPIED_STACKOBJECT,
+        RESOLVED_ABILITY,
+        COPY_STACKOBJECT, COPIED_STACKOBJECT,
         /* ADD_MANA
          targetId    id of the ability that added the mana
          sourceId    sourceId of the ability that added the mana
diff --git a/Mage/src/main/java/mage/game/stack/StackAbility.java b/Mage/src/main/java/mage/game/stack/StackAbility.java
index cb4149bcfe..b35a664bb2 100644
--- a/Mage/src/main/java/mage/game/stack/StackAbility.java
+++ b/Mage/src/main/java/mage/game/stack/StackAbility.java
@@ -82,6 +82,7 @@ public class StackAbility extends StackObjImpl implements Ability {
     @Override
     public boolean resolve(Game game) {
         if (ability.getTargets().stillLegal(ability, game) || !canFizzle()) {
+            game.fireEvent(new GameEvent(GameEvent.EventType.RESOLVED_ABILITY, ability.getOriginalId(), ability.getSourceId(), ability.getControllerId()));
             boolean result = ability.resolve(game);
             game.getStack().remove(this, game);
             return result;
diff --git a/Mage/src/main/java/mage/watchers/common/AbilityResolvedWatcher.java b/Mage/src/main/java/mage/watchers/common/AbilityResolvedWatcher.java
index ccb1f75c81..dfa3d5daab 100644
--- a/Mage/src/main/java/mage/watchers/common/AbilityResolvedWatcher.java
+++ b/Mage/src/main/java/mage/watchers/common/AbilityResolvedWatcher.java
@@ -1,6 +1,5 @@
 package mage.watchers.common;
 
-import mage.MageObjectReference;
 import mage.abilities.Ability;
 import mage.constants.WatcherScope;
 import mage.game.Game;
@@ -9,14 +8,13 @@ import mage.watchers.Watcher;
 
 import java.util.HashMap;
 import java.util.Map;
-import java.util.UUID;
 
 /**
  * @author TheElk801
  */
 public class AbilityResolvedWatcher extends Watcher {
 
-    private final Map<MageObjectReference, Map<UUID, Integer>> activationMap = new HashMap<>();
+    private final Map<String, Integer> resolutionMap = new HashMap<>();
 
     public AbilityResolvedWatcher() {
         super(WatcherScope.GAME);
@@ -24,17 +22,18 @@ public class AbilityResolvedWatcher extends Watcher {
 
     @Override
     public void watch(GameEvent event, Game game) {
+        if (event.getType() == GameEvent.EventType.RESOLVED_ABILITY) {
+            resolutionMap.merge(event.getTargetId().toString() + game.getState().getZoneChangeCounter(event.getSourceId()), 1, Integer::sum);
+        }
     }
 
     @Override
     public void reset() {
         super.reset();
-        activationMap.clear();
+        resolutionMap.clear();
     }
 
-    public boolean checkActivations(Ability source, Game game) {
-        return activationMap.computeIfAbsent(new MageObjectReference(
-                source.getSourceId(), source.getSourceObjectZoneChangeCounter(), game
-        ), x -> new HashMap<>()).compute(source.getOriginalId(), (u, i) -> i == null ? 1 : i + 1).intValue() == 3;
+    public int getResolutionCount(Game game, Ability source) {
+        return resolutionMap.getOrDefault(source.getOriginalId().toString() + game.getState().getZoneChangeCounter(source.getSourceId()), 0);
     }
 }

From f3bde1dbabd9659ae26550524942a93f6cf96045 Mon Sep 17 00:00:00 2001
From: gp66 <garyprince66@hotmail.com>
Date: Sun, 3 May 2020 12:52:51 -0500
Subject: [PATCH 008/586] completed suggestions for IKO tokens

---
 .../sources/ScryfallImageSupportTokens.java   |  4 ++-
 .../src/main/resources/card-pictures-tok.txt  |  6 ++--
 .../src/mage/cards/q/QuartzwoodCrasher.java   |  1 -
 .../game/permanent/token/BeastXToken.java     | 31 -------------------
 .../permanent/token/DinosaurBeastToken.java   | 13 ++++----
 .../permanent/token/HumanSoldierToken.java    | 10 ++++++
 6 files changed, 23 insertions(+), 42 deletions(-)
 delete mode 100644 Mage/src/main/java/mage/game/permanent/token/BeastXToken.java

diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java
index 14ece56096..a73ed21c14 100644
--- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java
@@ -358,7 +358,9 @@ public class ScryfallImageSupportTokens {
             put("IKO/Dinosaur Beast", "https://api.scryfall.com/cards/tiko/11/en?format=image");
             put("IKO/Dinosaur", "https://api.scryfall.com/cards/tiko/8/en?format=image");
             put("IKO/Feather", "https://api.scryfall.com/cards/tiko/9/en?format=image");
-            put("IKO/Human Soldier", "https://api.scryfall.com/cards/tiko/4/en?format=image");
+            put("IKO/Human Soldier/1", "https://api.scryfall.com/cards/tiko/3/en?format=image");
+            put("IKO/Human Soldier/2", "https://api.scryfall.com/cards/tiko/4/en?format=image");
+            put("IKO/Human Soldier/3", "https://api.scryfall.com/cards/tiko/5/en?format=image");
             put("IKO/Kraken", "https://api.scryfall.com/cards/tiko/6/en?format=image");
             put("IKO/Shark", "https://api.scryfall.com/cards/tiko/7/en?format=image");
 
diff --git a/Mage.Client/src/main/resources/card-pictures-tok.txt b/Mage.Client/src/main/resources/card-pictures-tok.txt
index b327471863..dd753ba0cf 100644
--- a/Mage.Client/src/main/resources/card-pictures-tok.txt
+++ b/Mage.Client/src/main/resources/card-pictures-tok.txt
@@ -1365,6 +1365,8 @@
 |Generate|TOK:IKO|Dinosaur Beast|||DinosaurBeastToken|
 |Generate|TOK:IKO|Dinosaur|||DinosaurHasteToken|
 |Generate|TOK:IKO|Feather|||FeatherToken|
-|Generate|TOK:IKO|Human Soldier|||HumanSoldierToken|
+|Generate|TOK:IKO|Human Soldier|1||HumanSoldierToken|
+|Generate|TOK:IKO|Human Soldier|2||HumanSoldierToken|
+|Generate|TOK:IKO|Human Soldier|3||HumanSoldierToken|
 |Generate|TOK:IKO|Kraken|||KrakenToken|
-|Generate|TOK:IKO|Shark|||SharkToken|
+|Generate|TOK:IKO|Shark|||SharkToken|
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/q/QuartzwoodCrasher.java b/Mage.Sets/src/mage/cards/q/QuartzwoodCrasher.java
index aa00632951..bc7cd43eac 100644
--- a/Mage.Sets/src/mage/cards/q/QuartzwoodCrasher.java
+++ b/Mage.Sets/src/mage/cards/q/QuartzwoodCrasher.java
@@ -12,7 +12,6 @@ import mage.game.Game;
 import mage.game.events.DamagedPlayerEvent;
 import mage.game.events.GameEvent;
 import mage.game.permanent.Permanent;
-import mage.game.permanent.token.BeastXToken;
 import mage.game.permanent.token.DinosaurBeastToken;
 import mage.target.targetpointer.FixedTarget;
 import mage.watchers.Watcher;
diff --git a/Mage/src/main/java/mage/game/permanent/token/BeastXToken.java b/Mage/src/main/java/mage/game/permanent/token/BeastXToken.java
deleted file mode 100644
index 8d8ea05429..0000000000
--- a/Mage/src/main/java/mage/game/permanent/token/BeastXToken.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package mage.game.permanent.token;
-
-import mage.MageInt;
-import mage.abilities.keyword.TrampleAbility;
-import mage.constants.CardType;
-import mage.constants.SubType;
-
-/**
- * @author TheElk801
- */
-public final class BeastXToken extends TokenImpl {
-
-    public BeastXToken(int xValue) {
-        super("Beast", "X/X green Beast creature token");
-        cardType.add(CardType.CREATURE);
-        color.setGreen(true);
-        subtype.add(SubType.BEAST);
-        power = new MageInt(xValue);
-        toughness = new MageInt(xValue);
-        addAbility(TrampleAbility.getInstance());
-    }
-
-    private BeastXToken(final BeastXToken token) {
-        super(token);
-    }
-
-    @Override
-    public BeastXToken copy() {
-        return new BeastXToken(this);
-    }
-}
diff --git a/Mage/src/main/java/mage/game/permanent/token/DinosaurBeastToken.java b/Mage/src/main/java/mage/game/permanent/token/DinosaurBeastToken.java
index 6c0ab5730b..ad0026141e 100644
--- a/Mage/src/main/java/mage/game/permanent/token/DinosaurBeastToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/DinosaurBeastToken.java
@@ -1,13 +1,11 @@
-
 package mage.game.permanent.token;
 
-import mage.constants.CardType;
-import mage.MageInt;
-import mage.abilities.keyword.TrampleAbility;
-import mage.constants.SubType;
+        import mage.MageInt;
+        import mage.abilities.keyword.TrampleAbility;
+        import mage.constants.CardType;
+        import mage.constants.SubType;
 
 /**
- *
  * @author TheElk801
  */
 public final class DinosaurBeastToken extends TokenImpl {
@@ -23,10 +21,11 @@ public final class DinosaurBeastToken extends TokenImpl {
         addAbility(TrampleAbility.getInstance());
     }
 
-    public DinosaurBeastToken(final DinosaurBeastToken token) {
+    private DinosaurBeastToken(final DinosaurBeastToken token) {
         super(token);
     }
 
+    @Override
     public DinosaurBeastToken copy() {
         return new DinosaurBeastToken(this);
     }
diff --git a/Mage/src/main/java/mage/game/permanent/token/HumanSoldierToken.java b/Mage/src/main/java/mage/game/permanent/token/HumanSoldierToken.java
index 1d99d2328c..d331b634d5 100644
--- a/Mage/src/main/java/mage/game/permanent/token/HumanSoldierToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/HumanSoldierToken.java
@@ -3,6 +3,7 @@ package mage.game.permanent.token;
 import mage.MageInt;
 import mage.constants.CardType;
 import mage.constants.SubType;
+import mage.util.RandomUtil;
 
 public final class HumanSoldierToken extends TokenImpl {
 
@@ -16,6 +17,15 @@ public final class HumanSoldierToken extends TokenImpl {
         toughness = new MageInt(1);
     }
 
+    @Override
+    public void setExpansionSetCodeForImage(String code) {
+        super.setExpansionSetCodeForImage(code);
+
+        if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("IKO")) {
+            setTokenType(RandomUtil.nextInt(3) + 1); // 1...3
+        }
+    }
+
     public HumanSoldierToken(final HumanSoldierToken token) {
         super(token);
     }

From 75577cdbe977948cd2f681a1082098e322290f99 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sun, 3 May 2020 14:35:26 -0400
Subject: [PATCH 009/586] Added new method for discarding cards to handle batch
 triggers (ready for review) (#6489)

* added new discard method

* started refactoring to use new discard method

* refactored A through I

* fixed some issues

* separated balance effect into its own class

* refactored J through R

* refactored S through Z

* applied requested changes
---
 Mage.Sets/src/mage/cards/a/Amnesia.java       |  24 +--
 Mage.Sets/src/mage/cards/a/Apocalypse.java    |  73 +-------
 Mage.Sets/src/mage/cards/b/Balance.java       | 153 +---------------
 Mage.Sets/src/mage/cards/b/BalancingAct.java  |  56 +++---
 Mage.Sets/src/mage/cards/b/BarbedShocker.java |  28 ++-
 Mage.Sets/src/mage/cards/b/Breakthrough.java  |  56 +++---
 .../src/mage/cards/b/BrinkOfMadness.java      |  67 +++----
 .../src/mage/cards/b/BurningInquiry.java      |  12 +-
 .../src/mage/cards/c/CabalTherapist.java      |  22 ++-
 Mage.Sets/src/mage/cards/c/CabalTherapy.java  |  40 +++--
 .../src/mage/cards/c/ChandraFlamecaller.java  |  16 +-
 .../src/mage/cards/c/CollectiveDefiance.java  |  14 +-
 .../src/mage/cards/c/CompulsiveResearch.java  |  41 ++---
 .../src/mage/cards/c/CrosisThePurger.java     |  64 +++----
 .../src/mage/cards/d/DangerousWager.java      |  50 +-----
 .../src/mage/cards/d/DarettiScrapSavant.java  |  22 +--
 .../src/mage/cards/d/DiscordantDirge.java     |  48 +++--
 .../src/mage/cards/d/DistendedMindbender.java |  53 +++---
 .../src/mage/cards/d/DrasticRevelation.java   |  23 +--
 Mage.Sets/src/mage/cards/e/Extortion.java     |  34 ++--
 .../src/mage/cards/g/GruesomeDiscovery.java   |  54 +++---
 .../src/mage/cards/h/HintOfInsanity.java      |  65 +++----
 Mage.Sets/src/mage/cards/i/InfernalKirin.java |  60 +++----
 Mage.Sets/src/mage/cards/j/JayaBallard.java   |  30 ++--
 Mage.Sets/src/mage/cards/l/LastRites.java     |  53 +++---
 Mage.Sets/src/mage/cards/l/LeaveChance.java   |  49 +++--
 .../src/mage/cards/m/MagusOfTheBalance.java   | 156 +---------------
 .../src/mage/cards/m/MinamosMeddling.java     |  66 ++++---
 Mage.Sets/src/mage/cards/m/MindBomb.java      |  91 +++++-----
 .../src/mage/cards/m/MindExtraction.java      |   4 +-
 Mage.Sets/src/mage/cards/m/MindMaggots.java   |  47 +++--
 Mage.Sets/src/mage/cards/m/MindWarp.java      |  43 ++---
 Mage.Sets/src/mage/cards/m/Monomania.java     |  43 ++---
 .../src/mage/cards/m/MyojinOfNightsReach.java |  21 +--
 .../src/mage/cards/n/NantukoCultivator.java   |  63 ++++---
 .../src/mage/cards/n/Nebuchadnezzar.java      |  42 +++--
 .../mage/cards/n/NehebDreadhordeChampion.java |  17 +-
 Mage.Sets/src/mage/cards/n/Nightsnare.java    |  45 +++--
 Mage.Sets/src/mage/cards/n/NogginWhack.java   |  86 ++++-----
 Mage.Sets/src/mage/cards/n/NoxiousVapors.java |  74 ++++----
 Mage.Sets/src/mage/cards/r/RareBGone.java     |  76 +++-----
 .../src/mage/cards/r/RestoreBalance.java      | 156 +---------------
 Mage.Sets/src/mage/cards/r/RiseFall.java      | 104 ++++++-----
 Mage.Sets/src/mage/cards/r/RitesOfSpring.java |  47 ++---
 Mage.Sets/src/mage/cards/s/SacredRites.java   |  41 ++---
 .../src/mage/cards/s/ShatterAssumptions.java  |  11 +-
 Mage.Sets/src/mage/cards/s/Shocker.java       |  30 ++--
 .../src/mage/cards/s/SireOfInsanity.java      |  32 ++--
 Mage.Sets/src/mage/cards/s/SkullRend.java     |  47 ++---
 .../src/mage/cards/t/TrapfindersTrick.java    |  41 ++---
 Mage.Sets/src/mage/cards/t/TsabosDecree.java  |  68 ++++---
 Mage.Sets/src/mage/cards/t/Tyrannize.java     |   5 +-
 Mage.Sets/src/mage/cards/v/Void.java          |  72 ++++----
 .../src/mage/cards/w/WhisperingMadness.java   |  41 ++---
 Mage.Sets/src/mage/cards/w/Windfall.java      |  26 ++-
 Mage.Sets/src/mage/cards/w/WitsEnd.java       |  27 ++-
 Mage.Sets/src/mage/cards/w/WrenchMind.java    |  41 +++--
 .../java/org/mage/test/player/TestPlayer.java |   5 +
 .../java/org/mage/test/stub/PlayerStub.java   |  10 +-
 .../costs/common/DiscardHandCost.java         |  19 +-
 .../costs/common/DiscardTargetCost.java       |  23 ++-
 .../effects/common/BalanceEffect.java         | 168 ++++++++++++++++++
 .../DiscardCardYouChooseTargetEffect.java     |   7 +-
 .../discard/DiscardControllerEffect.java      |  22 +--
 .../discard/DiscardEachPlayerEffect.java      |  89 +++++-----
 .../common/discard/DiscardHandAllEffect.java  |  10 +-
 Mage/src/main/java/mage/players/Player.java   |   2 +
 .../main/java/mage/players/PlayerImpl.java    |  18 ++
 68 files changed, 1290 insertions(+), 1953 deletions(-)
 create mode 100644 Mage/src/main/java/mage/abilities/effects/common/BalanceEffect.java

diff --git a/Mage.Sets/src/mage/cards/a/Amnesia.java b/Mage.Sets/src/mage/cards/a/Amnesia.java
index ae4eebaa75..b452e933bc 100644
--- a/Mage.Sets/src/mage/cards/a/Amnesia.java
+++ b/Mage.Sets/src/mage/cards/a/Amnesia.java
@@ -1,28 +1,25 @@
-
 package mage.cards.a;
 
-import java.util.Set;
-import java.util.UUID;
+import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
-import mage.cards.Card;
-import mage.cards.CardImpl;
-import mage.cards.CardSetInfo;
-import mage.cards.Cards;
+import mage.cards.*;
 import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.game.Game;
 import mage.players.Player;
 import mage.target.TargetPlayer;
 
+import java.util.Set;
+import java.util.UUID;
+
 /**
- *
  * @author fireshoes
  */
 public final class Amnesia extends CardImpl {
 
     public Amnesia(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{U}{U}{U}");
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{U}{U}{U}");
 
         // Target player reveals their hand and discards all nonland cards.
         this.getSpellAbility().addEffect(new AmnesiaEffect());
@@ -60,13 +57,10 @@ class AmnesiaEffect extends OneShotEffect {
         Player player = game.getPlayer(source.getFirstTarget());
         if (player != null) {
             Cards hand = player.getHand();
-            player.revealCards("Amnesia", hand, game);
+            player.revealCards(source, hand, game);
             Set<Card> cards = hand.getCards(game);
-            for (Card card : cards) {
-                if (card != null && !card.isLand()) {
-                    player.discard(card, source, game);
-                }
-            }
+            cards.removeIf(MageObject::isLand);
+            player.discard(new CardsImpl(cards), source, game);
             return true;
         }
         return false;
diff --git a/Mage.Sets/src/mage/cards/a/Apocalypse.java b/Mage.Sets/src/mage/cards/a/Apocalypse.java
index 09008dac71..073e15d336 100644
--- a/Mage.Sets/src/mage/cards/a/Apocalypse.java
+++ b/Mage.Sets/src/mage/cards/a/Apocalypse.java
@@ -1,30 +1,25 @@
-
 package mage.cards.a;
 
-import java.util.UUID;
-import mage.abilities.Ability;
-import mage.abilities.effects.OneShotEffect;
-import mage.cards.Card;
+import mage.abilities.effects.common.ExileAllEffect;
+import mage.abilities.effects.common.discard.DiscardHandControllerEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.Outcome;
-import mage.game.Game;
-import mage.game.permanent.Permanent;
-import mage.players.Player;
+import mage.filter.StaticFilters;
+
+import java.util.UUID;
 
 /**
- *
  * @author markedagain
  */
 public final class Apocalypse extends CardImpl {
 
     public Apocalypse(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{R}{R}{R}");
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}{R}{R}");
 
         // Exile all permanents. You discard your hand.
-        this.getSpellAbility().addEffect(new ApocalypseExileAllPermanentsEffect());
-        this.getSpellAbility().addEffect(new ApocalypseDiscardEffect());
+        this.getSpellAbility().addEffect(new ExileAllEffect(StaticFilters.FILTER_PERMANENTS));
+        this.getSpellAbility().addEffect(new DiscardHandControllerEffect().setText("You discard your hand"));
     }
 
     public Apocalypse(final Apocalypse card) {
@@ -36,55 +31,3 @@ public final class Apocalypse extends CardImpl {
         return new Apocalypse(this);
     }
 }
-class ApocalypseDiscardEffect extends OneShotEffect {
-
-    public ApocalypseDiscardEffect() {
-        super(Outcome.Discard);
-        this.staticText = "Discard your hand";
-    }
-
-    public ApocalypseDiscardEffect(final ApocalypseDiscardEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public ApocalypseDiscardEffect copy() {
-        return new ApocalypseDiscardEffect(this);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        Player player = game.getPlayer(source.getControllerId());
-        if (player != null) {
-            for (Card card : player.getHand().getCards(game)) {
-                player.discard(card, source, game);
-            }
-            return true;
-        }
-        return false;
-    }
-}
-class ApocalypseExileAllPermanentsEffect extends OneShotEffect {
-
-    public ApocalypseExileAllPermanentsEffect() {
-        super(Outcome.Exile);
-        staticText = "Exile all permanents";
-    }
-
-    public ApocalypseExileAllPermanentsEffect(final ApocalypseExileAllPermanentsEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public ApocalypseExileAllPermanentsEffect copy() {
-        return new ApocalypseExileAllPermanentsEffect(this);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        for (Permanent permanent : game.getBattlefield().getAllActivePermanents()) {
-                permanent.moveToExile(null, null, source.getSourceId(), game);
-        }
-        return true;
-    }
-}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/b/Balance.java b/Mage.Sets/src/mage/cards/b/Balance.java
index 153b24ecc5..2c7ea4d675 100644
--- a/Mage.Sets/src/mage/cards/b/Balance.java
+++ b/Mage.Sets/src/mage/cards/b/Balance.java
@@ -1,21 +1,10 @@
 package mage.cards.b;
 
-import mage.abilities.Ability;
-import mage.abilities.effects.OneShotEffect;
-import mage.cards.*;
+import mage.abilities.effects.common.BalanceEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.Outcome;
-import mage.filter.FilterCard;
-import mage.filter.common.FilterControlledCreaturePermanent;
-import mage.filter.common.FilterControlledLandPermanent;
-import mage.game.Game;
-import mage.game.permanent.Permanent;
-import mage.players.Player;
-import mage.target.common.TargetCardInHand;
-import mage.target.common.TargetControlledPermanent;
 
-import java.util.HashMap;
-import java.util.Map;
 import java.util.UUID;
 
 /**
@@ -39,139 +28,3 @@ public final class Balance extends CardImpl {
         return new Balance(this);
     }
 }
-
-class BalanceEffect extends OneShotEffect {
-
-    BalanceEffect() {
-        super(Outcome.Sacrifice);
-        staticText = "each player chooses a number of lands they control "
-                + "equal to the number of lands controlled by the player "
-                + "who controls the fewest, then sacrifices the rest. "
-                + "Players discard cards and sacrifice creatures the same way";
-    }
-
-    BalanceEffect(final BalanceEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public BalanceEffect copy() {
-        return new BalanceEffect(this);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        Player controller = game.getPlayer(source.getControllerId());
-        if (controller != null) {
-            //Lands
-            int minLand = Integer.MAX_VALUE;
-            Cards landsToSacrifice = new CardsImpl();
-            for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
-                Player player = game.getPlayer(playerId);
-                if (player != null) {
-                    int count = game.getBattlefield().countAll(new FilterControlledLandPermanent(), player.getId(), game);
-                    if (count < minLand) {
-                        minLand = count;
-                    }
-                }
-            }
-
-            for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
-                Player player = game.getPlayer(playerId);
-                if (player != null) {
-                    TargetControlledPermanent target = new TargetControlledPermanent(minLand, minLand, new FilterControlledLandPermanent("lands to keep"), true);
-                    if (target.choose(Outcome.Sacrifice, player.getId(), source.getSourceId(), game)) {
-                        for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterControlledLandPermanent(), player.getId(), source.getSourceId(), game)) {
-                            if (permanent != null && !target.getTargets().contains(permanent.getId())) {
-                                landsToSacrifice.add(permanent);
-                            }
-                        }
-                    }
-                }
-            }
-
-            for (UUID cardId : landsToSacrifice) {
-                Permanent permanent = game.getPermanent(cardId);
-                if (permanent != null) {
-                    permanent.sacrifice(source.getSourceId(), game);
-                }
-            }
-
-            //Creatures
-            int minCreature = Integer.MAX_VALUE;
-            Cards creaturesToSacrifice = new CardsImpl();
-            for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
-                Player player = game.getPlayer(playerId);
-                if (player != null) {
-                    int count = game.getBattlefield().countAll(new FilterControlledCreaturePermanent(), player.getId(), game);
-                    if (count < minCreature) {
-                        minCreature = count;
-                    }
-                }
-            }
-
-            for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
-                Player player = game.getPlayer(playerId);
-                if (player != null) {
-                    TargetControlledPermanent target = new TargetControlledPermanent(minCreature, minCreature, new FilterControlledCreaturePermanent("creatures to keep"), true);
-                    if (target.choose(Outcome.Sacrifice, player.getId(), source.getSourceId(), game)) {
-                        for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterControlledCreaturePermanent(), player.getId(), source.getSourceId(), game)) {
-                            if (permanent != null && !target.getTargets().contains(permanent.getId())) {
-                                creaturesToSacrifice.add(permanent);
-                            }
-                        }
-                    }
-                }
-            }
-
-            for (UUID cardId : creaturesToSacrifice) {
-                Permanent permanent = game.getPermanent(cardId);
-                if (permanent != null) {
-                    permanent.sacrifice(source.getSourceId(), game);
-                }
-            }
-
-            //Cards in hand
-            int minCard = Integer.MAX_VALUE;
-            Map<UUID, Cards> cardsToDiscard = new HashMap<>(2);
-            for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
-                Player player = game.getPlayer(playerId);
-                if (player != null) {
-                    int count = player.getHand().size();
-                    if (count < minCard) {
-                        minCard = count;
-                    }
-                }
-            }
-
-            for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
-                Player player = game.getPlayer(playerId);
-                if (player != null) {
-                    Cards cards = new CardsImpl();
-                    TargetCardInHand target = new TargetCardInHand(minCard, new FilterCard("cards to keep"));
-                    if (target.choose(Outcome.Discard, player.getId(), source.getSourceId(), game)) {
-                        for (Card card : player.getHand().getCards(game)) {
-                            if (card != null && !target.getTargets().contains(card.getId())) {
-                                cards.add(card);
-                            }
-                        }
-                        cardsToDiscard.put(playerId, cards);
-                    }
-                }
-            }
-
-            for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
-                Player player = game.getPlayer(playerId);
-                if (player != null && cardsToDiscard.get(playerId) != null) {
-                    for (UUID cardId : cardsToDiscard.get(playerId)) {
-                        Card card = game.getCard(cardId);
-                        player.discard(card, source, game);
-
-                    }
-                }
-            }
-            return true;
-        }
-        return false;
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/b/BalancingAct.java b/Mage.Sets/src/mage/cards/b/BalancingAct.java
index 2052ec14e8..d8cd45b70c 100644
--- a/Mage.Sets/src/mage/cards/b/BalancingAct.java
+++ b/Mage.Sets/src/mage/cards/b/BalancingAct.java
@@ -1,10 +1,7 @@
-
 package mage.cards.b;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.cards.Cards;
@@ -18,21 +15,21 @@ import mage.players.Player;
 import mage.target.common.TargetCardInHand;
 import mage.target.common.TargetControlledPermanent;
 
+import java.util.UUID;
+
 /**
- *
  * @author Plopman (Restore Balance), cbt33
  */
 public final class BalancingAct extends CardImpl {
 
     public BalancingAct(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{W}{W}");
-
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{W}{W}");
 
         // Each player chooses a number of permanents they control equal to the number of permanents controlled by the player who controls the fewest, then sacrifices the rest. Each player discards cards the same way.
         this.getSpellAbility().addEffect(new BalancingActEffect());
     }
 
-    public BalancingAct(final BalancingAct card) {
+    private BalancingAct(final BalancingAct card) {
         super(card);
     }
 
@@ -44,13 +41,12 @@ public final class BalancingAct extends CardImpl {
 
 class BalancingActEffect extends OneShotEffect {
 
-   
-    public BalancingActEffect() {
+    BalancingActEffect() {
         super(Outcome.Sacrifice);
         staticText = "Each player chooses a number of permanents they control equal to the number of permanents controlled by the player who controls the fewest, then sacrifices the rest. Each player discards cards the same way";
     }
 
-    public BalancingActEffect(final BalancingActEffect effect) {
+    private BalancingActEffect(final BalancingActEffect effect) {
         super(effect);
     }
 
@@ -65,23 +61,23 @@ class BalancingActEffect extends OneShotEffect {
         if (controller != null) {
             int minPermanent = Integer.MAX_VALUE, minCard = Integer.MAX_VALUE;
             // count minimal permanents
-            for(UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)){
+            for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
                 Player player = game.getPlayer(playerId);
-                if(player != null){
+                if (player != null) {
                     int count = game.getBattlefield().getActivePermanents(new FilterControlledPermanent(), player.getId(), source.getSourceId(), game).size();
-                    if(count < minPermanent){
+                    if (count < minPermanent) {
                         minPermanent = count;
                     }
                 }
             }
             // sacrifice permanents over the minimum
-            for(UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)){
+            for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
                 Player player = game.getPlayer(playerId);
-                if(player != null){
+                if (player != null) {
                     TargetControlledPermanent target = new TargetControlledPermanent(minPermanent, minPermanent, new FilterControlledPermanent(), true);
-                    if(target.choose(Outcome.Benefit, player.getId(), source.getSourceId(), game)){
-                        for(Permanent permanent : game.getBattlefield().getActivePermanents(new FilterControlledPermanent(), player.getId(), source.getSourceId(), game)){
-                            if(permanent != null && !target.getTargets().contains(permanent.getId())){
+                    if (target.choose(Outcome.Benefit, player.getId(), source.getSourceId(), game)) {
+                        for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterControlledPermanent(), player.getId(), source.getSourceId(), game)) {
+                            if (permanent != null && !target.getTargets().contains(permanent.getId())) {
                                 permanent.sacrifice(source.getSourceId(), game);
                             }
                         }
@@ -90,29 +86,25 @@ class BalancingActEffect extends OneShotEffect {
             }
 
             // count minimal cards in hand
-            for(UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)){
+            for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
                 Player player = game.getPlayer(playerId);
-                if(player != null){
+                if (player != null) {
                     int count = player.getHand().size();
-                    if(count < minCard){
+                    if (count < minCard) {
                         minCard = count;
                     }
                 }
             }
-            
+
             // discard cards over the minimum
-            for(UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)){
+            for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
                 Player player = game.getPlayer(playerId);
-                if(player != null){
+                if (player != null) {
                     TargetCardInHand target = new TargetCardInHand(minCard, new FilterCard());
-                    if(target.choose(Outcome.Benefit, player.getId(), source.getSourceId(), game)){
-                        Cards cards =  player.getHand().copy();
-                        for(UUID cardUUID : cards){
-                            Card card = player.getHand().get(cardUUID, game);
-                            if(!target.getTargets().contains(cardUUID)){
-                                player.discard(card, source, game);
-                            }
-                        }
+                    if (target.choose(Outcome.Benefit, player.getId(), source.getSourceId(), game)) {
+                        Cards cards = player.getHand().copy();
+                        cards.removeIf(target.getTargets()::contains);
+                        player.discard(cards, source, game);
                     }
                 }
             }
diff --git a/Mage.Sets/src/mage/cards/b/BarbedShocker.java b/Mage.Sets/src/mage/cards/b/BarbedShocker.java
index 0ae806c0d0..48177c00ff 100644
--- a/Mage.Sets/src/mage/cards/b/BarbedShocker.java
+++ b/Mage.Sets/src/mage/cards/b/BarbedShocker.java
@@ -1,14 +1,11 @@
-
 package mage.cards.b;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.DealsDamageToAPlayerTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.keyword.HasteAbility;
 import mage.abilities.keyword.TrampleAbility;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
@@ -17,14 +14,15 @@ import mage.constants.SubType;
 import mage.game.Game;
 import mage.players.Player;
 
+import java.util.UUID;
+
 /**
- *
  * @author markedagain
  */
 public final class BarbedShocker extends CardImpl {
 
     public BarbedShocker(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{R}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}");
         this.subtype.add(SubType.INSECT);
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
@@ -37,7 +35,7 @@ public final class BarbedShocker extends CardImpl {
         this.addAbility(new DealsDamageToAPlayerTriggeredAbility(new BarbedShockerEffect(), false, true));
     }
 
-    public BarbedShocker(final BarbedShocker card) {
+    private BarbedShocker(final BarbedShocker card) {
         super(card);
     }
 
@@ -46,14 +44,15 @@ public final class BarbedShocker extends CardImpl {
         return new BarbedShocker(this);
     }
 }
+
 class BarbedShockerEffect extends OneShotEffect {
 
-    public BarbedShockerEffect() {
+    BarbedShockerEffect() {
         super(Outcome.Discard);
         this.staticText = " that player discards all the cards in their hand, then draws that many cards";
     }
 
-    public BarbedShockerEffect(final BarbedShockerEffect effect) {
+    private BarbedShockerEffect(final BarbedShockerEffect effect) {
         super(effect);
     }
 
@@ -65,14 +64,11 @@ class BarbedShockerEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player targetPlayer = game.getPlayer(targetPointer.getFirst(game, source));
-            if (targetPlayer != null) {
-                int count = targetPlayer.getHand().size();
-                for (Card card : targetPlayer.getHand().getCards(game)) {
-                    targetPlayer.discard(card, source, game);
-                }
-                targetPlayer.drawCards(count, source.getSourceId(), game);
-                return false;
-            }
+        if (targetPlayer == null) {
+            return false;
+        }
+        int count = targetPlayer.discard(targetPlayer.getHand(), source, game).size();
+        targetPlayer.drawCards(count, source.getSourceId(), game);
         return true;
     }
 }
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/b/Breakthrough.java b/Mage.Sets/src/mage/cards/b/Breakthrough.java
index 15f6a890e9..b2392375b4 100644
--- a/Mage.Sets/src/mage/cards/b/Breakthrough.java
+++ b/Mage.Sets/src/mage/cards/b/Breakthrough.java
@@ -1,13 +1,11 @@
-
 package mage.cards.b;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
+import mage.cards.Cards;
 import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.filter.FilterCard;
@@ -15,25 +13,24 @@ import mage.game.Game;
 import mage.players.Player;
 import mage.target.common.TargetCardInHand;
 
+import java.util.UUID;
+
 /**
- *
  * @author emerald000
  */
 public final class Breakthrough extends CardImpl {
 
     public Breakthrough(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{X}{U}");
-
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{U}");
 
         // Draw four cards,
         this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(4));
-        
+
         //then choose X cards in your hand and discard the rest.
         this.getSpellAbility().addEffect(new BreakthroughEffect());
-
     }
 
-    public Breakthrough(final Breakthrough card) {
+    private Breakthrough(final Breakthrough card) {
         super(card);
     }
 
@@ -44,41 +41,38 @@ public final class Breakthrough extends CardImpl {
 }
 
 class BreakthroughEffect extends OneShotEffect {
-    
+
     BreakthroughEffect() {
         super(Outcome.Discard);
         this.staticText = ", then choose X cards in your hand and discard the rest.";
     }
-    
-    BreakthroughEffect(final BreakthroughEffect effect) {
+
+    private BreakthroughEffect(final BreakthroughEffect effect) {
         super(effect);
     }
-    
+
     @Override
     public BreakthroughEffect copy() {
         return new BreakthroughEffect(this);
     }
-    
+
     @Override
     public boolean apply(Game game, Ability source) {
         Player player = game.getPlayer(source.getControllerId());
-        if (player != null) {
-            int amountToKeep = source.getManaCostsToPay().getX();
-            if (amountToKeep == 0) {
-                player.discard(player.getHand().size(), false, source, game);
-            }
-            else if (amountToKeep < player.getHand().size()) {
-                TargetCardInHand target = new TargetCardInHand(amountToKeep, new FilterCard());
-                target.setTargetName("cards to keep");
-                target.choose(Outcome.Benefit, player.getId(), source.getSourceId(), game);
-                for (Card card : player.getHand().getCards(game)) {
-                    if (!target.getTargets().contains(card.getId())) {
-                        player.discard(card, source, game);
-                    }
-                }
-            }
-            return true;
+        if (player == null) {
+            return false;
         }
-        return false;
+        int amountToKeep = source.getManaCostsToPay().getX();
+        if (amountToKeep == 0) {
+            player.discard(player.getHand(), source, game);
+        } else if (amountToKeep < player.getHand().size()) {
+            TargetCardInHand target = new TargetCardInHand(amountToKeep, new FilterCard());
+            target.setTargetName("cards to keep");
+            target.choose(Outcome.Benefit, player.getId(), source.getSourceId(), game);
+            Cards cards = player.getHand().copy();
+            cards.removeIf(target.getTargets()::contains);
+            player.discard(cards, source, game);
+        }
+        return true;
     }
 }
diff --git a/Mage.Sets/src/mage/cards/b/BrinkOfMadness.java b/Mage.Sets/src/mage/cards/b/BrinkOfMadness.java
index fe17742f0b..58a37d6967 100644
--- a/Mage.Sets/src/mage/cards/b/BrinkOfMadness.java
+++ b/Mage.Sets/src/mage/cards/b/BrinkOfMadness.java
@@ -1,8 +1,5 @@
-
 package mage.cards.b;
 
-import java.util.Set;
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.TriggeredAbility;
 import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
@@ -10,38 +7,33 @@ import mage.abilities.condition.common.CardsInHandCondition;
 import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.SacrificeSourceEffect;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.CardType;
-import mage.constants.ComparisonType;
-import mage.constants.Outcome;
-import mage.constants.TargetController;
-import mage.constants.Zone;
+import mage.constants.*;
 import mage.game.Game;
 import mage.players.Player;
 import mage.target.common.TargetOpponent;
 
+import java.util.UUID;
+
 /**
- *
  * @author Plopman
  */
 public final class BrinkOfMadness extends CardImpl {
 
     public BrinkOfMadness(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{B}{B}");
-
+        super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}{B}");
 
         // At the beginning of your upkeep, if you have no cards in hand, sacrifice Brink of Madness and target opponent discards their hand.
-        TriggeredAbility ability  = new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new SacrificeSourceEffect(), TargetController.YOU, false);
+        TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new SacrificeSourceEffect(), TargetController.YOU, false);
         ability.addEffect(new BrinkOfMadnessEffect());
         ability.addTarget(new TargetOpponent());
         CardsInHandCondition contition = new CardsInHandCondition(ComparisonType.EQUAL_TO, 0);
         this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, contition, "At the beginning of your upkeep, if you have no cards in hand, sacrifice {this} and target opponent discards their hand."));
-        
+
     }
 
-    public BrinkOfMadness(final BrinkOfMadness card) {
+    private BrinkOfMadness(final BrinkOfMadness card) {
         super(card);
     }
 
@@ -49,34 +41,27 @@ public final class BrinkOfMadness extends CardImpl {
     public BrinkOfMadness copy() {
         return new BrinkOfMadness(this);
     }
-    
+
     static class BrinkOfMadnessEffect extends OneShotEffect {
 
-    public BrinkOfMadnessEffect() {
-        super(Outcome.Benefit);
-        this.staticText = "Target player discards their hand";
-    }
-
-    public BrinkOfMadnessEffect(final BrinkOfMadnessEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public BrinkOfMadnessEffect copy() {
-        return new BrinkOfMadnessEffect(this);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        Player player = game.getPlayer(source.getFirstTarget());
-        if (player != null) {
-            Set<Card> cards = player.getHand().getCards(game);
-            for (Card card : cards) {
-                player.discard(card, source, game);
-            }
-            return true;
+        private BrinkOfMadnessEffect() {
+            super(Outcome.Benefit);
+            this.staticText = "Target player discards their hand";
+        }
+
+        private BrinkOfMadnessEffect(final BrinkOfMadnessEffect effect) {
+            super(effect);
+        }
+
+        @Override
+        public BrinkOfMadnessEffect copy() {
+            return new BrinkOfMadnessEffect(this);
+        }
+
+        @Override
+        public boolean apply(Game game, Ability source) {
+            Player player = game.getPlayer(source.getFirstTarget());
+            return player != null && !player.discard(player.getHand(), source, game).isEmpty();
         }
-        return false;
     }
 }
-}
diff --git a/Mage.Sets/src/mage/cards/b/BurningInquiry.java b/Mage.Sets/src/mage/cards/b/BurningInquiry.java
index 6691d8323b..b8f1670d93 100644
--- a/Mage.Sets/src/mage/cards/b/BurningInquiry.java
+++ b/Mage.Sets/src/mage/cards/b/BurningInquiry.java
@@ -1,7 +1,5 @@
-
 package mage.cards.b;
 
-import java.util.UUID;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.DrawCardAllEffect;
 import mage.abilities.effects.common.discard.DiscardEachPlayerEffect;
@@ -9,24 +7,24 @@ import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 
+import java.util.UUID;
+
 /**
- *
  * @author North
  */
 public final class BurningInquiry extends CardImpl {
 
     public BurningInquiry(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{R}");
-
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{R}");
 
         // Each player draws three cards, then discards three cards at random.
         this.getSpellAbility().addEffect(new DrawCardAllEffect(3));
         Effect effect = new DiscardEachPlayerEffect(3, true);
-        effect.setText("then discards three cards at random");
+        effect.setText(", then discards three cards at random");
         this.getSpellAbility().addEffect(effect);
     }
 
-    public BurningInquiry(final BurningInquiry card) {
+    private BurningInquiry(final BurningInquiry card) {
         super(card);
     }
 
diff --git a/Mage.Sets/src/mage/cards/c/CabalTherapist.java b/Mage.Sets/src/mage/cards/c/CabalTherapist.java
index 96978c84ee..2a6a3ac881 100644
--- a/Mage.Sets/src/mage/cards/c/CabalTherapist.java
+++ b/Mage.Sets/src/mage/cards/c/CabalTherapist.java
@@ -19,7 +19,6 @@ import mage.game.events.GameEvent;
 import mage.players.Player;
 import mage.target.TargetPlayer;
 import mage.target.common.TargetControlledPermanent;
-import mage.target.common.TargetOpponent;
 import mage.util.CardUtil;
 
 import java.util.UUID;
@@ -138,22 +137,27 @@ class CabalTherapistDiscardEffect extends OneShotEffect {
             return false;
         }
         String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY);
-        Cards hand = targetPlayer.getHand();
-
-        for (Card card : hand.getCards(game)) {
+        Cards hand = targetPlayer.getHand().copy();
+        targetPlayer.revealCards(source, hand, game);
+        hand.removeIf(uuid -> {
+            Card card = hand.get(uuid, game);
+            if (card == null) {
+                return true;
+            }
             if (card.isSplitCard()) {
                 SplitCard splitCard = (SplitCard) card;
                 if (CardUtil.haveSameNames(splitCard.getLeftHalfCard().getName(), cardName)) {
-                    targetPlayer.discard(card, source, game);
+                    return false;
                 } else if (CardUtil.haveSameNames(splitCard.getRightHalfCard().getName(), cardName)) {
-                    targetPlayer.discard(card, source, game);
+                    return false;
                 }
             }
             if (CardUtil.haveSameNames(card.getName(), cardName)) {
-                targetPlayer.discard(card, source, game);
+                return false;
             }
-        }
-        targetPlayer.revealCards("Cabal Therapist", hand, game);
+            return true;
+        });
+        targetPlayer.discard(hand, source, game);
         return true;
     }
 
diff --git a/Mage.Sets/src/mage/cards/c/CabalTherapy.java b/Mage.Sets/src/mage/cards/c/CabalTherapy.java
index ba03643b0a..6aad9ff861 100644
--- a/Mage.Sets/src/mage/cards/c/CabalTherapy.java
+++ b/Mage.Sets/src/mage/cards/c/CabalTherapy.java
@@ -64,25 +64,31 @@ class CabalTherapyEffect extends OneShotEffect {
         Player targetPlayer = game.getPlayer(targetPointer.getFirst(game, source));
         Player controller = game.getPlayer(source.getControllerId());
         MageObject sourceObject = game.getObject(source.getSourceId());
-        if (targetPlayer != null && controller != null && sourceObject != null) {
-            String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY);
-            Cards hand = targetPlayer.getHand();
-
-            for (Card card : hand.getCards(game)) {
-                if (card.isSplitCard()) {
-                    SplitCard splitCard = (SplitCard) card;
-                    if (CardUtil.haveSameNames(splitCard.getLeftHalfCard().getName(), cardName)) {
-                        targetPlayer.discard(card, source, game);
-                    } else if (CardUtil.haveSameNames(splitCard.getRightHalfCard().getName(), cardName)) {
-                        targetPlayer.discard(card, source, game);
-                    }
-                }
-                if (CardUtil.haveSameNames(card.getName(), cardName)) {
-                    targetPlayer.discard(card, source, game);
+        if (targetPlayer == null || controller == null || sourceObject == null) {
+            return false;
+        }
+        String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY);
+        Cards hand = targetPlayer.getHand().copy();
+        targetPlayer.revealCards(source, hand, game);
+        hand.removeIf(uuid -> {
+            Card card = hand.get(uuid, game);
+            if (card == null) {
+                return true;
+            }
+            if (card.isSplitCard()) {
+                SplitCard splitCard = (SplitCard) card;
+                if (CardUtil.haveSameNames(splitCard.getLeftHalfCard().getName(), cardName)) {
+                    return false;
+                } else if (CardUtil.haveSameNames(splitCard.getRightHalfCard().getName(), cardName)) {
+                    return false;
                 }
             }
-            targetPlayer.revealCards("Cabal Therapy", hand, game);
-        }
+            if (CardUtil.haveSameNames(card.getName(), cardName)) {
+                return false;
+            }
+            return true;
+        });
+        targetPlayer.discard(hand, source, game);
         return true;
     }
 
diff --git a/Mage.Sets/src/mage/cards/c/ChandraFlamecaller.java b/Mage.Sets/src/mage/cards/c/ChandraFlamecaller.java
index ce7de428f6..2987b764e4 100644
--- a/Mage.Sets/src/mage/cards/c/ChandraFlamecaller.java
+++ b/Mage.Sets/src/mage/cards/c/ChandraFlamecaller.java
@@ -10,7 +10,6 @@ import mage.abilities.effects.Effect;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.abilities.effects.common.DamageAllEffect;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
@@ -22,7 +21,6 @@ import mage.game.Game;
 import mage.game.permanent.token.ElementalToken;
 import mage.players.Player;
 
-import java.util.Set;
 import java.util.UUID;
 
 /**
@@ -106,16 +104,12 @@ class ChandraDrawEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player player = game.getPlayer(source.getControllerId());
-        if (player != null) {
-            Set<Card> cardsInHand = player.getHand().getCards(game);
-            int amount = cardsInHand.size();
-            for (Card card : cardsInHand) {
-                player.discard(card, source, game);
-            }
-            player.drawCards(amount + 1, source.getSourceId(), game);
-            return true;
+        if (player == null) {
+            return false;
         }
-        return false;
+        int amount = player.discard(player.getHand(), source, game).size();
+        player.drawCards(amount + 1, source.getSourceId(), game);
+        return true;
     }
 }
 
diff --git a/Mage.Sets/src/mage/cards/c/CollectiveDefiance.java b/Mage.Sets/src/mage/cards/c/CollectiveDefiance.java
index 553a8acec7..81f3abefa9 100644
--- a/Mage.Sets/src/mage/cards/c/CollectiveDefiance.java
+++ b/Mage.Sets/src/mage/cards/c/CollectiveDefiance.java
@@ -7,7 +7,6 @@ import mage.abilities.effects.Effect;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.DamageTargetEffect;
 import mage.abilities.keyword.EscalateAbility;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
@@ -90,14 +89,11 @@ class CollectiveDefianceEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player targetPlayer = game.getPlayer(targetPointer.getFirst(game, source));
-        if (targetPlayer != null) {
-            int count = targetPlayer.getHand().size();
-            for (Card card : targetPlayer.getHand().getCards(game)) {
-                targetPlayer.discard(card, source, game);
-            }
-            targetPlayer.drawCards(count, source.getSourceId(), game);
-            return true;
+        if (targetPlayer == null) {
+            return false;
         }
-        return false;
+        int count = targetPlayer.discard(targetPlayer.getHand(), source, game).size();
+        targetPlayer.drawCards(count, source.getSourceId(), game);
+        return true;
     }
 }
diff --git a/Mage.Sets/src/mage/cards/c/CompulsiveResearch.java b/Mage.Sets/src/mage/cards/c/CompulsiveResearch.java
index d132be25f8..11e97e13c6 100644
--- a/Mage.Sets/src/mage/cards/c/CompulsiveResearch.java
+++ b/Mage.Sets/src/mage/cards/c/CompulsiveResearch.java
@@ -1,7 +1,5 @@
-
 package mage.cards.c;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.DrawCardTargetEffect;
@@ -10,20 +8,21 @@ import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.Outcome;
+import mage.filter.StaticFilters;
 import mage.game.Game;
 import mage.players.Player;
 import mage.target.TargetPlayer;
 import mage.target.common.TargetDiscard;
 
+import java.util.UUID;
+
 /**
- *
  * @author Plopman
  */
 public final class CompulsiveResearch extends CardImpl {
 
     public CompulsiveResearch(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{U}");
-
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}");
 
         // Target player draws three cards. Then that player discards two cards unless they discard a land card.
         this.getSpellAbility().addTarget(new TargetPlayer());
@@ -31,7 +30,7 @@ public final class CompulsiveResearch extends CardImpl {
         this.getSpellAbility().addEffect(new CompulsiveResearchDiscardEffect());
     }
 
-    public CompulsiveResearch(final CompulsiveResearch card) {
+    private CompulsiveResearch(final CompulsiveResearch card) {
         super(card);
     }
 
@@ -40,14 +39,15 @@ public final class CompulsiveResearch extends CardImpl {
         return new CompulsiveResearch(this);
     }
 }
+
 class CompulsiveResearchDiscardEffect extends OneShotEffect {
 
-    public CompulsiveResearchDiscardEffect() {
+    CompulsiveResearchDiscardEffect() {
         super(Outcome.Discard);
         this.staticText = "Then that player discards two cards unless they discard a land card";
     }
 
-    public CompulsiveResearchDiscardEffect(final CompulsiveResearchDiscardEffect effect) {
+    private CompulsiveResearchDiscardEffect(final CompulsiveResearchDiscardEffect effect) {
         super(effect);
     }
 
@@ -59,18 +59,19 @@ class CompulsiveResearchDiscardEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player targetPlayer = game.getPlayer(this.getTargetPointer().getFirst(game, source));
-        if (targetPlayer != null && !targetPlayer.getHand().isEmpty()) {
-            TargetDiscard target = new TargetDiscard(targetPlayer.getId());
-            targetPlayer.choose(Outcome.Discard, target, source.getSourceId(), game);
-            Card card = targetPlayer.getHand().get(target.getFirstTarget(), game);
-            if (card != null) {
-                targetPlayer.discard(card, source, game);
-                if (!card.isLand() && !targetPlayer.getHand().isEmpty()) {
-                    targetPlayer.discard(1, false, source, game);
-                }
-                return true;
-            }            
+        if (targetPlayer == null || targetPlayer.getHand().isEmpty()) {
+            return false;
         }
-        return false;
+        if (targetPlayer.getHand().count(StaticFilters.FILTER_CARD_LAND, game) < 1
+                || !targetPlayer.chooseUse(outcome, "Discard a land card?", source, game)) {
+            return !targetPlayer.discard(2, false, source, game).isEmpty();
+        }
+        TargetDiscard target = new TargetDiscard(StaticFilters.FILTER_CARD_LAND_A, targetPlayer.getId());
+        targetPlayer.choose(Outcome.Discard, target, source.getSourceId(), game);
+        Card card = targetPlayer.getHand().get(target.getFirstTarget(), game);
+        if (card != null && targetPlayer.discard(card, source, game)) {
+            return true;
+        }
+        return !targetPlayer.discard(2, false, source, game).isEmpty();
     }
 }
diff --git a/Mage.Sets/src/mage/cards/c/CrosisThePurger.java b/Mage.Sets/src/mage/cards/c/CrosisThePurger.java
index b2f87e2d6a..3646ea742d 100644
--- a/Mage.Sets/src/mage/cards/c/CrosisThePurger.java
+++ b/Mage.Sets/src/mage/cards/c/CrosisThePurger.java
@@ -1,9 +1,5 @@
-
 package mage.cards.c;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
@@ -11,23 +7,23 @@ import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.DoIfCostPaid;
 import mage.abilities.keyword.FlyingAbility;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
+import mage.cards.Cards;
+import mage.cards.CardsImpl;
 import mage.choices.ChoiceColor;
 import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.constants.SubType;
 import mage.constants.SuperType;
-import mage.filter.FilterCard;
-import mage.filter.predicate.mageobject.ColorPredicate;
 import mage.game.Game;
 import mage.players.Player;
 
+import java.util.UUID;
+import java.util.stream.Collectors;
+
 /**
- *
  * @author LoneFox
- *
  */
 public final class CrosisThePurger extends CardImpl {
 
@@ -45,7 +41,7 @@ public final class CrosisThePurger extends CardImpl {
                 new ManaCostsImpl("{2}{B}")), false, true));
     }
 
-    public CrosisThePurger(final CrosisThePurger card) {
+    private CrosisThePurger(final CrosisThePurger card) {
         super(card);
     }
 
@@ -62,7 +58,7 @@ class CrosisThePurgerEffect extends OneShotEffect {
         this.staticText = "choose a color, then that player reveals their hand and discards all cards of that color.";
     }
 
-    CrosisThePurgerEffect(final CrosisThePurgerEffect effect) {
+    private CrosisThePurgerEffect(final CrosisThePurgerEffect effect) {
         super(effect);
     }
 
@@ -74,30 +70,28 @@ class CrosisThePurgerEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player player = game.getPlayer(source.getControllerId());
-        if (player != null) {
-            ChoiceColor choice = new ChoiceColor();
-            player.choose(outcome, choice, game);
-            if (choice.isChosen()) {
-                game.informPlayers(player.getLogName() + " chooses " + choice.getColor());
-                Player damagedPlayer = game.getPlayer(this.getTargetPointer().getFirst(game, source));
-                if(damagedPlayer != null) {
-                    damagedPlayer.revealCards("hand of " + damagedPlayer.getName(), damagedPlayer.getHand(), game);
-                    FilterCard filter = new FilterCard();
-                    filter.add(new ColorPredicate(choice.getColor()));
-                    List<Card> toDiscard = new ArrayList<>();
-                    for (UUID cardId : damagedPlayer.getHand()) {
-                        Card card = game.getCard(cardId);
-                        if (filter.match(card, game)) {
-                            toDiscard.add(card);
-                        }
-                    }
-                    for (Card card : toDiscard) {
-                        damagedPlayer.discard(card, source, game);
-                    }
-                    return true;
-                }
-            }
+        if (player == null) {
+            return false;
         }
-        return false;
+        ChoiceColor choice = new ChoiceColor();
+        player.choose(outcome, choice, game);
+        if (!choice.isChosen()) {
+            return false;
+        }
+        game.informPlayers(player.getLogName() + " chooses " + choice.getColor());
+        Player damagedPlayer = game.getPlayer(this.getTargetPointer().getFirst(game, source));
+        if (damagedPlayer == null) {
+            return false;
+        }
+        damagedPlayer.revealCards("hand of " + damagedPlayer.getName(), damagedPlayer.getHand(), game);
+        Cards cards = new CardsImpl(
+                player.getHand()
+                        .getCards(game)
+                        .stream()
+                        .filter(card -> card.getColor(game).shares(choice.getColor()))
+                        .collect(Collectors.toSet())
+        );
+        damagedPlayer.discard(cards, source, game);
+        return true;
     }
 }
diff --git a/Mage.Sets/src/mage/cards/d/DangerousWager.java b/Mage.Sets/src/mage/cards/d/DangerousWager.java
index 1d328c8c06..1e98390b3e 100644
--- a/Mage.Sets/src/mage/cards/d/DangerousWager.java
+++ b/Mage.Sets/src/mage/cards/d/DangerousWager.java
@@ -1,34 +1,27 @@
-
 package mage.cards.d;
 
-import java.util.UUID;
-import mage.abilities.Ability;
-import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
-import mage.cards.Card;
+import mage.abilities.effects.common.discard.DiscardHandControllerEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.Outcome;
-import mage.game.Game;
-import mage.players.Player;
+
+import java.util.UUID;
 
 /**
- *
  * @author North
  */
 public final class DangerousWager extends CardImpl {
 
     public DangerousWager(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{R}");
-
+        super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{R}");
 
         // Discard your hand, then draw two cards.
-        this.getSpellAbility().addEffect(new DangerousWagerEffect());
-        this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2));
+        this.getSpellAbility().addEffect(new DiscardHandControllerEffect());
+        this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2).setText(", then draw two cards"));
     }
 
-    public DangerousWager(final DangerousWager card) {
+    private DangerousWager(final DangerousWager card) {
         super(card);
     }
 
@@ -37,32 +30,3 @@ public final class DangerousWager extends CardImpl {
         return new DangerousWager(this);
     }
 }
-
-class DangerousWagerEffect extends OneShotEffect {
-
-    public DangerousWagerEffect() {
-        super(Outcome.Discard);
-        this.staticText = "Discard your hand";
-    }
-
-    public DangerousWagerEffect(final DangerousWagerEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public DangerousWagerEffect copy() {
-        return new DangerousWagerEffect(this);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        Player player = game.getPlayer(source.getControllerId());
-        if (player != null) {
-            for (Card card : player.getHand().getCards(game)) {
-                player.discard(card, source, game);
-            }
-            return true;
-        }
-        return false;
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/d/DarettiScrapSavant.java b/Mage.Sets/src/mage/cards/d/DarettiScrapSavant.java
index 03bf92605c..294589fc0c 100644
--- a/Mage.Sets/src/mage/cards/d/DarettiScrapSavant.java
+++ b/Mage.Sets/src/mage/cards/d/DarettiScrapSavant.java
@@ -1,7 +1,5 @@
-
 package mage.cards.d;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.LoyaltyAbility;
 import mage.abilities.common.CanBeYourCommanderAbility;
@@ -14,12 +12,8 @@ import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffec
 import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.CardType;
-import mage.constants.SubType;
-import mage.constants.Outcome;
-import mage.constants.SuperType;
-import mage.constants.TargetController;
-import mage.constants.Zone;
+import mage.cards.CardsImpl;
+import mage.constants.*;
 import mage.filter.FilterCard;
 import mage.filter.common.FilterArtifactCard;
 import mage.filter.common.FilterControlledArtifactPermanent;
@@ -33,8 +27,9 @@ import mage.target.common.TargetControlledPermanent;
 import mage.target.common.TargetDiscard;
 import mage.target.targetpointer.FixedTarget;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class DarettiScrapSavant extends CardImpl {
@@ -93,14 +88,7 @@ class DarettiDiscardDrawEffect extends OneShotEffect {
         if (controller != null) {
             TargetDiscard target = new TargetDiscard(0, 2, new FilterCard(), controller.getId());
             target.choose(outcome, controller.getId(), source.getSourceId(), game);
-            int count = 0;
-            for (UUID cardId : target.getTargets()) {
-                Card card = game.getCard(cardId);
-                if (card != null) {
-                    controller.discard(card, source, game);
-                    count++;
-                }
-            }
+            int count = controller.discard(new CardsImpl(target.getTargets()), source, game).size();
             controller.drawCards(count, source.getSourceId(), game);
             return true;
         }
diff --git a/Mage.Sets/src/mage/cards/d/DiscordantDirge.java b/Mage.Sets/src/mage/cards/d/DiscordantDirge.java
index 4d2c5f9405..f81f8fa141 100644
--- a/Mage.Sets/src/mage/cards/d/DiscordantDirge.java
+++ b/Mage.Sets/src/mage/cards/d/DiscordantDirge.java
@@ -9,6 +9,7 @@ import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.counter.AddCountersSourceEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
+import mage.cards.CardsImpl;
 import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.constants.TargetController;
@@ -21,7 +22,6 @@ import mage.players.Player;
 import mage.target.TargetCard;
 import mage.target.common.TargetOpponent;
 
-import java.util.Objects;
 import java.util.UUID;
 
 /**
@@ -29,8 +29,6 @@ import java.util.UUID;
  */
 public final class DiscordantDirge extends CardImpl {
 
-    private static final String rule = "Look at target opponent's hand and choose up to X cards from it, where X is the number of verse counters on {this}. That player discards those cards..";
-
     public DiscordantDirge(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{B}{B}");
 
@@ -47,7 +45,7 @@ public final class DiscordantDirge extends CardImpl {
         this.addAbility(ability);
     }
 
-    public DiscordantDirge(final DiscordantDirge card) {
+    private DiscordantDirge(final DiscordantDirge card) {
         super(card);
     }
 
@@ -59,37 +57,37 @@ public final class DiscordantDirge extends CardImpl {
 
 class DiscordantDirgeEffect extends OneShotEffect {
 
-    public DiscordantDirgeEffect() {
+    DiscordantDirgeEffect() {
         super(Outcome.Benefit);
-        staticText = "Look at target opponent's hand and choose up to X cards from it, where X is the number of verse counters on {this}. That player discards those card.";
+        staticText = "Look at target opponent's hand and choose up to X cards from it, " +
+                "where X is the number of verse counters on {this}. That player discards those cards";
     }
 
-    public DiscordantDirgeEffect(final DiscordantDirgeEffect effect) {
+    private DiscordantDirgeEffect(final DiscordantDirgeEffect effect) {
         super(effect);
     }
 
     @Override
     public boolean apply(Game game, Ability source) {
         Permanent discordantDirge = game.getPermanentOrLKIBattlefield(source.getSourceId());
-        if (discordantDirge != null) {
-            int verseCounters = discordantDirge.getCounters(game).getCount(CounterType.VERSE);
-            Player targetOpponent = game.getPlayer(source.getFirstTarget());
-            Player controller = game.getPlayer(source.getControllerId());
-            if (targetOpponent != null
-                    && controller != null) {
-                controller.lookAtCards(targetOpponent.getName() + " hand", targetOpponent.getHand(), game);
-                TargetCard target = new TargetCard(0, verseCounters, Zone.HAND, new FilterCard());
-                target.setNotTarget(true);
-                if (controller.choose(Outcome.Benefit, targetOpponent.getHand(), target, game)) {
-                    target.getTargets().stream().map(game::getCard).filter(Objects::nonNull).filter((card) -> (card != null
-                            && targetOpponent.getHand().contains(card.getId()))).forEachOrdered((card) -> {
-                        targetOpponent.discard(card, source, game);
-                    });
-                    return true;
-                }
-            }
+        if (discordantDirge == null) {
+            return false;
         }
-        return false;
+        int verseCounters = discordantDirge.getCounters(game).getCount(CounterType.VERSE);
+        Player targetOpponent = game.getPlayer(source.getFirstTarget());
+        Player controller = game.getPlayer(source.getControllerId());
+        if (targetOpponent == null
+                || controller == null) {
+            return false;
+        }
+        controller.lookAtCards(targetOpponent.getName() + " hand", targetOpponent.getHand(), game);
+        TargetCard target = new TargetCard(0, verseCounters, Zone.HAND, new FilterCard());
+        target.setNotTarget(true);
+        if (!controller.choose(Outcome.Benefit, targetOpponent.getHand(), target, game)) {
+            return false;
+        }
+        targetOpponent.discard(new CardsImpl(target.getTargets()), source, game);
+        return true;
     }
 
     @Override
diff --git a/Mage.Sets/src/mage/cards/d/DistendedMindbender.java b/Mage.Sets/src/mage/cards/d/DistendedMindbender.java
index 13500ac144..cff8d820f6 100644
--- a/Mage.Sets/src/mage/cards/d/DistendedMindbender.java
+++ b/Mage.Sets/src/mage/cards/d/DistendedMindbender.java
@@ -1,38 +1,37 @@
-
 package mage.cards.d;
 
-import java.util.List;
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.CastSourceTriggeredAbility;
 import mage.abilities.keyword.EmergeAbility;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
+import mage.cards.Cards;
+import mage.cards.CardsImpl;
 import mage.constants.CardType;
-import mage.constants.SubType;
 import mage.constants.ComparisonType;
 import mage.constants.Outcome;
-import mage.constants.Zone;
+import mage.constants.SubType;
 import mage.filter.FilterCard;
 import mage.filter.predicate.Predicates;
 import mage.filter.predicate.mageobject.ConvertedManaCostPredicate;
 import mage.game.Game;
 import mage.players.Player;
 import mage.target.TargetCard;
+import mage.target.common.TargetCardInHand;
 import mage.target.common.TargetOpponent;
 
+import java.util.UUID;
+
 /**
- *
  * @author fireshoes
  */
 public final class DistendedMindbender extends CardImpl {
 
     public DistendedMindbender(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{8}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{8}");
         this.subtype.add(SubType.ELDRAZI);
         this.subtype.add(SubType.INSECT);
         this.power = new MageInt(5);
@@ -88,30 +87,20 @@ class DistendedMindbenderEffect extends OneShotEffect {
     public boolean apply(Game game, Ability source) {
         Player opponent = game.getPlayer(source.getFirstTarget());
         Player controller = game.getPlayer(source.getControllerId());
-        if (opponent != null && controller != null) {
-            opponent.revealCards("Distended Mindbender", opponent.getHand(), game);
-            TargetCard targetThreeOrLess = new TargetCard(1, Zone.HAND, filterThreeOrLess);
-            TargetCard targetFourOrGreater = new TargetCard(1, Zone.HAND, filterFourOrGreater);
-            if (controller.choose(Outcome.Benefit, opponent.getHand(), targetThreeOrLess, game)) {
-                List<UUID> targets = targetThreeOrLess.getTargets();
-                for (UUID targetId : targets) {
-                    Card card = opponent.getHand().get(targetId, game);
-                    if (card != null) {
-                        opponent.discard(card, source, game);
-                    }
-                }
-            }
-            if (controller.choose(Outcome.Benefit, opponent.getHand(), targetFourOrGreater, game)) {
-                List<UUID> targets = targetFourOrGreater.getTargets();
-                for (UUID targetId : targets) {
-                    Card card = opponent.getHand().get(targetId, game);
-                    if (card != null) {
-                        opponent.discard(card, source, game);
-                    }
-                }
-            }
-            return true;
+        if (opponent == null || controller == null) {
+            return false;
         }
-        return false;
+        opponent.revealCards(source, opponent.getHand(), game);
+        TargetCard targetThreeOrLess = new TargetCardInHand(1, filterThreeOrLess);
+        TargetCard targetFourOrGreater = new TargetCardInHand(1, filterFourOrGreater);
+        Cards toDiscard = new CardsImpl();
+        if (controller.choose(Outcome.Benefit, opponent.getHand(), targetThreeOrLess, game)) {
+            toDiscard.addAll(targetThreeOrLess.getTargets());
+        }
+        if (controller.choose(Outcome.Benefit, opponent.getHand(), targetFourOrGreater, game)) {
+            toDiscard.addAll(targetFourOrGreater.getTargets());
+        }
+        opponent.discard(toDiscard, source, game);
+        return true;
     }
 }
diff --git a/Mage.Sets/src/mage/cards/d/DrasticRevelation.java b/Mage.Sets/src/mage/cards/d/DrasticRevelation.java
index d6391c7de8..df6a334e9a 100644
--- a/Mage.Sets/src/mage/cards/d/DrasticRevelation.java
+++ b/Mage.Sets/src/mage/cards/d/DrasticRevelation.java
@@ -2,10 +2,8 @@ package mage.cards.d;
 
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.cards.Cards;
 import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.game.Game;
@@ -25,7 +23,7 @@ public final class DrasticRevelation extends CardImpl {
         this.getSpellAbility().addEffect(new DrasticRevelationEffect());
     }
 
-    public DrasticRevelation(final DrasticRevelation card) {
+    private DrasticRevelation(final DrasticRevelation card) {
         super(card);
     }
 
@@ -42,24 +40,19 @@ class DrasticRevelationEffect extends OneShotEffect {
         staticText = "Discard your hand. Draw seven cards, then discard three cards at random";
     }
 
-    DrasticRevelationEffect(final DrasticRevelationEffect effect) {
+    private DrasticRevelationEffect(final DrasticRevelationEffect effect) {
         super(effect);
     }
 
     @Override
     public boolean apply(Game game, Ability source) {
         Player you = game.getPlayer(source.getControllerId());
-        if (you != null) {
-            you.discard(you.getHand().size(), false, source, game);
-            you.drawCards(7, source.getSourceId(), game);
-            Cards hand = you.getHand();
-            for (int i = 0; i < 3; i++) {
-                Card card = hand.getRandom(game);
-                if (card != null) {
-                    you.discard(card, source, game);
-                }
-            }
+        if (you == null) {
+            return false;
         }
+        you.discard(you.getHand(), source, game);
+        you.drawCards(7, source.getSourceId(), game);
+        you.discard(3, true, source, game);
         return false;
     }
 
@@ -67,4 +60,4 @@ class DrasticRevelationEffect extends OneShotEffect {
     public DrasticRevelationEffect copy() {
         return new DrasticRevelationEffect(this);
     }
-}
\ No newline at end of file
+}
diff --git a/Mage.Sets/src/mage/cards/e/Extortion.java b/Mage.Sets/src/mage/cards/e/Extortion.java
index 46d5163ae0..86c210dc31 100644
--- a/Mage.Sets/src/mage/cards/e/Extortion.java
+++ b/Mage.Sets/src/mage/cards/e/Extortion.java
@@ -1,19 +1,18 @@
-
 package mage.cards.e;
 
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
+import mage.cards.CardsImpl;
 import mage.constants.CardType;
 import mage.constants.Outcome;
-import mage.constants.Zone;
-import mage.filter.FilterCard;
+import mage.filter.StaticFilters;
 import mage.game.Game;
 import mage.players.Player;
 import mage.target.TargetCard;
 import mage.target.TargetPlayer;
+import mage.target.common.TargetCardInHand;
 
 import java.util.UUID;
 
@@ -30,7 +29,7 @@ public final class Extortion extends CardImpl {
         this.getSpellAbility().addTarget(new TargetPlayer());
     }
 
-    public Extortion(final Extortion card) {
+    private Extortion(final Extortion card) {
         super(card);
     }
 
@@ -42,12 +41,12 @@ public final class Extortion extends CardImpl {
 
 class ExtortionEffect extends OneShotEffect {
 
-    public ExtortionEffect() {
+    ExtortionEffect() {
         super(Outcome.Discard);
-        staticText = "Look at target player's hand and choose up to two cards from it. That player discards that card.";
+        staticText = "Look at target player's hand and choose up to two cards from it. That player discards those cards.";
     }
 
-    public ExtortionEffect(final ExtortionEffect effect) {
+    private ExtortionEffect(final ExtortionEffect effect) {
         super(effect);
     }
 
@@ -55,18 +54,15 @@ class ExtortionEffect extends OneShotEffect {
     public boolean apply(Game game, Ability source) {
         Player targetPlayer = game.getPlayer(source.getFirstTarget());
         Player you = game.getPlayer(source.getControllerId());
-        if (targetPlayer != null && you != null) {
-            you.lookAtCards("Discard", targetPlayer.getHand(), game);
-            TargetCard target = new TargetCard(0, 2, Zone.HAND, new FilterCard());
-            target.setNotTarget(true);
-            if (you.choose(Outcome.Benefit, targetPlayer.getHand(), target, game)) {
-                Card card = targetPlayer.getHand().get(target.getFirstTarget(), game);
-                return targetPlayer.discard(card, source, game);
-
-            }
-
+        if (targetPlayer == null || you == null) {
+            return false;
         }
-        return false;
+        you.lookAtCards("Discard", targetPlayer.getHand(), game);
+        TargetCard target = new TargetCardInHand(0, 2, StaticFilters.FILTER_CARD_CARDS);
+        target.setNotTarget(true);
+        you.choose(Outcome.Discard, targetPlayer.getHand(), target, game);
+        targetPlayer.discard(new CardsImpl(target.getTargets()), source, game);
+        return true;
     }
 
     @Override
diff --git a/Mage.Sets/src/mage/cards/g/GruesomeDiscovery.java b/Mage.Sets/src/mage/cards/g/GruesomeDiscovery.java
index 7e996c5a25..9d33d69e3c 100644
--- a/Mage.Sets/src/mage/cards/g/GruesomeDiscovery.java
+++ b/Mage.Sets/src/mage/cards/g/GruesomeDiscovery.java
@@ -1,4 +1,3 @@
-
 package mage.cards.g;
 
 import mage.abilities.Ability;
@@ -6,19 +5,18 @@ import mage.abilities.condition.common.MorbidCondition;
 import mage.abilities.decorator.ConditionalOneShotEffect;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.discard.DiscardTargetEffect;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
+import mage.cards.CardsImpl;
 import mage.constants.CardType;
 import mage.constants.Outcome;
-import mage.constants.Zone;
-import mage.filter.FilterCard;
+import mage.filter.StaticFilters;
 import mage.game.Game;
 import mage.players.Player;
 import mage.target.TargetCard;
 import mage.target.TargetPlayer;
+import mage.target.common.TargetCardInHand;
 
-import java.util.List;
 import java.util.UUID;
 
 /**
@@ -29,18 +27,18 @@ public final class GruesomeDiscovery extends CardImpl {
     public GruesomeDiscovery(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}{B}");
 
-
         // Target player discards two cards.
         // <i>Morbid</i> &mdash; If a creature died this turn, instead that player reveals their hand, you choose two cards from it, then that player discards those cards.
         this.getSpellAbility().addEffect(new ConditionalOneShotEffect(
-                new GruesomeDiscoveryEffect(),
-                new DiscardTargetEffect(2),
-                MorbidCondition.instance,
-                "Target player discards two cards. <i>Morbid</i> &mdash; If a creature died this turn, instead that player reveals their hand, you choose two cards from it, then that player discards those cards"));
+                new GruesomeDiscoveryEffect(), new DiscardTargetEffect(2),
+                MorbidCondition.instance, "Target player discards two cards. " +
+                "<br><i>Morbid</i> &mdash; If a creature died this turn, instead that player reveals their hand, " +
+                "you choose two cards from it, then that player discards those cards"
+        ));
         this.getSpellAbility().addTarget(new TargetPlayer());
     }
 
-    public GruesomeDiscovery(final GruesomeDiscovery card) {
+    private GruesomeDiscovery(final GruesomeDiscovery card) {
         super(card);
     }
 
@@ -52,12 +50,11 @@ public final class GruesomeDiscovery extends CardImpl {
 
 class GruesomeDiscoveryEffect extends OneShotEffect {
 
-    public GruesomeDiscoveryEffect() {
+    GruesomeDiscoveryEffect() {
         super(Outcome.Discard);
-        this.staticText = "target player reveals their hand, you choose two cards from it, then that player discards those cards";
     }
 
-    public GruesomeDiscoveryEffect(final GruesomeDiscoveryEffect effect) {
+    private GruesomeDiscoveryEffect(final GruesomeDiscoveryEffect effect) {
         super(effect);
     }
 
@@ -70,25 +67,16 @@ class GruesomeDiscoveryEffect extends OneShotEffect {
     public boolean apply(Game game, Ability source) {
         Player player = game.getPlayer(source.getControllerId());
         Player targetPlayer = game.getPlayer(source.getFirstTarget());
-
-        if (player != null && targetPlayer != null) {
-            targetPlayer.revealCards("Gruesome Discovery", targetPlayer.getHand(), game);
-
-            if (targetPlayer.getHand().size() <= 2) {
-                targetPlayer.discard(2, false, source, game);
-            }
-
-            TargetCard target = new TargetCard(2, Zone.HAND, new FilterCard());
-            if (player.choose(Outcome.Benefit, targetPlayer.getHand(), target, game)) {
-                List<UUID> targets = target.getTargets();
-                for (UUID targetId : targets) {
-                    Card card = targetPlayer.getHand().get(targetId, game);
-                    targetPlayer.discard(card, source, game);
-
-                }
-            }
-            return true;
+        if (player == null || targetPlayer == null) {
+            return false;
         }
-        return false;
+        targetPlayer.revealCards(source, targetPlayer.getHand(), game);
+        if (targetPlayer.getHand().size() <= 2) {
+            targetPlayer.discard(2, false, source, game);
+        }
+        TargetCard target = new TargetCardInHand(2, StaticFilters.FILTER_CARD_CARDS);
+        player.choose(Outcome.Discard, targetPlayer.getHand(), target, game);
+        targetPlayer.discard(new CardsImpl(target.getTargets()), source, game);
+        return true;
     }
 }
diff --git a/Mage.Sets/src/mage/cards/h/HintOfInsanity.java b/Mage.Sets/src/mage/cards/h/HintOfInsanity.java
index 1d29958721..d41189a633 100644
--- a/Mage.Sets/src/mage/cards/h/HintOfInsanity.java
+++ b/Mage.Sets/src/mage/cards/h/HintOfInsanity.java
@@ -1,21 +1,26 @@
 package mage.cards.h;
 
+import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
-import mage.cards.*;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.cards.Cards;
+import mage.cards.CardsImpl;
 import mage.constants.CardType;
 import mage.constants.Outcome;
-import mage.filter.FilterCard;
 import mage.game.Game;
 import mage.players.Player;
 import mage.target.TargetPlayer;
-import mage.target.common.TargetCardInHand;
-import mage.util.CardUtil;
 
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
 import java.util.UUID;
+import java.util.stream.Collectors;
 
 /**
- * @author jeffwadsworth
+ * @author TheElk801
  */
 public final class HintOfInsanity extends CardImpl {
 
@@ -25,10 +30,9 @@ public final class HintOfInsanity extends CardImpl {
         // Target player reveals their hand. That player discards all nonland cards with the same name as another card in their hand.
         this.getSpellAbility().addEffect(new HintOfInsanityEffect());
         this.getSpellAbility().addTarget(new TargetPlayer());
-
     }
 
-    public HintOfInsanity(final HintOfInsanity card) {
+    private HintOfInsanity(final HintOfInsanity card) {
         super(card);
     }
 
@@ -40,12 +44,13 @@ public final class HintOfInsanity extends CardImpl {
 
 class HintOfInsanityEffect extends OneShotEffect {
 
-    public HintOfInsanityEffect() {
+    HintOfInsanityEffect() {
         super(Outcome.Discard);
-        this.staticText = "Target player reveals their hand. That player discards all nonland cards with the same name as another card in their hand";
+        this.staticText = "Target player reveals their hand. " +
+                "That player discards all nonland cards with the same name as another card in their hand";
     }
 
-    public HintOfInsanityEffect(final HintOfInsanityEffect effect) {
+    private HintOfInsanityEffect(final HintOfInsanityEffect effect) {
         super(effect);
     }
 
@@ -56,26 +61,26 @@ class HintOfInsanityEffect extends OneShotEffect {
 
     @Override
     public boolean apply(Game game, Ability source) {
-        FilterCard filter = new FilterCard("card from your hand");
-        Player targetPlayer = game.getPlayer(source.getFirstTarget());
-        Card chosenCard;
-        if (targetPlayer != null) {
-            TargetCardInHand targetCard = new TargetCardInHand(filter);
-            targetCard.setNotTarget(true);
-            Cards cardsInHand = new CardsImpl();
-            cardsInHand.addAll(targetPlayer.getHand());
-            targetPlayer.revealCards("Hint of Insanity Reveal", cardsInHand, game);
-            if (!cardsInHand.isEmpty()
-                    && targetPlayer.choose(Outcome.Discard, targetCard, source.getSourceId(), game)) {
-                chosenCard = game.getCard(targetCard.getFirstTarget());
-                for (Card card : cardsInHand.getCards(game)) {
-                    if (CardUtil.haveSameNames(card, chosenCard) && !card.isLand()) {
-                        targetPlayer.discard(card, source, game);
-                    }
-                }
-                return true;
-            }
+        Player player = game.getPlayer(source.getFirstTarget());
+        if (player == null) {
+            return false;
         }
-        return false;
+        Map<String, Integer> nameCounts = new HashMap<>();
+        player.getHand()
+                .getCards(game)
+                .stream()
+                .map(MageObject::getName)
+                .forEach(s -> nameCounts.compute(s, (u, i) -> i == null ? 1 : Integer.sum(i, 1)));
+        Cards cards = new CardsImpl(
+                player.getHand()
+                        .getCards(game)
+                        .stream()
+                        .filter(Objects::nonNull)
+                        .filter(card -> !card.isLand())
+                        .filter(card -> nameCounts.getOrDefault(card.getName(), 0) > 1)
+                        .collect(Collectors.toSet())
+        );
+        player.discard(cards, source, game);
+        return true;
     }
 }
diff --git a/Mage.Sets/src/mage/cards/i/InfernalKirin.java b/Mage.Sets/src/mage/cards/i/InfernalKirin.java
index 38cdc8b4a7..eedc3483ba 100644
--- a/Mage.Sets/src/mage/cards/i/InfernalKirin.java
+++ b/Mage.Sets/src/mage/cards/i/InfernalKirin.java
@@ -1,20 +1,14 @@
-
 package mage.cards.i;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.SpellCastControllerTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.keyword.FlyingAbility;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.CardType;
-import mage.constants.SubType;
-import mage.constants.Outcome;
-import mage.constants.SuperType;
-import mage.constants.Zone;
+import mage.cards.Cards;
+import mage.constants.*;
 import mage.filter.StaticFilters;
 import mage.game.Game;
 import mage.game.stack.Spell;
@@ -22,8 +16,9 @@ import mage.players.Player;
 import mage.target.Target;
 import mage.target.TargetPlayer;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class InfernalKirin extends CardImpl {
@@ -39,14 +34,14 @@ public final class InfernalKirin extends CardImpl {
 
         // Flying
         this.addAbility(FlyingAbility.getInstance());
+
         // Whenever you cast a Spirit or Arcane spell, target player reveals their hand and discards all cards with that spell's converted mana cost.
         Ability ability = new SpellCastControllerTriggeredAbility(Zone.BATTLEFIELD, new InfernalKirinEffect(), StaticFilters.SPIRIT_OR_ARCANE_CARD, false, true);
         ability.addTarget(new TargetPlayer());
         this.addAbility(ability);
-
     }
 
-    public InfernalKirin(final InfernalKirin card) {
+    private InfernalKirin(final InfernalKirin card) {
         super(card);
     }
 
@@ -58,12 +53,12 @@ public final class InfernalKirin extends CardImpl {
 
 class InfernalKirinEffect extends OneShotEffect {
 
-    public InfernalKirinEffect() {
+    InfernalKirinEffect() {
         super(Outcome.Detriment);
         this.staticText = "target player reveals their hand and discards all cards with that spell's converted mana cost";
     }
 
-    public InfernalKirinEffect(final InfernalKirinEffect effect) {
+    private InfernalKirinEffect(final InfernalKirinEffect effect) {
         super(effect);
     }
 
@@ -75,27 +70,26 @@ class InfernalKirinEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Spell spell = game.getSpellOrLKIStack(this.getTargetPointer().getFirst(game, source));
-        if (spell != null) {
-            int cmc = spell.getConvertedManaCost();
-            Player targetPlayer = null;
-            for (Target target : source.getTargets()) {
-                if (target instanceof TargetPlayer) {
-                    targetPlayer = game.getPlayer(target.getFirstTarget());
-                }
-            }
-            if (targetPlayer != null) {
-                if (!targetPlayer.getHand().isEmpty()) {
-                    targetPlayer.revealCards("Infernal Kirin", targetPlayer.getHand(), game);
-                    for (UUID uuid : targetPlayer.getHand().copy()) {
-                        Card card = game.getCard(uuid);
-                        if (card != null && card.getConvertedManaCost() == cmc) {
-                            targetPlayer.discard(card, source, game);
-                        }
-                    }
-                }
-                return true;
+        if (spell == null) {
+            return false;
+        }
+        int cmc = spell.getConvertedManaCost();
+        Player targetPlayer = null;
+        for (Target target : source.getTargets()) {
+            if (target instanceof TargetPlayer) {
+                targetPlayer = game.getPlayer(target.getFirstTarget());
             }
         }
-        return false;
+        if (targetPlayer == null) {
+            return false;
+        }
+        if (targetPlayer.getHand().isEmpty()) {
+            return true;
+        }
+        targetPlayer.revealCards(source, targetPlayer.getHand(), game);
+        Cards cards = targetPlayer.getHand().copy();
+        cards.removeIf(uuid -> game.getCard(uuid).getConvertedManaCost() != cmc);
+        targetPlayer.discard(cards, source, game);
+        return true;
     }
 }
diff --git a/Mage.Sets/src/mage/cards/j/JayaBallard.java b/Mage.Sets/src/mage/cards/j/JayaBallard.java
index 8af23864bf..6b0e37e075 100644
--- a/Mage.Sets/src/mage/cards/j/JayaBallard.java
+++ b/Mage.Sets/src/mage/cards/j/JayaBallard.java
@@ -1,18 +1,16 @@
-
 package mage.cards.j;
 
-import java.util.UUID;
 import mage.Mana;
 import mage.abilities.Ability;
 import mage.abilities.LoyaltyAbility;
 import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
 import mage.abilities.effects.OneShotEffect;
-import mage.abilities.effects.mana.AddConditionalManaEffect;
 import mage.abilities.effects.common.GetEmblemEffect;
+import mage.abilities.effects.mana.AddConditionalManaEffect;
 import mage.abilities.mana.builder.common.InstantOrSorcerySpellManaBuilder;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
+import mage.cards.CardsImpl;
 import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.constants.SubType;
@@ -24,8 +22,9 @@ import mage.players.Player;
 import mage.target.common.TargetDiscard;
 import mage.watchers.common.CastFromGraveyardWatcher;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class JayaBallard extends CardImpl {
@@ -76,20 +75,13 @@ class JayaBallardDiscardDrawEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player controller = game.getPlayer(source.getControllerId());
-        if (controller != null) {
-            TargetDiscard target = new TargetDiscard(0, 3, new FilterCard(), controller.getId());
-            target.choose(outcome, controller.getId(), source.getSourceId(), game);
-            int count = 0;
-            for (UUID cardId : target.getTargets()) {
-                Card card = game.getCard(cardId);
-                if (card != null) {
-                    controller.discard(card, source, game);
-                    count++;
-                }
-            }
-            controller.drawCards(count, source.getSourceId(), game);
-            return true;
+        if (controller == null) {
+            return false;
         }
-        return false;
+        TargetDiscard target = new TargetDiscard(0, 3, new FilterCard(), controller.getId());
+        target.choose(outcome, controller.getId(), source.getSourceId(), game);
+        int count = controller.discard(new CardsImpl(target.getTargets()), source, game).size();
+        controller.drawCards(count, source.getSourceId(), game);
+        return true;
     }
 }
diff --git a/Mage.Sets/src/mage/cards/l/LastRites.java b/Mage.Sets/src/mage/cards/l/LastRites.java
index 7cb78edc19..81d3dcb06b 100644
--- a/Mage.Sets/src/mage/cards/l/LastRites.java
+++ b/Mage.Sets/src/mage/cards/l/LastRites.java
@@ -1,29 +1,30 @@
 package mage.cards.l;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.dynamicvalue.common.StaticValue;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.discard.DiscardCardYouChooseTargetEffect;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.cards.Cards;
+import mage.cards.CardsImpl;
 import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.constants.TargetController;
-import mage.constants.Zone;
 import mage.filter.FilterCard;
+import mage.filter.StaticFilters;
 import mage.filter.predicate.Predicates;
 import mage.game.Game;
 import mage.players.Player;
 import mage.target.TargetCard;
 import mage.target.TargetPlayer;
+import mage.target.common.TargetDiscard;
 import mage.target.targetpointer.FixedTarget;
 
+import java.util.UUID;
+
 /**
- *
  * @author L_J
  */
 public final class LastRites extends CardImpl {
@@ -32,14 +33,13 @@ public final class LastRites extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}");
 
         // Discard any number of cards. Target player reveals their hand, then 
-        //you choose a nonland card from it for each card discarded 
+        // you choose a nonland card from it for each card discarded
         // this way. That player discards those cards.
         this.getSpellAbility().addEffect(new LastRitesEffect());
         this.getSpellAbility().addTarget(new TargetPlayer());
-
     }
 
-    public LastRites(final LastRites card) {
+    private LastRites(final LastRites card) {
         super(card);
     }
 
@@ -58,7 +58,7 @@ class LastRitesEffect extends OneShotEffect {
                 + "each card discarded this way. That player discards those cards";
     }
 
-    LastRitesEffect(final LastRitesEffect effect) {
+    private LastRitesEffect(final LastRitesEffect effect) {
         super(effect);
     }
 
@@ -71,28 +71,21 @@ class LastRitesEffect extends OneShotEffect {
     public boolean apply(Game game, Ability source) {
         Player controller = game.getPlayer(source.getControllerId());
         Player targetPlayer = game.getPlayer(this.getTargetPointer().getFirst(game, source));
-        if (controller != null && targetPlayer != null) {
-            Cards cardsInHand = controller.getHand().copy();
-            TargetCard target = new TargetCard(0, Integer.MAX_VALUE, Zone.HAND, new FilterCard("cards to discard"));
-            controller.chooseTarget(Outcome.AIDontUseIt, cardsInHand, target, source, game);
-            int discardCount = target.getTargets().size();
-            if (discardCount > 0) {
-                for (UUID cardId : target.getTargets()) {
-                    Card card = game.getCard(cardId);
-                    if (card != null) {
-                        controller.discard(card, source, game);
-                    }
-                }
-                FilterCard filter = new FilterCard((discardCount > 1 ? "" : "a")
-                        + " nonland card" + (discardCount > 1 ? "s" : ""));
-                filter.add(Predicates.not(CardType.LAND.getPredicate()));
-                StaticValue discardValue = StaticValue.get(discardCount);
-                Effect effect = new DiscardCardYouChooseTargetEffect(discardValue, filter, TargetController.ANY);
-                effect.setTargetPointer(new FixedTarget(targetPlayer.getId()));
-                effect.apply(game, source);
-            }
-            return true;
+        if (controller == null || targetPlayer == null) {
+            return false;
         }
-        return false;
+        Cards cardsInHand = controller.getHand().copy();
+        TargetCard target = new TargetDiscard(
+                0, Integer.MAX_VALUE, StaticFilters.FILTER_CARD_CARDS, controller.getId()
+        );
+        controller.chooseTarget(Outcome.AIDontUseIt, cardsInHand, target, source, game);
+        int discardCount = controller.discard(new CardsImpl(target.getTargets()), source, game).size();
+        FilterCard filter = new FilterCard((discardCount > 1 ? "" : "a")
+                + " nonland card" + (discardCount > 1 ? "s" : ""));
+        filter.add(Predicates.not(CardType.LAND.getPredicate()));
+        Effect effect = new DiscardCardYouChooseTargetEffect(StaticValue.get(discardCount), filter, TargetController.ANY);
+        effect.setTargetPointer(new FixedTarget(targetPlayer.getId()));
+        effect.apply(game, source);
+        return true;
     }
 }
diff --git a/Mage.Sets/src/mage/cards/l/LeaveChance.java b/Mage.Sets/src/mage/cards/l/LeaveChance.java
index 4298a54540..00c6923f17 100644
--- a/Mage.Sets/src/mage/cards/l/LeaveChance.java
+++ b/Mage.Sets/src/mage/cards/l/LeaveChance.java
@@ -1,39 +1,41 @@
-
 package mage.cards.l;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.ReturnToHandTargetEffect;
 import mage.abilities.keyword.AftermathAbility;
-import mage.cards.Card;
 import mage.cards.CardSetInfo;
-import mage.cards.Cards;
+import mage.cards.CardsImpl;
 import mage.cards.SplitCard;
 import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.constants.SpellAbilityType;
 import mage.constants.TargetController;
-import mage.filter.FilterCard;
 import mage.filter.FilterPermanent;
+import mage.filter.StaticFilters;
 import mage.game.Game;
 import mage.players.Player;
 import mage.target.TargetCard;
 import mage.target.TargetPermanent;
-import mage.target.common.TargetCardInHand;
+import mage.target.common.TargetDiscard;
+
+import java.util.UUID;
 
 /**
- *
  * @author LevelX2
  */
 public final class LeaveChance extends SplitCard {
 
+    private static final FilterPermanent filter = new FilterPermanent("permanents you own");
+
+    static {
+        filter.add(TargetController.YOU.getOwnerPredicate());
+    }
+
     public LeaveChance(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, new CardType[]{CardType.SORCERY}, "{1}{W}", "{3}{R}", SpellAbilityType.SPLIT_AFTERMATH);
 
         // Return any number of target permanents you own to your hand.
-        FilterPermanent filter = new FilterPermanent("permanents you own");
-        filter.add(TargetController.YOU.getOwnerPredicate());
         getLeftHalfCard().getSpellAbility().addEffect(new ReturnToHandTargetEffect());
         getLeftHalfCard().getSpellAbility().addTarget(new TargetPermanent(0, Integer.MAX_VALUE, filter, false));
 
@@ -41,12 +43,12 @@ public final class LeaveChance extends SplitCard {
         // Sorcery
         // Aftermath
         getRightHalfCard().addAbility(new AftermathAbility().setRuleAtTheTop(true));
+
         // Discard any number of cards, then draw that many cards.
         getRightHalfCard().getSpellAbility().addEffect(new ChanceEffect());
-
     }
 
-    public LeaveChance(final LeaveChance card) {
+    private LeaveChance(final LeaveChance card) {
         super(card);
     }
 
@@ -63,7 +65,7 @@ class ChanceEffect extends OneShotEffect {
         this.staticText = "Discard any number of cards, then draw that many cards";
     }
 
-    ChanceEffect(final ChanceEffect effect) {
+    private ChanceEffect(final ChanceEffect effect) {
         super(effect);
     }
 
@@ -75,22 +77,13 @@ class ChanceEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player controller = game.getPlayer(source.getControllerId());
-        if (controller != null) {
-            Cards cardsInHand = controller.getHand().copy();
-            TargetCard target = new TargetCardInHand(0, cardsInHand.size(), new FilterCard());
-            controller.chooseTarget(outcome, cardsInHand, target, source, game);
-            if (!target.getTargets().isEmpty()) {
-                for (UUID cardId : target.getTargets()) {
-                    Card card = game.getCard(cardId);
-                    if (card != null) {
-                        controller.discard(card, source, game);
-                    }
-                }
-                game.applyEffects();
-                controller.drawCards(target.getTargets().size(), source.getSourceId(), game);
-            }
-            return true;
+        if (controller == null) {
+            return false;
         }
-        return false;
+        TargetCard target = new TargetDiscard(0, controller.getHand().size(), StaticFilters.FILTER_CARD_CARDS, controller.getId());
+        controller.chooseTarget(outcome, controller.getHand(), target, source, game);
+        int amount = controller.discard(new CardsImpl(target.getTargets()), source, game).size();
+        controller.drawCards(amount, source.getSourceId(), game);
+        return true;
     }
 }
diff --git a/Mage.Sets/src/mage/cards/m/MagusOfTheBalance.java b/Mage.Sets/src/mage/cards/m/MagusOfTheBalance.java
index b5b386a559..bea1648029 100644
--- a/Mage.Sets/src/mage/cards/m/MagusOfTheBalance.java
+++ b/Mage.Sets/src/mage/cards/m/MagusOfTheBalance.java
@@ -6,22 +6,12 @@ import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.common.SacrificeSourceCost;
 import mage.abilities.costs.common.TapSourceCost;
 import mage.abilities.costs.mana.ManaCostsImpl;
-import mage.abilities.effects.OneShotEffect;
-import mage.cards.*;
+import mage.abilities.effects.common.BalanceEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.Outcome;
 import mage.constants.SubType;
-import mage.filter.FilterCard;
-import mage.filter.common.FilterControlledCreaturePermanent;
-import mage.filter.common.FilterControlledLandPermanent;
-import mage.game.Game;
-import mage.game.permanent.Permanent;
-import mage.players.Player;
-import mage.target.common.TargetCardInHand;
-import mage.target.common.TargetControlledPermanent;
 
-import java.util.HashMap;
-import java.util.Map;
 import java.util.UUID;
 
 /**
@@ -39,7 +29,7 @@ public final class MagusOfTheBalance extends CardImpl {
 
         // {4}{W}, {T}, Sacrifice Magus of the Balance: Each player chooses a number of lands they control equal to the number of lands controlled by the player who controls the fewest, then sacrifices the rest. Players discard cards and sacrifice creatures the same way.
         Ability ability = new SimpleActivatedAbility(
-                new MagusOfTheBalanceEffect(),
+                new BalanceEffect(),
                 new ManaCostsImpl("{4}{W}")
         );
         ability.addCost(new TapSourceCost());
@@ -47,7 +37,7 @@ public final class MagusOfTheBalance extends CardImpl {
         this.addAbility(ability);
     }
 
-    public MagusOfTheBalance(final MagusOfTheBalance card) {
+    private MagusOfTheBalance(final MagusOfTheBalance card) {
         super(card);
     }
 
@@ -56,139 +46,3 @@ public final class MagusOfTheBalance extends CardImpl {
         return new MagusOfTheBalance(this);
     }
 }
-
-class MagusOfTheBalanceEffect extends OneShotEffect {
-
-    MagusOfTheBalanceEffect() {
-        super(Outcome.Sacrifice);
-        staticText = "each player chooses a number of lands they control "
-                + "equal to the number of lands controlled by the player "
-                + "who controls the fewest, then sacrifices the rest. "
-                + "Players discard cards and sacrifice creatures the same way";
-    }
-
-    MagusOfTheBalanceEffect(final MagusOfTheBalanceEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public MagusOfTheBalanceEffect copy() {
-        return new MagusOfTheBalanceEffect(this);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        Player controller = game.getPlayer(source.getControllerId());
-        if (controller != null) {
-            //Lands
-            int minLand = Integer.MAX_VALUE;
-            Cards landsToSacrifice = new CardsImpl();
-            for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
-                Player player = game.getPlayer(playerId);
-                if (player != null) {
-                    int count = game.getBattlefield().countAll(new FilterControlledLandPermanent(), player.getId(), game);
-                    if (count < minLand) {
-                        minLand = count;
-                    }
-                }
-            }
-
-            for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
-                Player player = game.getPlayer(playerId);
-                if (player != null) {
-                    TargetControlledPermanent target = new TargetControlledPermanent(minLand, minLand, new FilterControlledLandPermanent("lands to keep"), true);
-                    if (target.choose(Outcome.Sacrifice, player.getId(), source.getSourceId(), game)) {
-                        for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterControlledLandPermanent(), player.getId(), source.getSourceId(), game)) {
-                            if (permanent != null && !target.getTargets().contains(permanent.getId())) {
-                                landsToSacrifice.add(permanent);
-                            }
-                        }
-                    }
-                }
-            }
-
-            for (UUID cardId : landsToSacrifice) {
-                Permanent permanent = game.getPermanent(cardId);
-                if (permanent != null) {
-                    permanent.sacrifice(source.getSourceId(), game);
-                }
-            }
-
-            //Creatures
-            int minCreature = Integer.MAX_VALUE;
-            Cards creaturesToSacrifice = new CardsImpl();
-            for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
-                Player player = game.getPlayer(playerId);
-                if (player != null) {
-                    int count = game.getBattlefield().countAll(new FilterControlledCreaturePermanent(), player.getId(), game);
-                    if (count < minCreature) {
-                        minCreature = count;
-                    }
-                }
-            }
-
-            for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
-                Player player = game.getPlayer(playerId);
-                if (player != null) {
-                    TargetControlledPermanent target = new TargetControlledPermanent(minCreature, minCreature, new FilterControlledCreaturePermanent("creatures to keep"), true);
-                    if (target.choose(Outcome.Sacrifice, player.getId(), source.getSourceId(), game)) {
-                        for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterControlledCreaturePermanent(), player.getId(), source.getSourceId(), game)) {
-                            if (permanent != null && !target.getTargets().contains(permanent.getId())) {
-                                creaturesToSacrifice.add(permanent);
-                            }
-                        }
-                    }
-                }
-            }
-
-            for (UUID cardId : creaturesToSacrifice) {
-                Permanent permanent = game.getPermanent(cardId);
-                if (permanent != null) {
-                    permanent.sacrifice(source.getSourceId(), game);
-                }
-            }
-
-            //Cards in hand
-            int minCard = Integer.MAX_VALUE;
-            Map<UUID, Cards> cardsToDiscard = new HashMap<>(2);
-            for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
-                Player player = game.getPlayer(playerId);
-                if (player != null) {
-                    int count = player.getHand().size();
-                    if (count < minCard) {
-                        minCard = count;
-                    }
-                }
-            }
-
-            for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
-                Player player = game.getPlayer(playerId);
-                if (player != null) {
-                    Cards cards = new CardsImpl();
-                    TargetCardInHand target = new TargetCardInHand(minCard, new FilterCard("cards to keep"));
-                    if (target.choose(Outcome.Discard, player.getId(), source.getSourceId(), game)) {
-                        for (Card card : player.getHand().getCards(game)) {
-                            if (card != null && !target.getTargets().contains(card.getId())) {
-                                cards.add(card);
-                            }
-                        }
-                        cardsToDiscard.put(playerId, cards);
-                    }
-                }
-            }
-
-            for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
-                Player player = game.getPlayer(playerId);
-                if (player != null && cardsToDiscard.get(playerId) != null) {
-                    for (UUID cardId : cardsToDiscard.get(playerId)) {
-                        Card card = game.getCard(cardId);
-                        player.discard(card, source, game);
-
-                    }
-                }
-            }
-            return true;
-        }
-        return false;
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/m/MinamosMeddling.java b/Mage.Sets/src/mage/cards/m/MinamosMeddling.java
index bed01234fd..11887b5046 100644
--- a/Mage.Sets/src/mage/cards/m/MinamosMeddling.java
+++ b/Mage.Sets/src/mage/cards/m/MinamosMeddling.java
@@ -1,7 +1,5 @@
-
 package mage.cards.m;
 
-import java.util.UUID;
 import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.SpellAbility;
@@ -10,29 +8,27 @@ import mage.cards.*;
 import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.constants.SpellAbilityType;
-import mage.filter.StaticFilters;
 import mage.game.Game;
 import mage.game.stack.Spell;
 import mage.players.Player;
 import mage.target.TargetSpell;
 
+import java.util.UUID;
 
 /**
- *
  * @author LevelX2
  */
 public final class MinamosMeddling extends CardImpl {
 
     public MinamosMeddling(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{U}{U}");
-
+        super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}{U}");
 
         // Counter target spell. That spell's controller reveals their hand, then discards each card with the same name as a card spliced onto that spell.
-        this.getSpellAbility().addTarget(new TargetSpell(StaticFilters.FILTER_SPELL));
         this.getSpellAbility().addEffect(new MinamosMeddlingCounterTargetEffect());
+        this.getSpellAbility().addTarget(new TargetSpell());
     }
 
-    public MinamosMeddling(final MinamosMeddling card) {
+    private MinamosMeddling(final MinamosMeddling card) {
         super(card);
     }
 
@@ -44,12 +40,12 @@ public final class MinamosMeddling extends CardImpl {
 
 class MinamosMeddlingCounterTargetEffect extends OneShotEffect {
 
-    public MinamosMeddlingCounterTargetEffect() {
+    MinamosMeddlingCounterTargetEffect() {
         super(Outcome.Benefit);
         staticText = "Counter target spell. That spell's controller reveals their hand, then discards each card with the same name as a card spliced onto that spell";
     }
 
-    public MinamosMeddlingCounterTargetEffect(final MinamosMeddlingCounterTargetEffect effect) {
+    private MinamosMeddlingCounterTargetEffect(final MinamosMeddlingCounterTargetEffect effect) {
         super(effect);
     }
 
@@ -61,36 +57,34 @@ class MinamosMeddlingCounterTargetEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         MageObject sourceObject = game.getObject(source.getSourceId());
-        if (sourceObject != null) {
-            for (UUID targetId : getTargetPointer().getTargets(game, source) ) {
-                Spell spell = game.getStack().getSpell(targetId);
-                if (spell != null) {
-                    game.getStack().counter(targetId, source.getSourceId(), game);
-                    Player spellController = game.getPlayer(spell.getControllerId());
-                    if (spellController != null) {
-                        spellController.revealCards(sourceObject.getName(), spellController.getHand(), game);
-                        Cards cardsToDiscard = new CardsImpl();
-                        for (SpellAbility spellAbility : spell.getSpellAbilities()) {
-                            if (spellAbility.getSpellAbilityType() == SpellAbilityType.SPLICE) {
-                                for (Card card: spellController.getHand().getCards(game)) {
-                                    if (card.getName().equals(spellAbility.getCardName())) {
-                                        cardsToDiscard.add(card);
-                                    }
-                                }
-                            }
-                        }
-                        if (!cardsToDiscard.isEmpty()) {
-                            for (Card card :cardsToDiscard.getCards(game)) {
-                                spellController.discard(card, source, game);
-                            }
+        if (sourceObject == null) {
+            return false;
+        }
+        for (UUID targetId : getTargetPointer().getTargets(game, source)) {
+            Spell spell = game.getStack().getSpell(targetId);
+            if (spell == null) {
+                continue;
+            }
+            game.getStack().counter(targetId, source.getSourceId(), game);
+            Player spellController = game.getPlayer(spell.getControllerId());
+            if (spellController == null) {
+                continue;
+            }
+            spellController.revealCards(sourceObject.getName(), spellController.getHand(), game);
+            Cards cardsToDiscard = new CardsImpl();
+            for (SpellAbility spellAbility : spell.getSpellAbilities()) {
+                if (spellAbility.getSpellAbilityType() == SpellAbilityType.SPLICE) {
+                    for (Card card : spellController.getHand().getCards(game)) {
+                        if (card.getName().equals(spellAbility.getCardName())) {
+                            cardsToDiscard.add(card);
                         }
                     }
-
                 }
             }
-            return true;
+            if (!cardsToDiscard.isEmpty()) {
+                spellController.discard(cardsToDiscard, source, game);
+            }
         }
-        return false;
+        return true;
     }
-
 }
diff --git a/Mage.Sets/src/mage/cards/m/MindBomb.java b/Mage.Sets/src/mage/cards/m/MindBomb.java
index 16074428a2..4fa1119c70 100644
--- a/Mage.Sets/src/mage/cards/m/MindBomb.java
+++ b/Mage.Sets/src/mage/cards/m/MindBomb.java
@@ -3,7 +3,10 @@ package mage.cards.m;
 import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
-import mage.cards.*;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.cards.Cards;
+import mage.cards.CardsImpl;
 import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.filter.FilterCard;
@@ -28,7 +31,7 @@ public final class MindBomb extends CardImpl {
         this.getSpellAbility().addEffect(new MindBombEffect());
     }
 
-    public MindBomb(final MindBomb card) {
+    private MindBomb(final MindBomb card) {
         super(card);
     }
 
@@ -40,13 +43,13 @@ public final class MindBomb extends CardImpl {
 
 class MindBombEffect extends OneShotEffect {
 
-    public MindBombEffect() {
+    MindBombEffect() {
         super(Outcome.Neutral);
         this.staticText = "Each player may discard up to three cards."
                 + " {this} deals damage to each player equal to 3 minus the number of cards they discarded this way";
     }
 
-    public MindBombEffect(final MindBombEffect effect) {
+    private MindBombEffect(final MindBombEffect effect) {
         super(effect);
     }
 
@@ -59,48 +62,44 @@ class MindBombEffect extends OneShotEffect {
     public boolean apply(Game game, Ability source) {
         Player controller = game.getPlayer(source.getControllerId());
         MageObject sourceObject = source.getSourceObject(game);
-        if (controller != null && sourceObject != null) {
-            Map<UUID, Cards> cardsToDiscard = new HashMap<>();
-
-            // choose
-            for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
-                Player player = game.getPlayer(playerId);
-                if (player != null) {
-                    Cards cards = new CardsImpl();
-                    Target target = new TargetDiscard(0, 3, new FilterCard(), playerId);
-                    player.chooseTarget(outcome, target, source, game);
-                    cards.addAll(target.getTargets());
-                    cardsToDiscard.put(playerId, cards);
-                }
-            }
-
-            // discard
-            for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
-                Player player = game.getPlayer(playerId);
-                if (player != null) {
-                    Cards cardsPlayer = cardsToDiscard.get(playerId);
-                    if (cardsPlayer != null) {
-                        for (UUID cardId : cardsPlayer) {
-                            Card card = game.getCard(cardId);
-                            player.discard(card, source, game);
-
-                        }
-                    }
-                }
-            }
-
-            // damage
-            for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
-                Player player = game.getPlayer(playerId);
-                if (player != null) {
-                    Cards cardsPlayer = cardsToDiscard.get(playerId);
-                    if (cardsPlayer != null) {
-                        player.damage(3 - cardsPlayer.size(), source.getId(), game);
-                    }
-                }
-            }
-            return true;
+        if (controller == null || sourceObject == null) {
+            return false;
         }
-        return false;
+        Map<UUID, Cards> cardsToDiscard = new HashMap<>();
+
+        // choose
+        for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
+            Player player = game.getPlayer(playerId);
+            if (player == null) {
+                continue;
+            }
+            Cards cards = new CardsImpl();
+            Target target = new TargetDiscard(0, 3, new FilterCard(), playerId);
+            player.chooseTarget(Outcome.Discard, target, source, game);
+            cards.addAll(target.getTargets());
+            cardsToDiscard.put(playerId, cards);
+        }
+
+        // discard
+        for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
+            Player player = game.getPlayer(playerId);
+            if (player != null) {
+                Cards cardsPlayer = cardsToDiscard.get(playerId);
+                cardsToDiscard.put(playerId, player.discard(cardsPlayer, source, game));
+            }
+        }
+
+        // damage
+        for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
+            Player player = game.getPlayer(playerId);
+            if (player == null) {
+                continue;
+            }
+            Cards cardsPlayer = cardsToDiscard.get(playerId);
+            if (cardsPlayer != null) {
+                player.damage(3 - cardsPlayer.size(), source.getId(), game);
+            }
+        }
+        return true;
     }
 }
diff --git a/Mage.Sets/src/mage/cards/m/MindExtraction.java b/Mage.Sets/src/mage/cards/m/MindExtraction.java
index 485c0fa380..7a37732146 100644
--- a/Mage.Sets/src/mage/cards/m/MindExtraction.java
+++ b/Mage.Sets/src/mage/cards/m/MindExtraction.java
@@ -99,9 +99,7 @@ class MindExtractionEffect extends OneShotEffect {
                 .stream()
                 .filter(card -> card.getColor(game).shares(color))
                 .forEach(toDiscard::add);
-        toDiscard.getCards(game)
-                .stream()
-                .forEach(card -> player.discard(card, source, game));
+        player.discard(toDiscard, source, game);
         return true;
     }
 }
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/m/MindMaggots.java b/Mage.Sets/src/mage/cards/m/MindMaggots.java
index e98e414ce8..c68088cb88 100644
--- a/Mage.Sets/src/mage/cards/m/MindMaggots.java
+++ b/Mage.Sets/src/mage/cards/m/MindMaggots.java
@@ -1,25 +1,26 @@
 package mage.cards.m;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
-import mage.abilities.effects.common.counter.AddCountersSourceEffect;
-import mage.cards.Card;
-import mage.constants.SubType;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
+import mage.cards.CardsImpl;
 import mage.constants.CardType;
 import mage.constants.Outcome;
+import mage.constants.SubType;
 import mage.counters.CounterType;
 import mage.filter.StaticFilters;
 import mage.game.Game;
+import mage.game.permanent.Permanent;
 import mage.players.Player;
-import mage.target.common.TargetCardInHand;
+import mage.target.TargetCard;
+import mage.target.common.TargetDiscard;
+
+import java.util.UUID;
 
 /**
- *
  * @author jeffwadsworth
  */
 public final class MindMaggots extends CardImpl {
@@ -34,10 +35,9 @@ public final class MindMaggots extends CardImpl {
         // When Mind Maggots enters the battlefield, discard any number of creature cards.
         // For each card discarded this way, put two +1/+1 counters on Mind Maggots.
         this.addAbility(new EntersBattlefieldTriggeredAbility(new MindMaggotsEffect()));
-
     }
 
-    public MindMaggots(final MindMaggots card) {
+    private MindMaggots(final MindMaggots card) {
         super(card);
     }
 
@@ -51,10 +51,11 @@ class MindMaggotsEffect extends OneShotEffect {
 
     MindMaggotsEffect() {
         super(Outcome.BoostCreature);
-        this.staticText = "discard any number of creature cards. For each card discarded this way, put two +1/+1 counters on {this}";
+        this.staticText = "discard any number of creature cards. " +
+                "For each card discarded this way, put two +1/+1 counters on {this}";
     }
 
-    MindMaggotsEffect(final MindMaggotsEffect effect) {
+    private MindMaggotsEffect(final MindMaggotsEffect effect) {
         super(effect);
     }
 
@@ -66,23 +67,17 @@ class MindMaggotsEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player controller = game.getPlayer(source.getControllerId());
-        if (controller != null) {
-            int numToDiscard = controller.getAmount(0,
-                    controller.getHand().getCards(StaticFilters.FILTER_CARD_CREATURE, game).size(),
-                    "Discard how many creature cards?",
-                    game);
-            TargetCardInHand target = new TargetCardInHand(numToDiscard, StaticFilters.FILTER_CARD_CREATURE);
-            if (controller.choose(Outcome.Benefit, target, source.getSourceId(), game)) {
-                for (UUID targetId : target.getTargets()) {
-                    Card card = game.getCard(targetId);
-                    if (card != null
-                            && controller.discard(card, source, game)) {
-                        new AddCountersSourceEffect(CounterType.P1P1.createInstance(2)).apply(game, source);
-                    }
-                }
-            }
+        if (controller == null) {
+            return false;
+        }
+        TargetCard target = new TargetDiscard(0, Integer.MAX_VALUE, StaticFilters.FILTER_CARD_CREATURE, controller.getId());
+        controller.choose(outcome, controller.getHand(), target, game);
+        int counters = controller.discard(new CardsImpl(target.getTargets()), source, game).size();
+        Permanent permanent = game.getPermanent(source.getSourceId());
+        if (permanent == null || counters < 1) {
             return true;
         }
-        return false;
+        permanent.addCounters(CounterType.P1P1.createInstance(counters), source, game);
+        return true;
     }
 }
diff --git a/Mage.Sets/src/mage/cards/m/MindWarp.java b/Mage.Sets/src/mage/cards/m/MindWarp.java
index afef657aeb..c7fd19c858 100644
--- a/Mage.Sets/src/mage/cards/m/MindWarp.java
+++ b/Mage.Sets/src/mage/cards/m/MindWarp.java
@@ -1,21 +1,18 @@
-
 package mage.cards.m;
 
 import mage.abilities.Ability;
-import mage.abilities.dynamicvalue.DynamicValue;
-import mage.abilities.dynamicvalue.common.ManacostVariableValue;
 import mage.abilities.effects.OneShotEffect;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
+import mage.cards.CardsImpl;
 import mage.constants.CardType;
 import mage.constants.Outcome;
-import mage.constants.Zone;
-import mage.filter.FilterCard;
+import mage.filter.StaticFilters;
 import mage.game.Game;
 import mage.players.Player;
 import mage.target.TargetCard;
 import mage.target.TargetPlayer;
+import mage.target.common.TargetCardInHand;
 
 import java.util.UUID;
 
@@ -28,11 +25,11 @@ public final class MindWarp extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{3}{B}");
 
         // Look at target player's hand and choose X cards from it. That player discards those cards.
-        this.getSpellAbility().addEffect(new MindWarpEffect(ManacostVariableValue.instance));
+        this.getSpellAbility().addEffect(new MindWarpEffect());
         this.getSpellAbility().addTarget(new TargetPlayer());
     }
 
-    public MindWarp(final MindWarp card) {
+    private MindWarp(final MindWarp card) {
         super(card);
     }
 
@@ -44,39 +41,27 @@ public final class MindWarp extends CardImpl {
 
 class MindWarpEffect extends OneShotEffect {
 
-    private final DynamicValue xValue;
-
-    public MindWarpEffect(DynamicValue xValue) {
+    MindWarpEffect() {
         super(Outcome.Discard);
-        this.xValue = xValue;
         staticText = "Look at target player's hand and choose X cards from it. That player discards those card.";
     }
 
-    public MindWarpEffect(final MindWarpEffect effect) {
+    private MindWarpEffect(final MindWarpEffect effect) {
         super(effect);
-        this.xValue = effect.xValue;
     }
 
     @Override
     public boolean apply(Game game, Ability source) {
         Player targetPlayer = game.getPlayer(source.getFirstTarget());
         Player you = game.getPlayer(source.getControllerId());
-        if (targetPlayer != null && you != null) {
-            int amountToDiscard = Math.min(
-                    xValue.calculate(game, source, this),
-                    targetPlayer.getHand().size()
-            );
-            you.lookAtCards("Discard", targetPlayer.getHand(), game);
-            TargetCard target = new TargetCard(amountToDiscard, Zone.HAND, new FilterCard());
-            target.setNotTarget(true);
-            if (you.choose(Outcome.Benefit, targetPlayer.getHand(), target, game)) {
-                Card card = targetPlayer.getHand().get(target.getFirstTarget(), game);
-                return targetPlayer.discard(card, source, game);
-
-            }
-
+        if (targetPlayer == null || you == null) {
+            return false;
         }
-        return false;
+        int amountToDiscard = source.getManaCostsToPay().getX();
+        TargetCard target = new TargetCardInHand(amountToDiscard, StaticFilters.FILTER_CARD_CARDS);
+        you.choose(outcome, targetPlayer.getHand(), target, game);
+        targetPlayer.discard(new CardsImpl(target.getTargets()), source, game);
+        return true;
     }
 
     @Override
diff --git a/Mage.Sets/src/mage/cards/m/Monomania.java b/Mage.Sets/src/mage/cards/m/Monomania.java
index 8e27d39857..86f4ac069f 100644
--- a/Mage.Sets/src/mage/cards/m/Monomania.java
+++ b/Mage.Sets/src/mage/cards/m/Monomania.java
@@ -1,37 +1,35 @@
-
 package mage.cards.m;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
+import mage.cards.Cards;
 import mage.constants.CardType;
 import mage.constants.Outcome;
-import mage.constants.Zone;
 import mage.filter.FilterCard;
 import mage.game.Game;
 import mage.players.Player;
 import mage.target.TargetCard;
 import mage.target.TargetPlayer;
+import mage.target.common.TargetDiscard;
+
+import java.util.UUID;
 
 /**
- *
  * @author nantuko
  */
 public final class Monomania extends CardImpl {
 
     public Monomania(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{B}{B}");
-
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{B}{B}");
 
         // Target player chooses a card in their hand and discards the rest.
         this.getSpellAbility().addEffect(new MonomaniaEffect());
         this.getSpellAbility().addTarget(new TargetPlayer());
     }
 
-    public Monomania(final Monomania card) {
+    private Monomania(final Monomania card) {
         super(card);
     }
 
@@ -45,41 +43,30 @@ class MonomaniaEffect extends OneShotEffect {
 
     private static final FilterCard filter = new FilterCard("a card");
 
-    public MonomaniaEffect() {
+    MonomaniaEffect() {
         super(Outcome.Discard);
         staticText = "Target player chooses a card in their hand and discards the rest";
     }
 
-    public MonomaniaEffect(final MonomaniaEffect effect) {
+    private MonomaniaEffect(final MonomaniaEffect effect) {
         super(effect);
     }
 
     @Override
     public boolean apply(Game game, Ability source) {
         Player player = game.getPlayer(source.getFirstTarget());
-        if (player != null) {
-            TargetCard target = new TargetCard(Zone.HAND, filter);
-            if (player.choose(Outcome.Detriment, player.getHand(), target, game)) {
-                while (player.getHand().size() > 1) {
-                    for (UUID uuid : player.getHand()) {
-                        if (!uuid.equals(target.getFirstTarget())) {
-                            Card card = player.getHand().get(uuid, game);
-                            if (card != null) {
-                                player.discard(card, source, game);
-                                break;
-                            }
-                        }
-                    }
-                }
-                return true;
-            }
+        if (player == null || player.getHand().isEmpty()) {
+            return false;
         }
-        return false;
+        TargetCard target = new TargetDiscard(player.getId());
+        player.choose(Outcome.Benefit, player.getHand(), target, game);
+        Cards cards = player.getHand().copy();
+        cards.removeIf(target.getTargets()::contains);
+        return !player.discard(cards, source, game).isEmpty();
     }
 
     @Override
     public MonomaniaEffect copy() {
         return new MonomaniaEffect(this);
     }
-
 }
diff --git a/Mage.Sets/src/mage/cards/m/MyojinOfNightsReach.java b/Mage.Sets/src/mage/cards/m/MyojinOfNightsReach.java
index 9f6f424fd3..559c409e51 100644
--- a/Mage.Sets/src/mage/cards/m/MyojinOfNightsReach.java
+++ b/Mage.Sets/src/mage/cards/m/MyojinOfNightsReach.java
@@ -1,7 +1,5 @@
-
 package mage.cards.m;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldAbility;
@@ -16,7 +14,6 @@ import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
 import mage.abilities.effects.common.counter.AddCountersSourceEffect;
 import mage.abilities.keyword.IndestructibleAbility;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.*;
@@ -25,13 +22,15 @@ import mage.game.Game;
 import mage.players.Player;
 import mage.watchers.common.CastFromHandWatcher;
 
+import java.util.UUID;
+
 /**
  * @author LevelX
  */
 public final class MyojinOfNightsReach extends CardImpl {
 
     public MyojinOfNightsReach(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{5}{B}{B}{B}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{B}{B}{B}");
         addSuperType(SuperType.LEGENDARY);
         this.subtype.add(SubType.SPIRIT);
 
@@ -50,7 +49,7 @@ public final class MyojinOfNightsReach extends CardImpl {
         this.addAbility(ability);
     }
 
-    public MyojinOfNightsReach(final MyojinOfNightsReach card) {
+    private MyojinOfNightsReach(final MyojinOfNightsReach card) {
         super(card);
     }
 
@@ -61,12 +60,13 @@ public final class MyojinOfNightsReach extends CardImpl {
 }
 
 class MyojinOfNightsReachEffect extends OneShotEffect {
-    public MyojinOfNightsReachEffect() {
+
+    MyojinOfNightsReachEffect() {
         super(Outcome.Discard);
         staticText = "Each opponent discards their hand";
     }
 
-    public MyojinOfNightsReachEffect(final MyojinOfNightsReachEffect effect) {
+    private MyojinOfNightsReachEffect(final MyojinOfNightsReachEffect effect) {
         super(effect);
     }
 
@@ -74,10 +74,8 @@ class MyojinOfNightsReachEffect extends OneShotEffect {
     public boolean apply(Game game, Ability source) {
         for (UUID opponentId : game.getOpponents(source.getControllerId())) {
             Player opponent = game.getPlayer(opponentId);
-            if(opponent != null) {
-                for (Card c : opponent.getHand().getCards(game)) {
-                    opponent.discard(c, source, game);
-                }
+            if (opponent != null) {
+                opponent.discard(opponent.getHand(), source, game);
             }
         }
         return true;
@@ -87,5 +85,4 @@ class MyojinOfNightsReachEffect extends OneShotEffect {
     public MyojinOfNightsReachEffect copy() {
         return new MyojinOfNightsReachEffect(this);
     }
-
 }
diff --git a/Mage.Sets/src/mage/cards/n/NantukoCultivator.java b/Mage.Sets/src/mage/cards/n/NantukoCultivator.java
index f284e81d74..1372acb7b2 100644
--- a/Mage.Sets/src/mage/cards/n/NantukoCultivator.java
+++ b/Mage.Sets/src/mage/cards/n/NantukoCultivator.java
@@ -1,42 +1,41 @@
-
 package mage.cards.n;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
+import mage.cards.CardsImpl;
 import mage.constants.CardType;
-import mage.constants.SubType;
 import mage.constants.Outcome;
+import mage.constants.SubType;
 import mage.counters.CounterType;
-import mage.filter.common.FilterLandCard;
+import mage.filter.StaticFilters;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
 import mage.players.Player;
 import mage.target.common.TargetCardInHand;
 
-/**
- *
- * @author LoneFox
+import java.util.UUID;
 
+/**
+ * @author LoneFox
  */
 public final class NantukoCultivator extends CardImpl {
 
     public NantukoCultivator(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{G}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}");
         this.subtype.add(SubType.INSECT);
         this.subtype.add(SubType.DRUID);
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
 
         // When Nantuko Cultivator enters the battlefield, you may discard any number of land cards. Put that many +1/+1 counters on Nantuko Cultivator and draw that many cards.
-        this.addAbility(new EntersBattlefieldTriggeredAbility(new NantukoCultivatorEffect(), true));
+        this.addAbility(new EntersBattlefieldTriggeredAbility(new NantukoCultivatorEffect()));
     }
 
-    public NantukoCultivator(final NantukoCultivator card) {
+    private NantukoCultivator(final NantukoCultivator card) {
         super(card);
     }
 
@@ -48,40 +47,38 @@ public final class NantukoCultivator extends CardImpl {
 
 class NantukoCultivatorEffect extends OneShotEffect {
 
-    public NantukoCultivatorEffect() {
+    NantukoCultivatorEffect() {
         super(Outcome.BoostCreature);
-        staticText  = "you may discard any number of land cards. Put that many +1/+1 counters on {this} and draw that many cards.";
+        staticText = "you may discard any number of land cards. " +
+                "Put that many +1/+1 counters on {this} and draw that many cards.";
+    }
+
+    private NantukoCultivatorEffect(final NantukoCultivatorEffect effect) {
+        super(effect);
     }
 
     @Override
     public boolean apply(Game game, Ability source) {
         Player player = game.getPlayer(source.getControllerId());
-        if(player != null) {
-            TargetCardInHand toDiscard = new TargetCardInHand(0, Integer.MAX_VALUE, new FilterLandCard());
-            if(player.chooseTarget(Outcome.Discard, toDiscard, source, game)) {
-                int count = 0;
-                for(UUID targetId: toDiscard.getTargets()) {
-                    player.discard(game.getCard(targetId), source, game);
-                    count++;
-                }
-                Permanent permanent = game.getPermanent(source.getSourceId());
-                if(permanent != null) {
-                    permanent.addCounters(CounterType.P1P1.createInstance(count), source, game);
-                }
-                player.drawCards(count, source.getSourceId(), game);
-            }
-            return true;
+        if (player == null || player.getHand().count(StaticFilters.FILTER_CARD_LAND, game) < 1) {
+            return false;
         }
-        return false;
-    }
-
-    public NantukoCultivatorEffect(final NantukoCultivatorEffect effect) {
-        super(effect);
+        TargetCardInHand toDiscard = new TargetCardInHand(0, Integer.MAX_VALUE, StaticFilters.FILTER_CARD_LAND);
+        player.chooseTarget(Outcome.AIDontUseIt, toDiscard, source, game);
+        int count = player.discard(new CardsImpl(toDiscard.getTargets()), source, game).size();
+        if (count < 1) {
+            return false;
+        }
+        Permanent permanent = game.getPermanent(source.getSourceId());
+        if (permanent != null) {
+            permanent.addCounters(CounterType.P1P1.createInstance(count), source, game);
+        }
+        player.drawCards(count, source.getSourceId(), game);
+        return true;
     }
 
     @Override
     public NantukoCultivatorEffect copy() {
         return new NantukoCultivatorEffect(this);
     }
-
 }
diff --git a/Mage.Sets/src/mage/cards/n/Nebuchadnezzar.java b/Mage.Sets/src/mage/cards/n/Nebuchadnezzar.java
index e1ba7288f1..7eee5eec44 100644
--- a/Mage.Sets/src/mage/cards/n/Nebuchadnezzar.java
+++ b/Mage.Sets/src/mage/cards/n/Nebuchadnezzar.java
@@ -66,30 +66,28 @@ class NebuchadnezzarEffect extends OneShotEffect {
         Player opponent = game.getPlayer(targetPointer.getFirst(game, source));
         MageObject sourceObject = game.getObject(source.getSourceId());
         String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY);
-        if (opponent != null && sourceObject != null && !cardName.isEmpty()) {
-            int costX = source.getManaCostsToPay().getX();
-            if (costX > 0 && !opponent.getHand().isEmpty()) {
-                Cards cards = new CardsImpl();
-                while (costX > 0) {
-                    Card card = opponent.getHand().getRandom(game);
-                    if (!cards.contains(card.getId())) {
-                        cards.add(card);
-                        costX--;
-                    }
-                    if (opponent.getHand().size() <= cards.size()) {
-                        break;
-                    }
-                }
-                opponent.revealCards(sourceObject.getIdName(), cards, game);
-                for (Card cardToDiscard : cards.getCards(game)) {
-                    if (cardToDiscard.getName().equals(cardName)) {
-                        opponent.discard(cardToDiscard, source, game);
-                    }
-                }
-            }
+        if (opponent == null || sourceObject == null || cardName.isEmpty()) {
+            return false;
+        }
+        int costX = source.getManaCostsToPay().getX();
+        if (costX <= 0 || opponent.getHand().isEmpty()) {
             return true;
         }
-        return false;
+        Cards cards = new CardsImpl();
+        while (costX > 0) {
+            Card card = opponent.getHand().getRandom(game);
+            if (!cards.contains(card.getId())) {
+                cards.add(card);
+                costX--;
+            }
+            if (opponent.getHand().size() <= cards.size()) {
+                break;
+            }
+        }
+        opponent.revealCards(sourceObject.getIdName(), cards, game);
+        cards.removeIf(uuid -> !cardName.equals(game.getCard(uuid).getName()));
+        opponent.discard(cards, source, game);
+        return true;
     }
 
     @Override
diff --git a/Mage.Sets/src/mage/cards/n/NehebDreadhordeChampion.java b/Mage.Sets/src/mage/cards/n/NehebDreadhordeChampion.java
index 338608ef22..535dcff7a9 100644
--- a/Mage.Sets/src/mage/cards/n/NehebDreadhordeChampion.java
+++ b/Mage.Sets/src/mage/cards/n/NehebDreadhordeChampion.java
@@ -6,7 +6,9 @@ import mage.abilities.Ability;
 import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.keyword.TrampleAbility;
-import mage.cards.*;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.cards.CardsImpl;
 import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.constants.SubType;
@@ -18,6 +20,7 @@ import mage.target.TargetCard;
 import mage.target.common.TargetCardInHand;
 
 import java.util.UUID;
+import java.util.stream.IntStream;
 
 /**
  * @author TheElk801
@@ -80,18 +83,12 @@ class NehebDreadhordeChampionEffect extends OneShotEffect {
         if (!player.choose(outcome, target, source.getSourceId(), game)) {
             return false;
         }
-        Cards cards = new CardsImpl(target.getTargets());
-        int counter = 0;
+        int counter = player.discard(new CardsImpl(target.getTargets()), source, game).size();
         Mana mana = new Mana();
-        for (Card card : cards.getCards(game)) {
-            if (player.discard(card, source, game)) {
-                counter++;
-                mana.increaseRed();
-            }
-        }
-        if (counter == 0) {
+        if (counter < 1) {
             return true;
         }
+        IntStream.range(0, counter).forEach(x -> mana.increaseRed());
         player.drawCards(counter, source.getSourceId(), game);
         player.getManaPool().addMana(mana, game, source, true);
         return true;
diff --git a/Mage.Sets/src/mage/cards/n/Nightsnare.java b/Mage.Sets/src/mage/cards/n/Nightsnare.java
index a0326c9f49..e0535648e9 100644
--- a/Mage.Sets/src/mage/cards/n/Nightsnare.java
+++ b/Mage.Sets/src/mage/cards/n/Nightsnare.java
@@ -52,32 +52,27 @@ class NightsnareDiscardEffect extends OneShotEffect {
     public boolean apply(Game game, Ability source) {
         Player player = game.getPlayer(targetPointer.getFirst(game, source));
         Player controller = game.getPlayer(source.getControllerId());
-        if (player != null && controller != null) {
-            if (!player.getHand().isEmpty()) {
-                Cards revealedCards = new CardsImpl();
-                revealedCards.addAll(player.getHand());
-                Card sourceCard = game.getCard(source.getSourceId());
-                player.revealCards(sourceCard != null ? sourceCard.getIdName() : "Discard", revealedCards, game);
-                // You may choose a nonland card from it.
-                if (controller.chooseUse(outcome, "Choose a a card to discard? (Otherwise " + player.getLogName() + " has to discard 2 cards).", source, game)) {
-                    TargetCard target = new TargetCard(1, Zone.HAND, new FilterNonlandCard());
-                    if (controller.choose(Outcome.Benefit, revealedCards, target, game)) {
-                        for (UUID targetId : target.getTargets()) {
-                            Card card = revealedCards.get(targetId, game);
-                            player.discard(card, source, game);
-
-                        }
-                    }
-
-                } else {
-                    player.discard(2, false, source, game);
-                }
-            }
-            return true;
-
+        if (player == null || controller == null) {
+            return false;
         }
-        return false;
-
+        if (player.getHand().isEmpty()) {
+            return true;
+        }
+        Cards revealedCards = new CardsImpl();
+        revealedCards.addAll(player.getHand());
+        Card sourceCard = game.getCard(source.getSourceId());
+        player.revealCards(sourceCard != null ? sourceCard.getIdName() : "Discard", revealedCards, game);
+        // You may choose a nonland card from it.
+        if (!controller.chooseUse(outcome, "Choose a card to discard? (Otherwise " + player.getLogName() + " has to discard 2 cards).", source, game)) {
+            player.discard(2, false, source, game);
+            return true;
+        }
+        TargetCard target = new TargetCard(1, Zone.HAND, new FilterNonlandCard());
+        if (controller.choose(Outcome.Benefit, revealedCards, target, game)) {
+            Card card = revealedCards.get(target.getFirstTarget(), game);
+            player.discard(card, source, game);
+        }
+        return true;
     }
 
     @Override
diff --git a/Mage.Sets/src/mage/cards/n/NogginWhack.java b/Mage.Sets/src/mage/cards/n/NogginWhack.java
index 5aa88225c4..3eabce33e6 100644
--- a/Mage.Sets/src/mage/cards/n/NogginWhack.java
+++ b/Mage.Sets/src/mage/cards/n/NogginWhack.java
@@ -1,19 +1,12 @@
-
 package mage.cards.n;
 
-import java.util.List;
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.keyword.ProwlAbility;
-import mage.cards.Card;
-import mage.cards.CardImpl;
-import mage.cards.CardSetInfo;
-import mage.cards.Cards;
-import mage.cards.CardsImpl;
+import mage.cards.*;
 import mage.constants.CardType;
-import mage.constants.SubType;
 import mage.constants.Outcome;
+import mage.constants.SubType;
 import mage.constants.Zone;
 import mage.filter.FilterCard;
 import mage.game.Game;
@@ -21,14 +14,16 @@ import mage.players.Player;
 import mage.target.TargetCard;
 import mage.target.TargetPlayer;
 
+import java.util.List;
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class NogginWhack extends CardImpl {
 
     public NogginWhack(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.TRIBAL,CardType.SORCERY},"{2}{B}{B}");
+        super(ownerId, setInfo, new CardType[]{CardType.TRIBAL, CardType.SORCERY}, "{2}{B}{B}");
         this.subtype.add(SubType.ROGUE);
 
 
@@ -40,7 +35,7 @@ public final class NogginWhack extends CardImpl {
 
     }
 
-    public NogginWhack(final NogginWhack card) {
+    private NogginWhack(final NogginWhack card) {
         super(card);
     }
 
@@ -52,12 +47,12 @@ public final class NogginWhack extends CardImpl {
 
 class NogginWhackEffect extends OneShotEffect {
 
-    public NogginWhackEffect() {
+     NogginWhackEffect() {
         super(Outcome.Benefit);
         this.staticText = "Target player reveals three cards from their hand. You choose two of them. That player discards those cards";
     }
 
-    public NogginWhackEffect(final NogginWhackEffect effect) {
+    private NogginWhackEffect(final NogginWhackEffect effect) {
         super(effect);
     }
 
@@ -71,40 +66,35 @@ class NogginWhackEffect extends OneShotEffect {
         Player controller = game.getPlayer(source.getControllerId());
         Player targetPlayer = game.getPlayer(source.getFirstTarget());
         Card sourceCard = game.getCard(source.getSourceId());
-        if (controller != null && targetPlayer != null && sourceCard != null) {
-            Cards cardsInHand = new CardsImpl();
-            cardsInHand.addAll(targetPlayer.getHand());
-
-            int count = Math.min(cardsInHand.size(), 3);
-
-            TargetCard target = new TargetCard(count, Zone.HAND, new FilterCard());
-            Cards revealedCards = new CardsImpl();
-
-            if (targetPlayer.chooseTarget(Outcome.Discard, cardsInHand, target, source, game)) {
-                List<UUID> targets = target.getTargets();
-                for (UUID targetId : targets) {
-                    Card card = game.getCard(targetId);
-                    if (card != null) {
-                        revealedCards.add(card);
-                    }
-                }
-            }
-
-            int cardsToDiscard = Math.min(revealedCards.size(), 2);
-            TargetCard targetInHand = new TargetCard(cardsToDiscard, cardsToDiscard, Zone.HAND, new FilterCard("card to discard"));
-
-            if (!revealedCards.isEmpty()) {
-                targetPlayer.revealCards("Noggin Whack", revealedCards, game);
-                controller.chooseTarget(Outcome.Exile, revealedCards, targetInHand, source, game);
-                for (UUID cardId : targetInHand.getTargets()) {
-                    Card card = game.getCard(cardId);
-                    if (card != null) {
-                        controller.discard(card, source, game);
-                    }
-                }
-            }
-            return true;
+        if (controller == null || targetPlayer == null || sourceCard == null) {
+            return false;
         }
-        return false;
+        Cards cardsInHand = new CardsImpl();
+        cardsInHand.addAll(targetPlayer.getHand());
+
+        int count = Math.min(cardsInHand.size(), 3);
+
+        TargetCard target = new TargetCard(count, Zone.HAND, new FilterCard());
+        Cards revealedCards = new CardsImpl();
+
+        if (targetPlayer.chooseTarget(Outcome.Discard, cardsInHand, target, source, game)) {
+            List<UUID> targets = target.getTargets();
+            for (UUID targetId : targets) {
+                Card card = game.getCard(targetId);
+                if (card != null) {
+                    revealedCards.add(card);
+                }
+            }
+        }
+
+        int cardsToDiscard = Math.min(revealedCards.size(), 2);
+        TargetCard targetInHand = new TargetCard(cardsToDiscard, cardsToDiscard, Zone.HAND, new FilterCard("card to discard"));
+
+        if (!revealedCards.isEmpty()) {
+            targetPlayer.revealCards(source, revealedCards, game);
+            controller.chooseTarget(Outcome.Exile, revealedCards, targetInHand, source, game);
+            targetPlayer.discard(new CardsImpl(target.getTargets()), source, game);
+        }
+        return true;
     }
 }
diff --git a/Mage.Sets/src/mage/cards/n/NoxiousVapors.java b/Mage.Sets/src/mage/cards/n/NoxiousVapors.java
index 1e67b13521..e550c4c05e 100644
--- a/Mage.Sets/src/mage/cards/n/NoxiousVapors.java
+++ b/Mage.Sets/src/mage/cards/n/NoxiousVapors.java
@@ -1,26 +1,27 @@
-
 package mage.cards.n;
 
-import java.util.HashSet;
-import java.util.Set;
-import java.util.UUID;
 import mage.ObjectColor;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
 import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
+import mage.cards.Cards;
 import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.filter.FilterCard;
+import mage.filter.StaticFilters;
 import mage.filter.common.FilterNonlandCard;
 import mage.filter.predicate.mageobject.ColorPredicate;
 import mage.game.Game;
 import mage.players.Player;
 import mage.target.common.TargetCardInHand;
 
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
+
 /**
- *
  * @author L_J
  */
 public final class NoxiousVapors extends CardImpl {
@@ -30,10 +31,9 @@ public final class NoxiousVapors extends CardImpl {
 
         // Each player reveals their hand, chooses one card of each color from it, then discards all other nonland cards.
         this.getSpellAbility().addEffect(new NoxiousVaporsEffect());
-
     }
 
-    public NoxiousVapors(final NoxiousVapors card) {
+    private NoxiousVapors(final NoxiousVapors card) {
         super(card);
     }
 
@@ -47,12 +47,12 @@ class NoxiousVaporsEffect extends OneShotEffect {
 
     private static final FilterNonlandCard filter = new FilterNonlandCard();
 
-    public NoxiousVaporsEffect() {
+    NoxiousVaporsEffect() {
         super(Outcome.Benefit);
         this.staticText = "Each player reveals their hand, chooses one card of each color from it, then discards all other nonland cards";
     }
 
-    public NoxiousVaporsEffect(final NoxiousVaporsEffect effect) {
+    private NoxiousVaporsEffect(final NoxiousVaporsEffect effect) {
         super(effect);
     }
 
@@ -64,37 +64,35 @@ class NoxiousVaporsEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player controller = game.getPlayer(source.getControllerId());
-        if (controller != null) {
-            for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
-                Player player = game.getPlayer(playerId);
-                if (player != null) {
-                    player.revealCards(player.getName() + "'s hand", player.getHand(), game);
-                }
-            }
-            
-            for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
-                Player player = game.getPlayer(playerId);
-                if (player != null) {
-                    Set<Card> chosenCards = new HashSet<>();
-                    chooseCardForColor(ObjectColor.WHITE, chosenCards, player, game, source);
-                    chooseCardForColor(ObjectColor.BLUE, chosenCards, player, game, source);
-                    chooseCardForColor(ObjectColor.BLACK, chosenCards, player, game, source);
-                    chooseCardForColor(ObjectColor.RED, chosenCards, player, game, source);
-                    chooseCardForColor(ObjectColor.GREEN, chosenCards, player, game, source);
-                    
-                    Set<Card> cards = player.getHand().getCards(game);
-                    for (Card card : cards) {
-                        if (card != null && !chosenCards.contains(card) && filter.match(card, game)) {
-                            player.discard(card, source, game);
-                        }
-                    }
-                }
-            }
-            return true;
+        if (controller == null) {
+            return false;
         }
-        return false;
+        for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
+            Player player = game.getPlayer(playerId);
+            if (player != null) {
+                player.revealCards(player.getName() + "'s hand", player.getHand(), game);
+            }
+        }
+
+        for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
+            Player player = game.getPlayer(playerId);
+            if (player == null) {
+                continue;
+            }
+            Set<Card> chosenCards = new HashSet<>();
+            chooseCardForColor(ObjectColor.WHITE, chosenCards, player, game, source);
+            chooseCardForColor(ObjectColor.BLUE, chosenCards, player, game, source);
+            chooseCardForColor(ObjectColor.BLACK, chosenCards, player, game, source);
+            chooseCardForColor(ObjectColor.RED, chosenCards, player, game, source);
+            chooseCardForColor(ObjectColor.GREEN, chosenCards, player, game, source);
+            chosenCards.addAll(player.getHand().getCards(StaticFilters.FILTER_CARD_LAND, game));
+            Cards cards = player.getHand().copy();
+            cards.removeIf(chosenCards::contains);
+            player.discard(cards, source, game);
+        }
+        return true;
     }
-    
+
     private void chooseCardForColor(ObjectColor color, Set<Card> chosenCards, Player player, Game game, Ability source) {
         FilterCard filter = new FilterCard();
         filter.add(new ColorPredicate(color));
diff --git a/Mage.Sets/src/mage/cards/r/RareBGone.java b/Mage.Sets/src/mage/cards/r/RareBGone.java
index bb97dfceb0..5440a478f8 100644
--- a/Mage.Sets/src/mage/cards/r/RareBGone.java
+++ b/Mage.Sets/src/mage/cards/r/RareBGone.java
@@ -2,23 +2,18 @@ package mage.cards.r;
 
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
-import mage.cards.Card;
-import mage.cards.CardImpl;
-import mage.cards.CardSetInfo;
-import mage.cards.Cards;
+import mage.cards.*;
 import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.constants.Rarity;
 import mage.filter.FilterCard;
 import mage.filter.FilterPermanent;
 import mage.filter.predicate.Predicate;
-import mage.filter.predicate.Predicates;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
 import mage.players.Player;
 
 import java.util.Objects;
-import java.util.Set;
 import java.util.UUID;
 
 /**
@@ -31,10 +26,9 @@ public final class RareBGone extends CardImpl {
 
         // Each player sacrifices all permanents that are rare or mythic rare, then each player reveals their hand and discards all cards that are rare or mythic rare.
         this.getSpellAbility().addEffect(new RareBGoneEffect());
-
     }
 
-    public RareBGone(final RareBGone card) {
+    private RareBGone(final RareBGone card) {
         super(card);
     }
 
@@ -50,22 +44,17 @@ class RareBGoneEffect extends OneShotEffect {
     private static final FilterCard filterCard = new FilterCard();
 
     static {
-        filterPermanent.add(Predicates.or(
-                new RarityPredicate(Rarity.RARE),
-                new RarityPredicate(Rarity.MYTHIC)
-        ));
-        filterCard.add(Predicates.or(
-                new RarityPredicate(Rarity.RARE),
-                new RarityPredicate(Rarity.MYTHIC)
-        ));
+        filterPermanent.add(RareBGonePredicate.instance);
+        filterCard.add(RareBGonePredicate.instance);
     }
 
-    public RareBGoneEffect() {
+    RareBGoneEffect() {
         super(Outcome.Benefit);
-        this.staticText = "Each player sacrifices all permanents that are rare or mythic rare, then each player reveals their hand and discards all cards that are rare or mythic rare";
+        this.staticText = "Each player sacrifices all permanents that are rare or mythic rare, " +
+                "then each player reveals their hand and discards all cards that are rare or mythic rare";
     }
 
-    public RareBGoneEffect(final RareBGoneEffect effect) {
+    private RareBGoneEffect(final RareBGoneEffect effect) {
         super(effect);
     }
 
@@ -77,44 +66,29 @@ class RareBGoneEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player controller = game.getPlayer(source.getControllerId());
-        if (controller != null) {
-            for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
-                Player player = game.getPlayer(playerId);
-                if (player != null) {
-                    for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filterPermanent, playerId, game)) {
-                        permanent.sacrifice(source.getSourceId(), game);
-                    }
-                    Cards hand = player.getHand();
-                    player.revealCards("Rare-B-Gone", hand, game);
-                    Set<Card> cards = hand.getCards(game);
-                    for (Card card : cards) {
-                        if (card != null && filterCard.match(card, game)) {
-                            player.discard(card, source, game);
-                        }
-                    }
-                }
-            }
-            return true;
+        if (controller == null) {
+            return false;
         }
-        return false;
+        for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
+            Player player = game.getPlayer(playerId);
+            if (player == null) {
+            }
+            for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filterPermanent, playerId, game)) {
+                permanent.sacrifice(source.getSourceId(), game);
+            }
+            Cards cards = player.getHand();
+            player.revealCards(source, cards, game);
+            player.discard(new CardsImpl(cards.getCards(filterCard, game)), source, game);
+        }
+        return true;
     }
 }
 
-class RarityPredicate implements Predicate<Card> {
-
-    private final Rarity rarity;
-
-    public RarityPredicate(Rarity rarity) {
-        this.rarity = rarity;
-    }
+enum RareBGonePredicate implements Predicate<Card> {
+    instance;
 
     @Override
     public boolean apply(Card input, Game game) {
-        return Objects.equals(input.getRarity(), rarity);
-    }
-
-    @Override
-    public String toString() {
-        return "Rarity(" + rarity + ')';
+        return Objects.equals(input.getRarity(), Rarity.RARE) || Objects.equals(input.getRarity(), Rarity.MYTHIC);
     }
 }
diff --git a/Mage.Sets/src/mage/cards/r/RestoreBalance.java b/Mage.Sets/src/mage/cards/r/RestoreBalance.java
index dec27da79f..18a8f97963 100644
--- a/Mage.Sets/src/mage/cards/r/RestoreBalance.java
+++ b/Mage.Sets/src/mage/cards/r/RestoreBalance.java
@@ -1,25 +1,13 @@
-
 package mage.cards.r;
 
-import mage.abilities.Ability;
 import mage.abilities.costs.mana.ColoredManaCost;
-import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.BalanceEffect;
 import mage.abilities.keyword.SuspendAbility;
-import mage.cards.*;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.ColoredManaSymbol;
-import mage.constants.Outcome;
-import mage.filter.FilterCard;
-import mage.filter.common.FilterControlledCreaturePermanent;
-import mage.filter.common.FilterControlledLandPermanent;
-import mage.game.Game;
-import mage.game.permanent.Permanent;
-import mage.players.Player;
-import mage.target.common.TargetCardInHand;
-import mage.target.common.TargetControlledPermanent;
 
-import java.util.HashMap;
-import java.util.Map;
 import java.util.UUID;
 
 /**
@@ -35,10 +23,10 @@ public final class RestoreBalance extends CardImpl {
         // Suspend 6-{W}
         this.addAbility(new SuspendAbility(6, new ColoredManaCost(ColoredManaSymbol.W), this));
         // Each player chooses a number of lands they control equal to the number of lands controlled by the player who controls the fewest, then sacrifices the rest. Players sacrifice creatures and discard cards the same way.
-        this.getSpellAbility().addEffect(new RestoreBalanceEffect());
+        this.getSpellAbility().addEffect(new BalanceEffect());
     }
 
-    public RestoreBalance(final RestoreBalance card) {
+    private RestoreBalance(final RestoreBalance card) {
         super(card);
     }
 
@@ -47,137 +35,3 @@ public final class RestoreBalance extends CardImpl {
         return new RestoreBalance(this);
     }
 }
-
-
-class RestoreBalanceEffect extends OneShotEffect {
-
-    RestoreBalanceEffect() {
-        super(Outcome.Sacrifice);
-        staticText = "Each player chooses a number of lands they control equal to the number of lands controlled by the player who controls the fewest, then sacrifices the rest. Players sacrifice creatures and discard cards the same way";
-    }
-
-    RestoreBalanceEffect(final RestoreBalanceEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public RestoreBalanceEffect copy() {
-        return new RestoreBalanceEffect(this);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        Player controller = game.getPlayer(source.getControllerId());
-        if (controller != null) {
-            //Lands
-            int minLand = Integer.MAX_VALUE;
-            Cards landsToSacrifice = new CardsImpl();
-            for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
-                Player player = game.getPlayer(playerId);
-                if (player != null) {
-                    int count = game.getBattlefield().countAll(new FilterControlledLandPermanent(), player.getId(), game);
-                    if (count < minLand) {
-                        minLand = count;
-                    }
-                }
-            }
-
-            for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
-                Player player = game.getPlayer(playerId);
-                if (player != null) {
-                    TargetControlledPermanent target = new TargetControlledPermanent(minLand, minLand, new FilterControlledLandPermanent(), true);
-                    if (target.choose(Outcome.Sacrifice, player.getId(), source.getSourceId(), game)) {
-                        for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterControlledLandPermanent(), player.getId(), source.getSourceId(), game)) {
-                            if (permanent != null && !target.getTargets().contains(permanent.getId())) {
-                                landsToSacrifice.add(permanent);
-                            }
-                        }
-                    }
-                }
-            }
-
-            for (UUID cardId : landsToSacrifice) {
-                Permanent permanent = game.getPermanent(cardId);
-                if (permanent != null) {
-                    permanent.sacrifice(source.getSourceId(), game);
-                }
-            }
-
-            //Creatures
-            int minCreature = Integer.MAX_VALUE;
-            Cards creaturesToSacrifice = new CardsImpl();
-            for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
-                Player player = game.getPlayer(playerId);
-                if (player != null) {
-                    int count = game.getBattlefield().countAll(new FilterControlledCreaturePermanent(), player.getId(), game);
-                    if (count < minCreature) {
-                        minCreature = count;
-                    }
-                }
-            }
-
-            for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
-                Player player = game.getPlayer(playerId);
-                if (player != null) {
-                    TargetControlledPermanent target = new TargetControlledPermanent(minCreature, minCreature, new FilterControlledCreaturePermanent(), true);
-                    if (target.choose(Outcome.Sacrifice, player.getId(), source.getSourceId(), game)) {
-                        for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterControlledCreaturePermanent(), player.getId(), source.getSourceId(), game)) {
-                            if (permanent != null && !target.getTargets().contains(permanent.getId())) {
-                                creaturesToSacrifice.add(permanent);
-                            }
-                        }
-                    }
-                }
-            }
-
-            for (UUID cardId : creaturesToSacrifice) {
-                Permanent permanent = game.getPermanent(cardId);
-                if (permanent != null) {
-                    permanent.sacrifice(source.getSourceId(), game);
-                }
-            }
-
-            //Cards in hand
-            int minCard = Integer.MAX_VALUE;
-            Map<UUID, Cards> cardsToDiscard = new HashMap<>(2);
-            for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
-                Player player = game.getPlayer(playerId);
-                if (player != null) {
-                    int count = player.getHand().size();
-                    if (count < minCard) {
-                        minCard = count;
-                    }
-                }
-            }
-
-            for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
-                Player player = game.getPlayer(playerId);
-                if (player != null) {
-                    Cards cards = new CardsImpl();
-                    TargetCardInHand target = new TargetCardInHand(minCard, new FilterCard());
-                    if (target.choose(Outcome.Discard, player.getId(), source.getSourceId(), game)) {
-                        for (Card card : player.getHand().getCards(game)) {
-                            if (card != null && !target.getTargets().contains(card.getId())) {
-                                cards.add(card);
-                            }
-                        }
-                        cardsToDiscard.put(playerId, cards);
-                    }
-                }
-            }
-
-            for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
-                Player player = game.getPlayer(playerId);
-                if (player != null && cardsToDiscard.get(playerId) != null) {
-                    for (UUID cardId : cardsToDiscard.get(playerId)) {
-                        Card card = game.getCard(cardId);
-                        player.discard(card, source, game);
-
-                    }
-                }
-            }
-            return true;
-        }
-        return false;
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/r/RiseFall.java b/Mage.Sets/src/mage/cards/r/RiseFall.java
index b3042b9902..e32809e280 100644
--- a/Mage.Sets/src/mage/cards/r/RiseFall.java
+++ b/Mage.Sets/src/mage/cards/r/RiseFall.java
@@ -1,19 +1,14 @@
-
 package mage.cards.r;
 
-import java.util.UUID;
 import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
-import mage.cards.Card;
-import mage.cards.CardSetInfo;
-import mage.cards.Cards;
-import mage.cards.CardsImpl;
-import mage.cards.SplitCard;
+import mage.cards.*;
 import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.constants.SpellAbilityType;
 import mage.constants.Zone;
+import mage.filter.FilterCard;
 import mage.filter.common.FilterCreatureCard;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
@@ -22,19 +17,22 @@ import mage.target.TargetPlayer;
 import mage.target.common.TargetCardInGraveyard;
 import mage.target.common.TargetCreaturePermanent;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class RiseFall extends SplitCard {
 
+    private static final FilterCard filter = new FilterCreatureCard("creature card from a graveyard");
+
     public RiseFall(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{U}{B}", "{B}{R}", SpellAbilityType.SPLIT);
 
         // Rise
         // Return target creature card from a graveyard and target creature on the battlefield to their owners' hands.
         getLeftHalfCard().getSpellAbility().addEffect(new RiseEffect());
-        getLeftHalfCard().getSpellAbility().addTarget(new TargetCardInGraveyard(new FilterCreatureCard("creature card from a graveyard")));
+        getLeftHalfCard().getSpellAbility().addTarget(new TargetCardInGraveyard(filter));
         getLeftHalfCard().getSpellAbility().addTarget(new TargetCreaturePermanent());
 
         // Fall
@@ -43,7 +41,7 @@ public final class RiseFall extends SplitCard {
         getRightHalfCard().getSpellAbility().addTarget(new TargetPlayer());
     }
 
-    public RiseFall(final RiseFall card) {
+    private RiseFall(final RiseFall card) {
         super(card);
     }
 
@@ -55,12 +53,12 @@ public final class RiseFall extends SplitCard {
 
 class RiseEffect extends OneShotEffect {
 
-    public RiseEffect() {
+    RiseEffect() {
         super(Outcome.ReturnToHand);
         this.staticText = "Return target creature card from a graveyard and target creature on the battlefield to their owners' hands";
     }
 
-    public RiseEffect(final RiseEffect effect) {
+    private RiseEffect(final RiseEffect effect) {
         super(effect);
     }
 
@@ -72,31 +70,31 @@ class RiseEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player controller = game.getPlayer(source.getControllerId());
-        if (controller != null) {
-            Cards cardsToHand = new CardsImpl();
-            Card cardInGraveyard = game.getCard(getTargetPointer().getFirst(game, source));
-            if (cardInGraveyard != null) {
-                cardsToHand.add(cardInGraveyard);
-            }
-            Permanent permanent = game.getPermanent(source.getTargets().get(1).getFirstTarget());
-            if (permanent != null) {
-                cardsToHand.add(permanent);
-            }
-            controller.moveCards(cardsToHand, Zone.HAND, source, game);
-            return true;
+        if (controller == null) {
+            return false;
         }
-        return false;
+        Cards cardsToHand = new CardsImpl();
+        Card cardInGraveyard = game.getCard(getTargetPointer().getFirst(game, source));
+        if (cardInGraveyard != null) {
+            cardsToHand.add(cardInGraveyard);
+        }
+        Permanent permanent = game.getPermanent(source.getTargets().get(1).getFirstTarget());
+        if (permanent != null) {
+            cardsToHand.add(permanent);
+        }
+        controller.moveCards(cardsToHand, Zone.HAND, source, game);
+        return true;
     }
 }
 
 class FallEffect extends OneShotEffect {
 
-    public FallEffect() {
+    FallEffect() {
         super(Outcome.Discard);
         this.staticText = "Target player reveals two cards at random from their hand, then discards each nonland card revealed this way";
     }
 
-    public FallEffect(final FallEffect effect) {
+    private FallEffect(final FallEffect effect) {
         super(effect);
     }
 
@@ -109,34 +107,32 @@ class FallEffect extends OneShotEffect {
     public boolean apply(Game game, Ability source) {
         Player controller = game.getPlayer(source.getControllerId());
         MageObject sourceObject = game.getObject(source.getSourceId());
-        if (controller != null) {
-            Player targetPlayer = game.getPlayer(getTargetPointer().getFirst(game, source));
-            if (targetPlayer != null) {
-                if (!targetPlayer.getHand().isEmpty()) {
-                    Card card = targetPlayer.getHand().getRandom(game);
-                    if (card == null) {
-                        return false;
-                    }
-                    Cards cards = new CardsImpl(card);
-                    if (targetPlayer.getHand().size() > 1) {
-                        do {
-                            card = targetPlayer.getHand().getRandom(game);
-                            if (card == null) {
-                                return false;
-                            }
-                        } while (cards.contains(card.getId()));
-                        cards.add(card);
-                    }
-                    targetPlayer.revealCards(sourceObject.getIdName(), cards, game);
-                    for (Card cardToDiscard : cards.getCards(game)) {
-                        if (!cardToDiscard.isLand()) {
-                            targetPlayer.discard(cardToDiscard, source, game);
-                        }
-                    }
-                }
-            }
+        if (controller == null) {
+            return false;
+        }
+        Player targetPlayer = game.getPlayer(getTargetPointer().getFirst(game, source));
+        if (targetPlayer == null) {
             return true;
         }
-        return false;
+        if (targetPlayer.getHand().isEmpty()) {
+            return true;
+        }
+        Card card = targetPlayer.getHand().getRandom(game);
+        if (card == null) {
+            return false;
+        }
+        Cards cards = new CardsImpl(card);
+        if (targetPlayer.getHand().size() > 1) {
+            do {
+                card = targetPlayer.getHand().getRandom(game);
+                if (card == null) {
+                    return false;
+                }
+            } while (cards.contains(card.getId()));
+            cards.add(card);
+        }
+        targetPlayer.revealCards(sourceObject.getIdName(), cards, game);
+        targetPlayer.discard(cards, source, game);
+        return true;
     }
 }
diff --git a/Mage.Sets/src/mage/cards/r/RitesOfSpring.java b/Mage.Sets/src/mage/cards/r/RitesOfSpring.java
index 76dfca48a1..3991d3acd3 100644
--- a/Mage.Sets/src/mage/cards/r/RitesOfSpring.java
+++ b/Mage.Sets/src/mage/cards/r/RitesOfSpring.java
@@ -1,25 +1,23 @@
-
 package mage.cards.r;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
+import mage.cards.CardsImpl;
 import mage.constants.CardType;
 import mage.constants.Outcome;
-import mage.filter.FilterCard;
 import mage.filter.StaticFilters;
 import mage.game.Game;
 import mage.players.Player;
-import mage.target.Target;
-import mage.target.common.TargetCardInHand;
+import mage.target.TargetCard;
 import mage.target.common.TargetCardInLibrary;
+import mage.target.common.TargetDiscard;
+
+import java.util.UUID;
 
 /**
- *
  * @author LevelX2
  */
 public final class RitesOfSpring extends CardImpl {
@@ -31,7 +29,7 @@ public final class RitesOfSpring extends CardImpl {
         getSpellAbility().addEffect(new RitesOfSpringEffect());
     }
 
-    public RitesOfSpring(final RitesOfSpring card) {
+    private RitesOfSpring(final RitesOfSpring card) {
         super(card);
     }
 
@@ -43,12 +41,13 @@ public final class RitesOfSpring extends CardImpl {
 
 class RitesOfSpringEffect extends OneShotEffect {
 
-    public RitesOfSpringEffect() {
+    RitesOfSpringEffect() {
         super(Outcome.DrawCard);
-        this.staticText = "Discard any number of cards. Search your library for up to that many basic land cards, reveal those cards, and put them into your hand. Then shuffle your library.";
+        this.staticText = "Discard any number of cards. Search your library for up to that many basic land cards, " +
+                "reveal those cards, and put them into your hand. Then shuffle your library.";
     }
 
-    public RitesOfSpringEffect(final RitesOfSpringEffect effect) {
+    private RitesOfSpringEffect(final RitesOfSpringEffect effect) {
         super(effect);
     }
 
@@ -60,23 +59,15 @@ class RitesOfSpringEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player controller = game.getPlayer(source.getControllerId());
-        if (controller != null) {
-            Target target = new TargetCardInHand(0, Integer.MAX_VALUE, new FilterCard("cards to discard"));
-            while (controller.canRespond() && !target.isChosen()) {
-                target.choose(Outcome.BoostCreature, controller.getId(), source.getSourceId(), game);
-            }
-            int numDiscarded = 0;
-            for (UUID targetId : target.getTargets()) {
-                Card card = controller.getHand().get(targetId, game);
-                if (controller.discard(card, source, game)) {
-                    numDiscarded++;
-                }
-            }
-            game.applyEffects();
-            return new SearchLibraryPutInHandEffect(
-                    new TargetCardInLibrary(0, numDiscarded, StaticFilters.FILTER_CARD_BASIC_LAND), true, true)
-                    .apply(game, source);
+        if (controller == null) {
+            return false;
         }
-        return false;
+        TargetCard target = new TargetDiscard(0, Integer.MAX_VALUE, StaticFilters.FILTER_CARD, controller.getId());
+        controller.choose(Outcome.AIDontUseIt, controller.getHand(), target, game);
+        int numDiscarded = controller.discard(new CardsImpl(target.getTargets()), source, game).size();
+        new SearchLibraryPutInHandEffect(new TargetCardInLibrary(
+                0, numDiscarded, StaticFilters.FILTER_CARD_BASIC_LAND
+        ), true, true).apply(game, source);
+        return true;
     }
 }
diff --git a/Mage.Sets/src/mage/cards/s/SacredRites.java b/Mage.Sets/src/mage/cards/s/SacredRites.java
index a1302c4ba3..3104c737bd 100644
--- a/Mage.Sets/src/mage/cards/s/SacredRites.java
+++ b/Mage.Sets/src/mage/cards/s/SacredRites.java
@@ -1,24 +1,23 @@
-
 package mage.cards.s;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.continuous.BoostControlledEffect;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
+import mage.cards.CardsImpl;
 import mage.constants.CardType;
 import mage.constants.Duration;
 import mage.constants.Outcome;
-import mage.filter.FilterCard;
+import mage.filter.StaticFilters;
 import mage.game.Game;
 import mage.players.Player;
 import mage.target.Target;
-import mage.target.common.TargetCardInHand;
+import mage.target.common.TargetDiscard;
+
+import java.util.UUID;
 
 /**
- *
  * @author cbt33
  */
 public final class SacredRites extends CardImpl {
@@ -30,7 +29,7 @@ public final class SacredRites extends CardImpl {
         this.getSpellAbility().addEffect(new SacredRitesEffect());
     }
 
-    public SacredRites(final SacredRites card) {
+    private SacredRites(final SacredRites card) {
         super(card);
     }
 
@@ -43,11 +42,11 @@ public final class SacredRites extends CardImpl {
 class SacredRitesEffect extends OneShotEffect {
 
     SacredRitesEffect() {
-        super(Outcome.Benefit);
+        super(Outcome.AIDontUseIt);
         this.staticText = "Discard any number of cards. Creatures you control get +0/+1 until end of turn for each card discarded this way.";
     }
 
-    SacredRitesEffect(final SacredRitesEffect effect) {
+    private SacredRitesEffect(final SacredRitesEffect effect) {
         super(effect);
     }
 
@@ -59,21 +58,15 @@ class SacredRitesEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player controller = game.getPlayer(source.getControllerId());
-        if (controller != null) {
-            Target target = new TargetCardInHand(0, Integer.MAX_VALUE, new FilterCard("cards to discard"));
-            while (controller.canRespond() && !target.isChosen()) {
-                target.choose(Outcome.BoostCreature, controller.getId(), source.getSourceId(), game);
-            }
-            int numDiscarded = 0;
-            for (UUID targetId : target.getTargets()) {
-                Card card = controller.getHand().get(targetId, game);
-                if (controller.discard(card, source, game)) {
-                    numDiscarded++;
-                }
-            }
-            game.addEffect(new BoostControlledEffect(0, numDiscarded, Duration.EndOfTurn), source);
-            return true;
+        if (controller == null) {
+            return false;
         }
-        return false;
+        Target target = new TargetDiscard(0, Integer.MAX_VALUE, StaticFilters.FILTER_CARD, controller.getId());
+        target.choose(outcome, controller.getId(), source.getSourceId(), game);
+        int numDiscarded = controller.discard(new CardsImpl(target.getTargets()), source, game).size();
+        if (numDiscarded > 0) {
+            game.addEffect(new BoostControlledEffect(0, numDiscarded, Duration.EndOfTurn), source);
+        }
+        return true;
     }
 }
diff --git a/Mage.Sets/src/mage/cards/s/ShatterAssumptions.java b/Mage.Sets/src/mage/cards/s/ShatterAssumptions.java
index bea5cab22d..09dc73af6f 100644
--- a/Mage.Sets/src/mage/cards/s/ShatterAssumptions.java
+++ b/Mage.Sets/src/mage/cards/s/ShatterAssumptions.java
@@ -3,7 +3,9 @@ package mage.cards.s;
 import mage.abilities.Ability;
 import mage.abilities.Mode;
 import mage.abilities.effects.OneShotEffect;
-import mage.cards.*;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.cards.CardsImpl;
 import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.filter.FilterCard;
@@ -83,17 +85,14 @@ class ShatterAssumptionsEffect extends OneShotEffect {
         if (player == null) {
             return false;
         }
-        Cards cards = new CardsImpl(player.getHand());
-        player.revealCards(source, cards, game);
+        player.revealCards(source, player.getHand(), game);
         FilterCard f;
         if (colorless) {
             f = filter;
         } else {
             f = filter2;
         }
-        for (Card card : cards.getCards(f, source.getSourceId(), source.getControllerId(), game)) {
-            player.discard(card, source, game);
-        }
+        player.discard(new CardsImpl(player.getHand().getCards(f, game)), source, game);
         return true;
     }
 }
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/s/Shocker.java b/Mage.Sets/src/mage/cards/s/Shocker.java
index e988fd2caf..176afefbf0 100644
--- a/Mage.Sets/src/mage/cards/s/Shocker.java
+++ b/Mage.Sets/src/mage/cards/s/Shocker.java
@@ -1,28 +1,26 @@
-
 package mage.cards.s;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.DealsDamageToAPlayerTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.SubType;
 import mage.constants.Outcome;
+import mage.constants.SubType;
 import mage.game.Game;
 import mage.players.Player;
 
+import java.util.UUID;
+
 /**
- *
  * @author markedagain
  */
 public final class Shocker extends CardImpl {
 
     public Shocker(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{R}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}");
         this.subtype.add(SubType.INSECT);
         this.power = new MageInt(1);
         this.toughness = new MageInt(1);
@@ -40,14 +38,15 @@ public final class Shocker extends CardImpl {
         return new Shocker(this);
     }
 }
+
 class ShockerEffect extends OneShotEffect {
 
-    public ShockerEffect() {
+    ShockerEffect() {
         super(Outcome.Discard);
         this.staticText = " that player discards all the cards in their hand, then draws that many cards";
     }
 
-    public ShockerEffect(final ShockerEffect effect) {
+    private ShockerEffect(final ShockerEffect effect) {
         super(effect);
     }
 
@@ -59,14 +58,11 @@ class ShockerEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player targetPlayer = game.getPlayer(targetPointer.getFirst(game, source));
-            if (targetPlayer != null) {
-                int count = targetPlayer.getHand().size();
-                for (Card card : targetPlayer.getHand().getCards(game)) {
-                    targetPlayer.discard(card, source, game);
-                }
-                targetPlayer.drawCards(count, source.getSourceId(), game);
-                return false;
-            }
+        if (targetPlayer == null) {
+            return false;
+        }
+        int count = targetPlayer.discard(targetPlayer.getHand(), source, game).size();
+        targetPlayer.drawCards(count, source.getSourceId(), game);
         return true;
     }
-}
\ No newline at end of file
+}
diff --git a/Mage.Sets/src/mage/cards/s/SireOfInsanity.java b/Mage.Sets/src/mage/cards/s/SireOfInsanity.java
index 7a9f1af074..90ece9fc95 100644
--- a/Mage.Sets/src/mage/cards/s/SireOfInsanity.java
+++ b/Mage.Sets/src/mage/cards/s/SireOfInsanity.java
@@ -1,43 +1,39 @@
-
 package mage.cards.s;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.OnEventTriggeredAbility;
+import mage.abilities.common.BeginningOfEndStepTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.SubType;
 import mage.constants.Outcome;
+import mage.constants.SubType;
+import mage.constants.TargetController;
 import mage.game.Game;
-import mage.game.events.GameEvent;
 import mage.players.Player;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
-
-
 public final class SireOfInsanity extends CardImpl {
 
-    public SireOfInsanity (UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{B}{R}");
+    public SireOfInsanity(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}{R}");
         this.subtype.add(SubType.DEMON);
 
-
         this.power = new MageInt(6);
         this.toughness = new MageInt(4);
 
         // At the beginning of each end step, each player discards their hand.
-        this.addAbility(new OnEventTriggeredAbility(GameEvent.EventType.END_TURN_STEP_PRE, "beginning of each end step", true, new SireOfInsanityEffect()));
-
+        this.addAbility(new BeginningOfEndStepTriggeredAbility(
+                new SireOfInsanityEffect(), TargetController.ANY, false
+        ));
     }
 
-    public SireOfInsanity (final SireOfInsanity card) {
+    private SireOfInsanity(final SireOfInsanity card) {
         super(card);
     }
 
@@ -54,7 +50,7 @@ class SireOfInsanityEffect extends OneShotEffect {
         staticText = "each player discards their hand";
     }
 
-    SireOfInsanityEffect(final SireOfInsanityEffect effect) {
+    private SireOfInsanityEffect(final SireOfInsanityEffect effect) {
         super(effect);
     }
 
@@ -67,9 +63,7 @@ class SireOfInsanityEffect extends OneShotEffect {
         for (UUID playerId : game.getState().getPlayersInRange(sourcePlayer.getId(), game)) {
             Player player = game.getPlayer(playerId);
             if (player != null) {
-                for (Card c : player.getHand().getCards(game)) {
-                    player.discard(c, source, game);
-                }
+                player.discard(player.getHand(), source, game);
             }
         }
         return true;
diff --git a/Mage.Sets/src/mage/cards/s/SkullRend.java b/Mage.Sets/src/mage/cards/s/SkullRend.java
index 9b521c8794..e2a3f3ec39 100644
--- a/Mage.Sets/src/mage/cards/s/SkullRend.java
+++ b/Mage.Sets/src/mage/cards/s/SkullRend.java
@@ -2,7 +2,6 @@ package mage.cards.s;
 
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
@@ -10,24 +9,24 @@ import mage.constants.Outcome;
 import mage.game.Game;
 import mage.players.Player;
 
+import java.util.List;
 import java.util.Objects;
 import java.util.UUID;
+import java.util.stream.Collectors;
 
 /**
- * @author LevelX2
+ * @author TheElk801
  */
 public final class SkullRend extends CardImpl {
 
     public SkullRend(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{B}{R}");
 
-
         // Skull Rend deals 2 damage to each opponent. Those players each discard two cards at random.
         this.getSpellAbility().addEffect(new SkullRendEffect());
-
     }
 
-    public SkullRend(final SkullRend card) {
+    private SkullRend(final SkullRend card) {
         super(card);
     }
 
@@ -38,39 +37,29 @@ public final class SkullRend extends CardImpl {
 
     private static class SkullRendEffect extends OneShotEffect {
 
-        public SkullRendEffect() {
+        private SkullRendEffect() {
             super(Outcome.Damage);
             staticText = "{this} deals 2 damage to each opponent. Those players each discard two cards at random";
         }
 
-        public SkullRendEffect(final SkullRendEffect effect) {
+        private SkullRendEffect(final SkullRendEffect effect) {
             super(effect);
         }
 
         @Override
         public boolean apply(Game game, Ability source) {
-            Player controller = game.getPlayer(source.getControllerId());
-            if (controller != null) {
-                for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
-                    if (!Objects.equals(playerId, source.getControllerId())) {
-                        Player opponent = game.getPlayer(playerId);
-                        if (opponent != null) {
-                            // damage
-                            opponent.damage(2, source.getSourceId(), game);
-                            // discard 2 cards at random
-                            int amount = Math.min(2, opponent.getHand().size());
-                            for (int i = 0; i < amount; i++) {
-                                Card card = opponent.getHand().getRandom(game);
-                                if (card != null) {
-                                    opponent.discard(card, source, game);
-                                }
-                            }
-                        }
-                    }
-                }
-                return true;
+            List<Player> opponents = game
+                    .getOpponents(source.getControllerId())
+                    .stream().map(game::getPlayer)
+                    .filter(Objects::nonNull)
+                    .collect(Collectors.toList());
+            for (Player opponent : opponents) {
+                opponent.damage(2, source.getSourceId(), game);
             }
-            return false;
+            for (Player opponent : opponents) {
+                opponent.discard(2, true, source, game);
+            }
+            return true;
         }
 
         @Override
@@ -78,4 +67,4 @@ public final class SkullRend extends CardImpl {
             return new SkullRendEffect(this);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/Mage.Sets/src/mage/cards/t/TrapfindersTrick.java b/Mage.Sets/src/mage/cards/t/TrapfindersTrick.java
index 970b08a6f7..03c02e1b03 100644
--- a/Mage.Sets/src/mage/cards/t/TrapfindersTrick.java
+++ b/Mage.Sets/src/mage/cards/t/TrapfindersTrick.java
@@ -1,37 +1,34 @@
-
 package mage.cards.t;
 
-import java.util.Set;
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.cards.Cards;
+import mage.cards.CardsImpl;
 import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.constants.SubType;
+import mage.filter.FilterCard;
 import mage.game.Game;
 import mage.players.Player;
 import mage.target.TargetPlayer;
 
+import java.util.UUID;
+
 /**
- *
  * @author North
  */
 public final class TrapfindersTrick extends CardImpl {
 
     public TrapfindersTrick(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{U}");
-
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{U}");
 
         // Target player reveals their hand and discards all Trap cards.
         this.getSpellAbility().addEffect(new TrapfindersTrickEffect());
         this.getSpellAbility().addTarget(new TargetPlayer());
     }
 
-    public TrapfindersTrick(final TrapfindersTrick card) {
+    private TrapfindersTrick(final TrapfindersTrick card) {
         super(card);
     }
 
@@ -43,12 +40,18 @@ public final class TrapfindersTrick extends CardImpl {
 
 class TrapfindersTrickEffect extends OneShotEffect {
 
-    public TrapfindersTrickEffect() {
+    private static final FilterCard filter = new FilterCard();
+
+    static {
+        filter.add(SubType.TRAP.getPredicate());
+    }
+
+    TrapfindersTrickEffect() {
         super(Outcome.Discard);
         this.staticText = "Target player reveals their hand and discards all Trap cards";
     }
 
-    public TrapfindersTrickEffect(final TrapfindersTrickEffect effect) {
+    private TrapfindersTrickEffect(final TrapfindersTrickEffect effect) {
         super(effect);
     }
 
@@ -60,17 +63,11 @@ class TrapfindersTrickEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player player = game.getPlayer(source.getFirstTarget());
-        if (player != null) {
-            Cards hand = player.getHand();
-            player.revealCards("Trapfinder's Trick", hand, game);
-            Set<Card> cards = hand.getCards(game);
-            for (Card card : cards) {
-                if (card != null && card.hasSubtype(SubType.TRAP, game)) {
-                    player.discard(card, source, game);
-                }
-            }
-            return true;
+        if (player == null) {
+            return false;
         }
-        return false;
+        player.revealCards(source, player.getHand(), game);
+        player.discard(new CardsImpl(player.getHand().getCards(filter, game)), source, game);
+        return true;
     }
 }
diff --git a/Mage.Sets/src/mage/cards/t/TsabosDecree.java b/Mage.Sets/src/mage/cards/t/TsabosDecree.java
index 193105e8b1..ab5b862b09 100644
--- a/Mage.Sets/src/mage/cards/t/TsabosDecree.java
+++ b/Mage.Sets/src/mage/cards/t/TsabosDecree.java
@@ -1,15 +1,11 @@
-
 package mage.cards.t;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
 import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
+import mage.cards.CardsImpl;
 import mage.choices.Choice;
 import mage.choices.ChoiceCreatureType;
 import mage.constants.CardType;
@@ -22,8 +18,9 @@ import mage.game.permanent.Permanent;
 import mage.players.Player;
 import mage.target.TargetPlayer;
 
+import java.util.UUID;
+
 /**
- *
  * @author fireshoes
  */
 public final class TsabosDecree extends CardImpl {
@@ -36,7 +33,7 @@ public final class TsabosDecree extends CardImpl {
         this.getSpellAbility().addTarget(new TargetPlayer());
     }
 
-    public TsabosDecree(final TsabosDecree card) {
+    private TsabosDecree(final TsabosDecree card) {
         super(card);
     }
 
@@ -48,12 +45,14 @@ public final class TsabosDecree extends CardImpl {
 
 class TsabosDecreeEffect extends OneShotEffect {
 
-    public TsabosDecreeEffect() {
+    TsabosDecreeEffect() {
         super(Outcome.UnboostCreature);
-        staticText = "Choose a creature type. Target player reveals their hand and discards all creature cards of that type. Then destroy all creatures of that type that player controls. They can't be regenerated";
+        staticText = "Choose a creature type. Target player reveals their hand and discards " +
+                "all creature cards of that type. Then destroy all creatures of that type that player controls. " +
+                "They can't be regenerated";
     }
 
-    public TsabosDecreeEffect(final TsabosDecreeEffect effect) {
+    private TsabosDecreeEffect(final TsabosDecreeEffect effect) {
         super(effect);
     }
 
@@ -62,36 +61,29 @@ class TsabosDecreeEffect extends OneShotEffect {
         Player player = game.getPlayer(source.getControllerId());
         Player targetPlayer = game.getPlayer(targetPointer.getFirst(game, source));
         MageObject sourceObject = game.getObject(source.getSourceId());
-        if (player != null) {
-            if(sourceObject != null) {
-                Choice typeChoice = new ChoiceCreatureType(sourceObject);
-                if (!player.choose(outcome, typeChoice, game)) {
-                    return false;
-                }
-                game.informPlayers(sourceObject.getLogName() + " chosen type: " + typeChoice.getChoice());
-                targetPlayer.revealCards("hand of " + targetPlayer.getName(), targetPlayer.getHand(), game);
-                FilterCard filterCard = new FilterCard();
-                filterCard.add(SubType.byDescription(typeChoice.getChoice()).getPredicate());
-                List<Card> toDiscard = new ArrayList<>();
-                for (Card card : targetPlayer.getHand().getCards(game)) {
-                    if (filterCard.match(card, game)) {
-                        toDiscard.add(card);
-                    }
-                }
-                for (Card card : toDiscard) {
-                    targetPlayer.discard(card, source, game);
-                }
-                FilterCreaturePermanent filterCreaturePermanent = new FilterCreaturePermanent();
-                filterCreaturePermanent.add(SubType.byDescription(typeChoice.getChoice()).getPredicate());
-                for (Permanent creature : game.getBattlefield().getActivePermanents(filterCreaturePermanent, source.getSourceId(), game)) {
-                    if (creature.isControlledBy(targetPlayer.getId())) {
-                        creature.destroy(source.getSourceId(), game, true);
-                    }
-                }
-                return true;
+        if (player == null) {
+            return false;
+        }
+        if (sourceObject == null) {
+            return false;
+        }
+        Choice typeChoice = new ChoiceCreatureType(sourceObject);
+        if (!player.choose(outcome, typeChoice, game)) {
+            return false;
+        }
+        game.informPlayers(sourceObject.getLogName() + " chosen type: " + typeChoice.getChoice());
+        targetPlayer.revealCards("hand of " + targetPlayer.getName(), targetPlayer.getHand(), game);
+        FilterCard filterCard = new FilterCard();
+        filterCard.add(SubType.byDescription(typeChoice.getChoice()).getPredicate());
+        targetPlayer.discard(new CardsImpl(targetPlayer.getHand().getCards(filterCard, game)), source, game);
+        FilterCreaturePermanent filterCreaturePermanent = new FilterCreaturePermanent();
+        filterCreaturePermanent.add(SubType.byDescription(typeChoice.getChoice()).getPredicate());
+        for (Permanent creature : game.getBattlefield().getActivePermanents(filterCreaturePermanent, source.getSourceId(), game)) {
+            if (creature.isControlledBy(targetPlayer.getId())) {
+                creature.destroy(source.getSourceId(), game, true);
             }
         }
-        return false;
+        return true;
     }
 
     @Override
diff --git a/Mage.Sets/src/mage/cards/t/Tyrannize.java b/Mage.Sets/src/mage/cards/t/Tyrannize.java
index c7055ae97a..90c56333a0 100644
--- a/Mage.Sets/src/mage/cards/t/Tyrannize.java
+++ b/Mage.Sets/src/mage/cards/t/Tyrannize.java
@@ -29,7 +29,6 @@ public final class Tyrannize extends CardImpl {
         // Target player discards their hand unless they pay 7 life.
         this.getSpellAbility().addTarget(new TargetPlayer());
         this.getSpellAbility().addEffect(new TyrannizeEffect());
-        
     }
 
     public Tyrannize(final Tyrannize card) {
@@ -66,9 +65,7 @@ class TyrannizeEffect extends OneShotEffect {
             if (!cost.canPay(source, source.getSourceId(), player.getId(), game)
                     || !player.chooseUse(Outcome.LoseLife, "Pay 7 life?", source, game)
                     || !cost.pay(source, game, source.getSourceId(), player.getId(), false, null)) {
-                for (Card card : player.getHand().getCards(game)) {
-                    player.discard(card, source, game);
-                }
+                player.discard(player.getHand(),source,game);
             }
             return true;
         }
diff --git a/Mage.Sets/src/mage/cards/v/Void.java b/Mage.Sets/src/mage/cards/v/Void.java
index 2f11a51224..902f2a2b6f 100644
--- a/Mage.Sets/src/mage/cards/v/Void.java
+++ b/Mage.Sets/src/mage/cards/v/Void.java
@@ -1,14 +1,10 @@
-
 package mage.cards.v;
 
-import java.util.HashSet;
-import java.util.Set;
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
+import mage.cards.CardsImpl;
 import mage.choices.Choice;
 import mage.choices.ChoiceImpl;
 import mage.constants.CardType;
@@ -22,6 +18,10 @@ import mage.game.permanent.Permanent;
 import mage.players.Player;
 import mage.target.TargetPlayer;
 
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
+
 /**
  * @author LevelX2
  */
@@ -33,7 +33,6 @@ public final class Void extends CardImpl {
         // Choose a number. Destroy all artifacts and creatures with converted mana cost equal to that number. Then target player reveals their hand and discards all nonland cards with converted mana cost equal to the number.
         this.getSpellAbility().addTarget(new TargetPlayer());
         this.getSpellAbility().addEffect(new VoidEffect());
-
     }
 
     public Void(final Void card) {
@@ -48,12 +47,12 @@ public final class Void extends CardImpl {
 
 class VoidEffect extends OneShotEffect {
 
-    public VoidEffect() {
+    VoidEffect() {
         super(Outcome.DestroyPermanent);
         this.staticText = "Choose a number. Destroy all artifacts and creatures with converted mana cost equal to that number. Then target player reveals their hand and discards all nonland cards with converted mana cost equal to the number";
     }
 
-    public VoidEffect(final VoidEffect effect) {
+    private VoidEffect(final VoidEffect effect) {
         super(effect);
     }
 
@@ -65,39 +64,36 @@ class VoidEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player controller = game.getPlayer(source.getControllerId());
-        if (controller != null) {
-            Choice numberChoice = new ChoiceImpl();
-            Set<String> numbers = new HashSet<>(16);
-            for (int i = 0; i <= 15; i++) {
-                numbers.add(Integer.toString(i));
+        if (controller == null) {
+            return false;
+        }
+        Choice numberChoice = new ChoiceImpl();
+        Set<String> numbers = new HashSet<>(16);
+        for (int i = 0; i <= 15; i++) {
+            numbers.add(Integer.toString(i));
+        }
+        numberChoice.setChoices(numbers);
+        numberChoice.setMessage("Choose a number");
+        if (!controller.choose(Outcome.DestroyPermanent, numberChoice, game)) {
+            return false;
+        }
+        int number = Integer.parseInt(numberChoice.getChoice());
+        for (Permanent permanent : game.getBattlefield().getActivePermanents(source.getControllerId(), game)) {
+            if ((permanent.isArtifact() || permanent.isCreature())
+                    && permanent.getConvertedManaCost() == number) {
+                permanent.destroy(source.getSourceId(), game, false);
             }
-            numberChoice.setChoices(numbers);
-            numberChoice.setMessage("Choose a number");
-            if (!controller.choose(Outcome.DestroyPermanent, numberChoice, game)) {
-                return false;
-            }
-            int number = Integer.parseInt(numberChoice.getChoice());
-            for (Permanent permanent : game.getBattlefield().getActivePermanents(source.getControllerId(), game)) {
-                if ((permanent.isArtifact() || permanent.isCreature())
-                        && permanent.getConvertedManaCost() == number) {
-                    permanent.destroy(source.getSourceId(), game, false);
-                }
-            }
-            FilterCard filterCard = new FilterCard();
-            filterCard.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, number));
-            filterCard.add(Predicates.not(CardType.LAND.getPredicate()));
+        }
+        FilterCard filterCard = new FilterCard();
+        filterCard.add(new ConvertedManaCostPredicate(ComparisonType.EQUAL_TO, number));
+        filterCard.add(Predicates.not(CardType.LAND.getPredicate()));
 
-            Player targetPlayer = game.getPlayer(targetPointer.getFirst(game, source));
-            if (targetPlayer != null) {
-                targetPlayer.revealCards("Void", targetPlayer.getHand(), game);
-                for (Card card : targetPlayer.getHand().getCards(game)) {
-                    if (filterCard.match(card, game)) {
-                        targetPlayer.discard(card, source, game);
-                    }
-                }
-            }
+        Player targetPlayer = game.getPlayer(targetPointer.getFirst(game, source));
+        if (targetPlayer == null) {
             return true;
         }
-        return false;
+        targetPlayer.revealCards(source, targetPlayer.getHand(), game);
+        targetPlayer.discard(new CardsImpl(targetPlayer.getHand().getCards(filterCard, game)), source, game);
+        return true;
     }
 }
diff --git a/Mage.Sets/src/mage/cards/w/WhisperingMadness.java b/Mage.Sets/src/mage/cards/w/WhisperingMadness.java
index be9b8b6bc9..eb04fd9fa2 100644
--- a/Mage.Sets/src/mage/cards/w/WhisperingMadness.java
+++ b/Mage.Sets/src/mage/cards/w/WhisperingMadness.java
@@ -1,11 +1,8 @@
-
 package mage.cards.w;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.CipherEffect;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
@@ -13,23 +10,24 @@ import mage.constants.Outcome;
 import mage.game.Game;
 import mage.players.Player;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class WhisperingMadness extends CardImpl {
 
     public WhisperingMadness(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{U}{B}");
-
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}{B}");
 
         // Each player discards their hand, then draws cards equal to the greatest number of cards a player discarded this way.
         this.getSpellAbility().addEffect(new WhisperingMadnessEffect());
+
         // Cipher
         this.getSpellAbility().addEffect(new CipherEffect());
     }
 
-    public WhisperingMadness(final WhisperingMadness card) {
+    private WhisperingMadness(final WhisperingMadness card) {
         super(card);
     }
 
@@ -45,38 +43,33 @@ class WhisperingMadnessEffect extends OneShotEffect {
         staticText = "Each player discards their hand, then draws cards equal to the greatest number of cards a player discarded this way";
     }
 
-    WhisperingMadnessEffect(final WhisperingMadnessEffect effect) {
+    private WhisperingMadnessEffect(final WhisperingMadnessEffect effect) {
         super(effect);
     }
 
     @Override
     public boolean apply(Game game, Ability source) {
         int maxDiscarded = 0;
-        Player sourcePlayer = game.getPlayer(source.getControllerId());
-        if (sourcePlayer == null) {
+        Player controller = game.getPlayer(source.getControllerId());
+        if (controller == null) {
             return false;
         }
-        for (UUID playerId : game.getState().getPlayersInRange(sourcePlayer.getId(), game)) {
+        for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
             Player player = game.getPlayer(playerId);
-            if (player != null) {
-                int discarded = 0;
-                for (Card c : player.getHand().getCards(game)) {
-                    if (player.discard(c, source, game)) {
-                        discarded++;
-                    }
-                }
-                if (discarded > maxDiscarded) {
-                    maxDiscarded = discarded;
-                }
+            if (player == null) {
+                continue;
+            }
+            int discarded = player.discard(player.getHand(), source, game).size();
+            if (discarded > maxDiscarded) {
+                maxDiscarded = discarded;
             }
         }
-        for (UUID playerId : game.getState().getPlayersInRange(sourcePlayer.getId(), game)) {
+        for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
             Player player = game.getPlayer(playerId);
             if (player != null) {
                 player.drawCards(maxDiscarded, source.getSourceId(), game);
             }
         }
-
         return true;
     }
 
@@ -84,4 +77,4 @@ class WhisperingMadnessEffect extends OneShotEffect {
     public WhisperingMadnessEffect copy() {
         return new WhisperingMadnessEffect(this);
     }
-}
\ No newline at end of file
+}
diff --git a/Mage.Sets/src/mage/cards/w/Windfall.java b/Mage.Sets/src/mage/cards/w/Windfall.java
index 955883d9d8..b7c641a9a4 100644
--- a/Mage.Sets/src/mage/cards/w/Windfall.java
+++ b/Mage.Sets/src/mage/cards/w/Windfall.java
@@ -1,10 +1,7 @@
-
 package mage.cards.w;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
@@ -12,14 +9,15 @@ import mage.constants.Outcome;
 import mage.game.Game;
 import mage.players.Player;
 
+import java.util.UUID;
+
 /**
- *
  * @author emerald000
  */
 public final class Windfall extends CardImpl {
 
     public Windfall(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{U}");
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}");
 
         // Each player discards their hand, then draws cards equal to the greatest number of cards a player discarded this way.
         this.getSpellAbility().addEffect(new WindfallEffect());
@@ -42,7 +40,7 @@ class WindfallEffect extends OneShotEffect {
         staticText = "Each player discards their hand, then draws cards equal to the greatest number of cards a player discarded this way.";
     }
 
-    WindfallEffect(final WindfallEffect effect) {
+    private WindfallEffect(final WindfallEffect effect) {
         super(effect);
     }
 
@@ -55,16 +53,12 @@ class WindfallEffect extends OneShotEffect {
         }
         for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
             Player player = game.getPlayer(playerId);
-            if (player != null) {
-                int discarded = 0;
-                for (Card c : player.getHand().getCards(game)) {
-                    if (player.discard(c, source, game)) {
-                        discarded++;
-                    }
-                }
-                if (discarded > maxDiscarded) {
-                    maxDiscarded = discarded;
-                }
+            if (player == null) {
+                continue;
+            }
+            int discarded = player.discard(player.getHand(), source, game).size();
+            if (discarded > maxDiscarded) {
+                maxDiscarded = discarded;
             }
         }
         for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
diff --git a/Mage.Sets/src/mage/cards/w/WitsEnd.java b/Mage.Sets/src/mage/cards/w/WitsEnd.java
index 3bb57d18e8..ff2e542b7d 100644
--- a/Mage.Sets/src/mage/cards/w/WitsEnd.java
+++ b/Mage.Sets/src/mage/cards/w/WitsEnd.java
@@ -1,11 +1,7 @@
-
 package mage.cards.w;
 
-import java.util.Set;
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
@@ -14,22 +10,22 @@ import mage.game.Game;
 import mage.players.Player;
 import mage.target.TargetPlayer;
 
+import java.util.UUID;
+
 /**
- *
  * @author North
  */
 public final class WitsEnd extends CardImpl {
 
     public WitsEnd(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{5}{B}{B}");
-
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{5}{B}{B}");
 
         // Target player discards their hand.
         this.getSpellAbility().addEffect(new WitsEndEffect());
         this.getSpellAbility().addTarget(new TargetPlayer());
     }
 
-    public WitsEnd(final WitsEnd card) {
+    private WitsEnd(final WitsEnd card) {
         super(card);
     }
 
@@ -41,12 +37,12 @@ public final class WitsEnd extends CardImpl {
 
 class WitsEndEffect extends OneShotEffect {
 
-    public WitsEndEffect() {
+    WitsEndEffect() {
         super(Outcome.Benefit);
         this.staticText = "Target player discards their hand";
     }
 
-    public WitsEndEffect(final WitsEndEffect effect) {
+    private WitsEndEffect(final WitsEndEffect effect) {
         super(effect);
     }
 
@@ -58,13 +54,10 @@ class WitsEndEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player player = game.getPlayer(source.getFirstTarget());
-        if (player != null) {
-            Set<Card> cards = player.getHand().getCards(game);
-            for (Card card : cards) {
-                player.discard(card, source, game);
-            }
-            return true;
+        if (player == null) {
+            return false;
         }
-        return false;
+        player.discard(player.getHand(), source, game);
+        return true;
     }
 }
diff --git a/Mage.Sets/src/mage/cards/w/WrenchMind.java b/Mage.Sets/src/mage/cards/w/WrenchMind.java
index ae5dbe64aa..552f2cfb16 100644
--- a/Mage.Sets/src/mage/cards/w/WrenchMind.java
+++ b/Mage.Sets/src/mage/cards/w/WrenchMind.java
@@ -1,7 +1,5 @@
-
 package mage.cards.w;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
 import mage.cards.Card;
@@ -9,28 +7,28 @@ import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.Outcome;
+import mage.filter.StaticFilters;
 import mage.game.Game;
 import mage.players.Player;
 import mage.target.TargetPlayer;
 import mage.target.common.TargetDiscard;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class WrenchMind extends CardImpl {
 
     public WrenchMind(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{B}{B}");
-
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{B}{B}");
 
         // Target player discards two cards unless they discard an artifact card.
         this.getSpellAbility().addTarget(new TargetPlayer());
         this.getSpellAbility().addEffect(new WrenchMindEffect());
-
     }
 
-    public WrenchMind(final WrenchMind card) {
+    private WrenchMind(final WrenchMind card) {
         super(card);
     }
 
@@ -42,12 +40,12 @@ public final class WrenchMind extends CardImpl {
 
 class WrenchMindEffect extends OneShotEffect {
 
-    public WrenchMindEffect() {
+    WrenchMindEffect() {
         super(Outcome.Discard);
         this.staticText = "Target player discards two cards unless they discard an artifact card";
     }
 
-    public WrenchMindEffect(final WrenchMindEffect effect) {
+    private WrenchMindEffect(final WrenchMindEffect effect) {
         super(effect);
     }
 
@@ -59,18 +57,19 @@ class WrenchMindEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player targetPlayer = game.getPlayer(this.getTargetPointer().getFirst(game, source));
-        if (targetPlayer != null && !targetPlayer.getHand().isEmpty()) {
-            TargetDiscard target = new TargetDiscard(targetPlayer.getId());
-            targetPlayer.choose(Outcome.Discard, target, source.getSourceId(), game);
-            Card card = targetPlayer.getHand().get(target.getFirstTarget(), game);
-            if (card != null) {
-                targetPlayer.discard(card, source, game);
-                if (!card.isArtifact() && !targetPlayer.getHand().isEmpty()) {
-                    targetPlayer.discard(1, false, source, game);
-                }
-                return true;
-            }            
+        if (targetPlayer == null || targetPlayer.getHand().isEmpty()) {
+            return false;
         }
-        return false;
+        if (targetPlayer.getHand().count(StaticFilters.FILTER_CARD_ARTIFACT, game) < 1
+                || !targetPlayer.chooseUse(Outcome.Benefit, "Discard an artifact card?", source, game)) {
+            return !targetPlayer.discard(2, false, source, game).isEmpty();
+        }
+        TargetDiscard target = new TargetDiscard(StaticFilters.FILTER_CARD_ARTIFACT_AN, targetPlayer.getId());
+        targetPlayer.choose(Outcome.Discard, target, source.getSourceId(), game);
+        Card card = targetPlayer.getHand().get(target.getFirstTarget(), game);
+        if (card != null && targetPlayer.discard(card, source, game)) {
+            return true;
+        }
+        return !targetPlayer.discard(2, false, source, game).isEmpty();
     }
 }
diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
index b7ac18de7b..34f9dbdc1a 100644
--- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
+++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
@@ -2550,6 +2550,11 @@ public class TestPlayer implements Player {
         return computerPlayer.discard(amount, random, source, game);
     }
 
+    @Override
+    public Cards discard(Cards cards, Ability source, Game game) {
+        return computerPlayer.discard(cards, source, game);
+    }
+
     @Override
     public boolean discard(Card card, Ability source, Game game) {
         return computerPlayer.discard(card, source, game);
diff --git a/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java b/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java
index c5bcdeb7de..ed5e1adbbc 100644
--- a/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java
+++ b/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java
@@ -1,7 +1,5 @@
 package org.mage.test.stub;
 
-import java.io.Serializable;
-import java.util.*;
 import mage.MageObject;
 import mage.MageObjectReference;
 import mage.abilities.*;
@@ -41,6 +39,9 @@ import mage.target.TargetAmount;
 import mage.target.TargetCard;
 import mage.target.common.TargetCardInLibrary;
 
+import java.io.Serializable;
+import java.util.*;
+
 /**
  * @author Quercitron
  */
@@ -656,6 +657,11 @@ public class PlayerStub implements Player {
         return 1;
     }
 
+    @Override
+    public Cards discard(Cards cards, Ability source, Game game) {
+        return null;
+    }
+
     @Override
     public Card discardOne(boolean random, Ability source, Game game) {
         return null;
diff --git a/Mage/src/main/java/mage/abilities/costs/common/DiscardHandCost.java b/Mage/src/main/java/mage/abilities/costs/common/DiscardHandCost.java
index 4023d942b4..32a76e3a79 100644
--- a/Mage/src/main/java/mage/abilities/costs/common/DiscardHandCost.java
+++ b/Mage/src/main/java/mage/abilities/costs/common/DiscardHandCost.java
@@ -1,27 +1,23 @@
-
-
 package mage.abilities.costs.common;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.costs.Cost;
 import mage.abilities.costs.CostImpl;
-import mage.cards.Card;
 import mage.game.Game;
 import mage.players.Player;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 
 public class DiscardHandCost extends CostImpl {
 
     public DiscardHandCost() {
-    
     }
 
-    public DiscardHandCost(final DiscardHandCost cost) {
+    private DiscardHandCost(final DiscardHandCost cost) {
         super(cost);
     }
 
@@ -38,12 +34,11 @@ public class DiscardHandCost extends CostImpl {
     @Override
     public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) {
         Player player = game.getPlayer(controllerId);
-        if (player != null) {
-            for (Card card : player.getHand().getCards(game)) {
-                player.discard(card, ability, game);
-            }
-            paid = true;
+        if (player == null) {
+            return paid;
         }
+        player.discard(player.getHand(), ability, game);
+        paid = true;
         return paid;
     }
 
diff --git a/Mage/src/main/java/mage/abilities/costs/common/DiscardTargetCost.java b/Mage/src/main/java/mage/abilities/costs/common/DiscardTargetCost.java
index ff190e15cb..6c786f6ff1 100644
--- a/Mage/src/main/java/mage/abilities/costs/common/DiscardTargetCost.java
+++ b/Mage/src/main/java/mage/abilities/costs/common/DiscardTargetCost.java
@@ -1,20 +1,21 @@
-
 package mage.abilities.costs.common;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.costs.Cost;
 import mage.abilities.costs.CostImpl;
 import mage.cards.Card;
+import mage.cards.Cards;
+import mage.cards.CardsImpl;
 import mage.constants.Outcome;
 import mage.game.Game;
 import mage.players.Player;
 import mage.target.common.TargetCardInHand;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
 /**
- *
  * @author BetaSteward_at_googlemail.com
  */
 public class DiscardTargetCost extends CostImpl {
@@ -50,13 +51,11 @@ public class DiscardTargetCost extends CostImpl {
         if (randomDiscard) {
             this.cards.addAll(player.discard(amount, true, ability, game).getCards(game));
         } else if (targets.choose(Outcome.Discard, controllerId, sourceId, game)) {
-            for (UUID targetId : targets.get(0).getTargets()) {
-                Card card = player.getHand().get(targetId, game);
-                if (card == null) {
-                    return false;
-                }
-                player.discard(card, ability, game);
-                this.cards.add(card);
+            Cards toDiscard = new CardsImpl();
+            toDiscard.addAll(targets.get(0).getTargets());
+            Cards discarded = player.discard(toDiscard, ability, game);
+            if (!discarded.isEmpty()) {
+                cards.addAll(discarded.getCards(game));
             }
         }
         paid = cards.size() >= amount;
diff --git a/Mage/src/main/java/mage/abilities/effects/common/BalanceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/BalanceEffect.java
new file mode 100644
index 0000000000..c81187c4ad
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/effects/common/BalanceEffect.java
@@ -0,0 +1,168 @@
+package mage.abilities.effects.common;
+
+import mage.abilities.Ability;
+import mage.abilities.effects.OneShotEffect;
+import mage.cards.Card;
+import mage.cards.Cards;
+import mage.cards.CardsImpl;
+import mage.constants.Outcome;
+import mage.filter.FilterCard;
+import mage.filter.StaticFilters;
+import mage.filter.common.FilterControlledCreaturePermanent;
+import mage.filter.common.FilterControlledLandPermanent;
+import mage.filter.common.FilterControlledPermanent;
+import mage.game.Game;
+import mage.game.permanent.Permanent;
+import mage.players.Player;
+import mage.target.common.TargetCardInHand;
+import mage.target.common.TargetControlledPermanent;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * @author emerald000
+ */
+public class BalanceEffect extends OneShotEffect {
+
+    private static final FilterControlledPermanent filter = new FilterControlledLandPermanent("lands to keep");
+    private static final FilterControlledPermanent filter2 = new FilterControlledCreaturePermanent("creatures to keep");
+    private static final FilterCard filter3 = new FilterCard("cards to keep");
+
+    public BalanceEffect() {
+        super(Outcome.Sacrifice);
+        staticText = "each player chooses a number of lands they control "
+                + "equal to the number of lands controlled by the player "
+                + "who controls the fewest, then sacrifices the rest. "
+                + "Players discard cards and sacrifice creatures the same way";
+    }
+
+    private BalanceEffect(final BalanceEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public BalanceEffect copy() {
+        return new BalanceEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Player controller = game.getPlayer(source.getControllerId());
+        if (controller == null) {
+            return false;
+        }
+        //Lands
+        int minLand = Integer.MAX_VALUE;
+        Cards landsToSacrifice = new CardsImpl();
+        for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
+            Player player = game.getPlayer(playerId);
+            if (player == null) {
+                continue;
+            }
+            int count = game.getBattlefield().countAll(StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND, player.getId(), game);
+            if (count < minLand) {
+                minLand = count;
+            }
+        }
+
+        for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
+            Player player = game.getPlayer(playerId);
+            if (player == null) {
+                continue;
+            }
+            TargetControlledPermanent target = new TargetControlledPermanent(minLand, minLand, filter, true);
+            if (!target.choose(Outcome.Sacrifice, player.getId(), source.getSourceId(), game)) {
+                continue;
+            }
+            for (Permanent permanent : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND, player.getId(), source.getSourceId(), game)) {
+                if (permanent != null && !target.getTargets().contains(permanent.getId())) {
+                    landsToSacrifice.add(permanent);
+                }
+            }
+        }
+
+        for (UUID cardId : landsToSacrifice) {
+            Permanent permanent = game.getPermanent(cardId);
+            if (permanent != null) {
+                permanent.sacrifice(source.getSourceId(), game);
+            }
+        }
+
+        //Creatures
+        int minCreature = Integer.MAX_VALUE;
+        Cards creaturesToSacrifice = new CardsImpl();
+        for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
+            Player player = game.getPlayer(playerId);
+            if (player == null) {
+            }
+            int count = game.getBattlefield().countAll(StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND, player.getId(), game);
+            if (count < minCreature) {
+                minCreature = count;
+            }
+        }
+
+        for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
+            Player player = game.getPlayer(playerId);
+            if (player == null) {
+                continue;
+            }
+            TargetControlledPermanent target = new TargetControlledPermanent(minCreature, minCreature, filter2, true);
+            if (!target.choose(Outcome.Sacrifice, player.getId(), source.getSourceId(), game)) {
+                continue;
+            }
+            for (Permanent permanent : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_CONTROLLED_CREATURE, player.getId(), source.getSourceId(), game)) {
+                if (permanent != null && !target.getTargets().contains(permanent.getId())) {
+                    creaturesToSacrifice.add(permanent);
+                }
+            }
+        }
+
+        for (UUID cardId : creaturesToSacrifice) {
+            Permanent permanent = game.getPermanent(cardId);
+            if (permanent != null) {
+                permanent.sacrifice(source.getSourceId(), game);
+            }
+        }
+
+        //Cards in hand
+        int minCard = Integer.MAX_VALUE;
+        Map<UUID, Cards> cardsToDiscard = new HashMap<>(2);
+        for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
+            Player player = game.getPlayer(playerId);
+            if (player == null) {
+            }
+            int count = player.getHand().size();
+            if (count < minCard) {
+                minCard = count;
+            }
+        }
+
+        for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
+            Player player = game.getPlayer(playerId);
+            if (player == null) {
+                continue;
+            }
+            Cards cards = new CardsImpl();
+            TargetCardInHand target = new TargetCardInHand(minCard, filter3);
+            if (!target.choose(Outcome.Discard, player.getId(), source.getSourceId(), game)) {
+                continue;
+            }
+            for (Card card : player.getHand().getCards(game)) {
+                if (card != null && !target.getTargets().contains(card.getId())) {
+                    cards.add(card);
+                }
+            }
+            cardsToDiscard.put(playerId, cards);
+        }
+
+        for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
+            Player player = game.getPlayer(playerId);
+            if (player != null && cardsToDiscard.get(playerId) != null) {
+                player.discard(cardsToDiscard.get(playerId), source, game);
+            }
+        }
+        return true;
+    }
+}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardCardYouChooseTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardCardYouChooseTargetEffect.java
index d48b78dc8e..403fde557a 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardCardYouChooseTargetEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardCardYouChooseTargetEffect.java
@@ -144,12 +144,7 @@ public class DiscardCardYouChooseTargetEffect extends OneShotEffect {
         if (!controller.choose(Outcome.Benefit, revealedCards, target, game)) {
             return result;
         }
-        for (UUID targetId : target.getTargets()) {
-            Card card = revealedCards.get(targetId, game);
-            if (!player.discard(card, source, game)) {
-                result = false;
-            }
-        }
+        result=!player.discard(new CardsImpl(target.getTargets()),source,game).isEmpty();
         return result;
     }
 
diff --git a/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardControllerEffect.java
index 278a4747c8..88d1f95ade 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardControllerEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardControllerEffect.java
@@ -1,11 +1,9 @@
-
 package mage.abilities.effects.common.discard;
 
 import mage.abilities.Ability;
 import mage.abilities.dynamicvalue.DynamicValue;
 import mage.abilities.dynamicvalue.common.StaticValue;
 import mage.abilities.effects.OneShotEffect;
-import mage.cards.Card;
 import mage.constants.Outcome;
 import mage.game.Game;
 import mage.players.Player;
@@ -16,8 +14,8 @@ import mage.util.CardUtil;
  */
 public class DiscardControllerEffect extends OneShotEffect {
 
-    protected DynamicValue amount;
-    protected boolean randomDiscard;
+    protected final DynamicValue amount;
+    protected final boolean randomDiscard;
 
     public DiscardControllerEffect(int amount) {
         this(StaticValue.get(amount));
@@ -51,22 +49,8 @@ public class DiscardControllerEffect extends OneShotEffect {
 
     @Override
     public boolean apply(Game game, Ability source) {
-        boolean result = false;
         Player player = game.getPlayer(source.getControllerId());
-        if (player != null) {
-            if (randomDiscard) {
-                int maxAmount = Math.min(amount.calculate(game, source, this), player.getHand().size());
-                for (int i = 0; i < maxAmount; i++) {
-                    Card card = player.getHand().getRandom(game);
-                    result |= player.discard(card, source, game);
-
-                }
-            } else {
-                player.discard(amount.calculate(game, source, this), false, source, game);
-                result = true;
-            }
-        }
-        return result;
+        return player != null && !player.discard(amount.calculate(game, source, this), randomDiscard, source, game).isEmpty();
     }
 
     private void setText() {
diff --git a/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardEachPlayerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardEachPlayerEffect.java
index fba1fef2f9..92bf387644 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardEachPlayerEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardEachPlayerEffect.java
@@ -5,12 +5,11 @@ import mage.abilities.Mode;
 import mage.abilities.dynamicvalue.DynamicValue;
 import mage.abilities.dynamicvalue.common.StaticValue;
 import mage.abilities.effects.OneShotEffect;
-import mage.cards.Card;
 import mage.cards.Cards;
 import mage.cards.CardsImpl;
 import mage.constants.Outcome;
 import mage.constants.TargetController;
-import mage.filter.FilterCard;
+import mage.filter.StaticFilters;
 import mage.game.Game;
 import mage.players.Player;
 import mage.target.Target;
@@ -62,54 +61,50 @@ public class DiscardEachPlayerEffect extends OneShotEffect {
         Player controller = game.getPlayer(source.getControllerId());
         // Store for each player the cards to discard, that's important because all discard shall happen at the same time
         Map<UUID, Cards> cardsToDiscard = new HashMap<>();
-        if (controller != null) {
-            // choose cards to discard
-            for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
-                Player player = game.getPlayer(playerId);
-                if (player != null) {
-                    switch (targetController) {
-                        case NOT_YOU:
-                            if (playerId.equals(source.getControllerId())) {
-                                continue;
-                            }
-                            break;
-                        case OPPONENT:
-                            if (!game.getOpponents(source.getControllerId()).contains(playerId)) {
-                                continue;
-                            }
-                            break;
-                    }
-                    int numberOfCardsToDiscard = Math.min(amount.calculate(game, source, this), player.getHand().size());
-                    Cards cards = new CardsImpl();
-                    if (randomDiscard) {
-                        while (player.isInGame() && cards.size() < numberOfCardsToDiscard) {
-                            Card card = player.getHand().getRandom(game);
-                            if (!cards.contains(card.getId())) {
-                                cards.add(card);
-                            }
-                        }
-                    } else {
-                        Target target = new TargetDiscard(numberOfCardsToDiscard, numberOfCardsToDiscard, new FilterCard(), playerId);
-                        player.chooseTarget(outcome, target, source, game);
-                        cards.addAll(target.getTargets());
-                    }
-                    cardsToDiscard.put(playerId, cards);
-                }
+        if (controller == null) {
+            return true;
+        }
+        int toDiscard = amount.calculate(game, source, this);
+        // choose cards to discard
+        for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
+            Player player = game.getPlayer(playerId);
+            if (player == null) {
+                continue;
             }
-            // discard all choosen cards
-            for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
-                Player player = game.getPlayer(playerId);
-                if (player != null) {
-                    Cards cardsPlayer = cardsToDiscard.get(playerId);
-                    if (cardsPlayer != null) {
-                        for (UUID cardId : cardsPlayer) {
-                            Card card = game.getCard(cardId);
-                            player.discard(card, source, game);
-
-                        }
+            switch (targetController) {
+                case NOT_YOU:
+                    if (playerId.equals(source.getControllerId())) {
+                        continue;
                     }
-                }
+                    break;
+                case OPPONENT:
+                    if (!game.getOpponents(source.getControllerId()).contains(playerId)) {
+                        continue;
+                    }
+                    break;
             }
+            if (randomDiscard) {
+                player.discard(toDiscard, true, source, game);
+                continue;
+            }
+            int numberOfCardsToDiscard = Math.min(toDiscard, player.getHand().size());
+            Cards cards = new CardsImpl();
+            Target target = new TargetDiscard(numberOfCardsToDiscard, numberOfCardsToDiscard, StaticFilters.FILTER_CARD, playerId);
+            player.chooseTarget(outcome, target, source, game);
+            cards.addAll(target.getTargets());
+            cardsToDiscard.put(playerId, cards);
+        }
+        if (randomDiscard) {
+            return true;
+        }
+        // discard all choosen cards
+        for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
+            Player player = game.getPlayer(playerId);
+            if (player == null) {
+                continue;
+            }
+            Cards cardsPlayer = cardsToDiscard.get(playerId);
+            player.discard(cardsPlayer, source, game);
         }
         return true;
     }
diff --git a/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardHandAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardHandAllEffect.java
index d6cb2f4eb7..60cc0bd342 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardHandAllEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardHandAllEffect.java
@@ -2,12 +2,10 @@ package mage.abilities.effects.common.discard;
 
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
-import mage.cards.Card;
 import mage.constants.Outcome;
 import mage.game.Game;
 import mage.players.Player;
 
-import java.util.Set;
 import java.util.UUID;
 
 /**
@@ -33,12 +31,10 @@ public class DiscardHandAllEffect extends OneShotEffect {
     public boolean apply(Game game, Ability source) {
         for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) {
             Player player = game.getPlayer(playerId);
-            if (player != null) {
-                Set<Card> cards = player.getHand().getCards(game);
-                for (Card card : cards) {
-                    player.discard(card, source, game);
-                }
+            if (player == null) {
+                continue;
             }
+            player.discard(player.getHand(), source, game);
         }
         return true;
     }
diff --git a/Mage/src/main/java/mage/players/Player.java b/Mage/src/main/java/mage/players/Player.java
index 2128323f2c..0e7c610903 100644
--- a/Mage/src/main/java/mage/players/Player.java
+++ b/Mage/src/main/java/mage/players/Player.java
@@ -412,6 +412,8 @@ public interface Player extends MageItem, Copyable<Player> {
 
     Cards discard(int amount, boolean random, Ability source, Game game);
 
+    Cards discard(Cards cards, Ability source, Game game);
+
     void discardToMax(Game game);
 
     boolean discard(Card card, Ability source, Game game);
diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java
index 1bc9a3b339..f7ff8422b1 100644
--- a/Mage/src/main/java/mage/players/PlayerImpl.java
+++ b/Mage/src/main/java/mage/players/PlayerImpl.java
@@ -718,6 +718,24 @@ public abstract class PlayerImpl implements Player, Serializable {
         return discardedCards;
     }
 
+    @Override
+    public Cards discard(Cards cards, Ability source, Game game) {
+        Cards discardedCards = new CardsImpl();
+        for (Card card : cards.getCards(game)) {
+            if (doDiscard(card, source, game, false)) {
+                discardedCards.add(card);
+            }
+        }
+        if (!discardedCards.isEmpty()) {
+            UUID sourceId = source == null ? null : source.getSourceId();
+            game.fireEvent(GameEvent.getEvent(
+                    GameEvent.EventType.DISCARDED_CARDS, sourceId,
+                    sourceId, playerId, discardedCards.size()
+            ));
+        }
+        return discardedCards;
+    }
+
     private Cards doDiscard(int amount, boolean random, Ability source, Game game) {
         Cards discardedCards = new CardsImpl();
         if (amount <= 0) {

From 138fcf6c92ea68d059ec53c10303fefc200c952d Mon Sep 17 00:00:00 2001
From: SpeedProg <speedprogde@googlemail.com>
Date: Sun, 3 May 2020 18:32:56 +0200
Subject: [PATCH 010/586] Fixing Icon of Ancestry ability

---
 .../src/mage/cards/i/IconOfAncestry.java      | 65 ++++---------------
 1 file changed, 12 insertions(+), 53 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/i/IconOfAncestry.java b/Mage.Sets/src/mage/cards/i/IconOfAncestry.java
index a04801cda9..19196fd230 100644
--- a/Mage.Sets/src/mage/cards/i/IconOfAncestry.java
+++ b/Mage.Sets/src/mage/cards/i/IconOfAncestry.java
@@ -1,12 +1,12 @@
 package mage.cards.i;
 
-import java.util.Set;
 import mage.abilities.Ability;
 import mage.abilities.common.AsEntersBattlefieldAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.costs.common.TapSourceCost;
 import mage.abilities.effects.common.ChooseCreatureTypeEffect;
+import mage.abilities.effects.common.LookLibraryAndPickControllerEffect;
 import mage.abilities.effects.common.continuous.BoostAllEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -17,13 +17,8 @@ import mage.filter.predicate.mageobject.ChosenSubtypePredicate;
 
 import java.util.UUID;
 import mage.abilities.costs.mana.ManaCostsImpl;
-import mage.abilities.effects.OneShotEffect;
-import mage.cards.Card;
-import mage.cards.Cards;
-import mage.cards.CardsImpl;
+import mage.abilities.dynamicvalue.common.StaticValue;
 import mage.game.Game;
-import mage.players.Player;
-import mage.target.TargetCard;
 
 /**
  * @author TheElk801
@@ -51,7 +46,8 @@ public final class IconOfAncestry extends CardImpl {
 
         // {3}, {T}: Look at the top three cards of your library. You may reveal a creature card of the 
         // chosen type from among them and put it into your hand. Put the rest on the bottom of your library in a random order.
-        Ability ability = new SimpleActivatedAbility(new IconOfAncestryEffect(), new ManaCostsImpl("{3}"));
+        FilterCreatureCard filter2 = new FilterCreatureCard("creature card that matches the chosen subtype");
+        Ability ability = new SimpleActivatedAbility(new IconOfAncestryEffect(filter2), new ManaCostsImpl("{3}"));
         ability.addCost(new TapSourceCost());
         this.addAbility(ability);
     }
@@ -66,57 +62,20 @@ public final class IconOfAncestry extends CardImpl {
     }
 }
 
-class IconOfAncestryEffect extends OneShotEffect {
-
-    public IconOfAncestryEffect() {
-        super(Outcome.AIDontUseIt);
-        this.staticText = "Look at the top three cards of your library. "
-                + "You may reveal a creature card of the "
-                + "chosen type from among them and put it into your hand. "
-                + "Put the rest on the bottom of your library in a random order";
-    }
-
-    public IconOfAncestryEffect(final IconOfAncestryEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public IconOfAncestryEffect copy() {
-        return new IconOfAncestryEffect(this);
+class IconOfAncestryEffect extends LookLibraryAndPickControllerEffect {
+    public IconOfAncestryEffect(FilterCreatureCard filter) {
+        super(StaticValue.get(3), false, StaticValue.get(1), filter, Zone.LIBRARY, false,
+                true, false, Zone.HAND, true, false, false);
+        this.setOutcome(Outcome.AIDontUseIt);
+        this.setBackInRandomOrder(true);
     }
 
     @Override
     public boolean apply(Game game, Ability source) {
-        Player controller = game.getPlayer(source.getControllerId());
-        if (controller == null
-                || !controller.getLibrary().hasCards()) {
-            return false;
-        }
-        Set<Card> cardsFromTopOfLibrary = controller.getLibrary().getTopCards(game, 3);
-        Cards cardsFromLibrary = new CardsImpl();
-        Cards revealedCard = new CardsImpl();
-        cardsFromLibrary.addAll(cardsFromTopOfLibrary);
-        if (cardsFromTopOfLibrary.isEmpty()) {
-            return false;
-        }
-        FilterCreatureCard filter = new FilterCreatureCard("creature card that matches the chosen subtype");
         SubType subtype = (SubType) game.getState().getValue(source.getSourceId() + "_type");
         if (subtype != null) {
             filter.add(subtype.getPredicate());
         }
-        TargetCard target = new TargetCard(Zone.LIBRARY, filter);
-        if (target.canChoose(controller.getId(), game)
-                && controller.chooseUse(outcome, "Do you wish to use Icon of Ancestry's effect?", source, game)
-                && controller.choose(Outcome.Benefit, cardsFromLibrary, target, game)) {
-            Card chosenCard = game.getCard(target.getFirstTarget());
-            if (chosenCard != null) {
-                revealedCard.add(chosenCard);
-                controller.revealCards(source, revealedCard, game);
-                controller.putInHand(chosenCard, game);
-                cardsFromLibrary.remove(chosenCard);
-            }
-        }
-        controller.putCardsOnBottomOfLibrary(cardsFromLibrary, game, source, true);
-        return true;
+        return super.apply(game, source);
     }
-}
+}
\ No newline at end of file

From 3465493ccf0df6c7a7fa7abb8ac943259391d14e Mon Sep 17 00:00:00 2001
From: emerald000 <emerald000@hotmail.com>
Date: Sun, 3 May 2020 18:56:35 -0400
Subject: [PATCH 011/586] Rename, move and comment resolving ability event
 creation. Should be clearer now.

---
 Mage/src/main/java/mage/abilities/AbilityImpl.java             | 3 +++
 Mage/src/main/java/mage/game/events/GameEvent.java             | 2 +-
 Mage/src/main/java/mage/game/stack/StackAbility.java           | 1 -
 .../main/java/mage/watchers/common/AbilityResolvedWatcher.java | 2 +-
 4 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/Mage/src/main/java/mage/abilities/AbilityImpl.java b/Mage/src/main/java/mage/abilities/AbilityImpl.java
index 361a6c4ec4..6edcbfc98a 100644
--- a/Mage/src/main/java/mage/abilities/AbilityImpl.java
+++ b/Mage/src/main/java/mage/abilities/AbilityImpl.java
@@ -152,6 +152,9 @@ public abstract class AbilityImpl implements Ability {
         boolean result = true;
         //20100716 - 117.12
         if (checkIfClause(game)) {
+            // Ability has started resolving. Fire event.
+            // Used for abilities counting the number of resolutions like Ashling the Pilgrim.
+            game.fireEvent(new GameEvent(GameEvent.EventType.RESOLVING_ABILITY, this.getOriginalId(), this.getSourceId(), this.getControllerId()));
             if (this instanceof TriggeredAbility) {
                 for (UUID modeId : this.getModes().getSelectedModes()) {
                     this.getModes().setActiveMode(modeId);
diff --git a/Mage/src/main/java/mage/game/events/GameEvent.java b/Mage/src/main/java/mage/game/events/GameEvent.java
index 171da25007..7c467e3b40 100644
--- a/Mage/src/main/java/mage/game/events/GameEvent.java
+++ b/Mage/src/main/java/mage/game/events/GameEvent.java
@@ -150,7 +150,7 @@ public class GameEvent implements Serializable {
         SPELL_CAST,
         ACTIVATE_ABILITY, ACTIVATED_ABILITY,
         TRIGGERED_ABILITY,
-        RESOLVED_ABILITY,
+        RESOLVING_ABILITY,
         COPY_STACKOBJECT, COPIED_STACKOBJECT,
         /* ADD_MANA
          targetId    id of the ability that added the mana
diff --git a/Mage/src/main/java/mage/game/stack/StackAbility.java b/Mage/src/main/java/mage/game/stack/StackAbility.java
index b35a664bb2..cb4149bcfe 100644
--- a/Mage/src/main/java/mage/game/stack/StackAbility.java
+++ b/Mage/src/main/java/mage/game/stack/StackAbility.java
@@ -82,7 +82,6 @@ public class StackAbility extends StackObjImpl implements Ability {
     @Override
     public boolean resolve(Game game) {
         if (ability.getTargets().stillLegal(ability, game) || !canFizzle()) {
-            game.fireEvent(new GameEvent(GameEvent.EventType.RESOLVED_ABILITY, ability.getOriginalId(), ability.getSourceId(), ability.getControllerId()));
             boolean result = ability.resolve(game);
             game.getStack().remove(this, game);
             return result;
diff --git a/Mage/src/main/java/mage/watchers/common/AbilityResolvedWatcher.java b/Mage/src/main/java/mage/watchers/common/AbilityResolvedWatcher.java
index dfa3d5daab..953e4d64df 100644
--- a/Mage/src/main/java/mage/watchers/common/AbilityResolvedWatcher.java
+++ b/Mage/src/main/java/mage/watchers/common/AbilityResolvedWatcher.java
@@ -22,7 +22,7 @@ public class AbilityResolvedWatcher extends Watcher {
 
     @Override
     public void watch(GameEvent event, Game game) {
-        if (event.getType() == GameEvent.EventType.RESOLVED_ABILITY) {
+        if (event.getType() == GameEvent.EventType.RESOLVING_ABILITY) {
             resolutionMap.merge(event.getTargetId().toString() + game.getState().getZoneChangeCounter(event.getSourceId()), 1, Integer::sum);
         }
     }

From bde65d62799f134ba7fde9a647a7aa318dd987e4 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 4 May 2020 20:51:38 -0400
Subject: [PATCH 012/586] Created class for reflexive triggered abilities
 (Ready for review) (#6500)

* added class for reflexive triggered abilities

* added DoWhenCostPaid

* a few more refactors

* some more refactoring

* almost all refactors done

* finished refactoring

* updated text generation

* Delete SendOptionUsedEventEffect.java

* fixed Wildborn Preserver text
---
 Mage.Sets/src/mage/cards/b/BackForMore.java   |  65 +++-------
 .../src/mage/cards/c/CabalTherapist.java      |  91 +++-----------
 .../src/mage/cards/c/CavalierOfNight.java     |  79 ++----------
 Mage.Sets/src/mage/cards/d/DreamEater.java    |  82 ++++---------
 .../src/mage/cards/e/EscapeProtocol.java      |  98 +++------------
 .../src/mage/cards/f/FirebladeArtist.java     |  84 ++-----------
 .../src/mage/cards/f/FlaxenIntruder.java      |  76 ++----------
 .../mage/cards/h/HeartPiercerManticore.java   |  95 +++++----------
 Mage.Sets/src/mage/cards/h/Hypothesizzle.java |  90 +++-----------
 .../src/mage/cards/i/IsarethTheAwakener.java  | 100 +++++----------
 .../src/mage/cards/l/LavabrinkFloodgates.java |  51 ++------
 .../mage/cards/n/NarsetOfTheAncientWay.java   |  52 ++------
 .../src/mage/cards/s/SavaiThundermane.java    |  80 ++----------
 .../src/mage/cards/s/SkyriderPatrol.java      | 114 ++++-------------
 .../mage/cards/s/SorinImperiousBloodlord.java |  78 +++---------
 .../src/mage/cards/s/SparktongueDragon.java   |  91 +++-----------
 .../src/mage/cards/t/TheRoyalScions.java      |  44 ++-----
 .../src/mage/cards/v/ViviensInvocation.java   | 115 ++++++------------
 .../src/mage/cards/w/WildbornPreserver.java   |  44 +------
 .../cards/y/YannikScavengingSentinel.java     |  48 ++------
 .../delayed/ReflexiveTriggeredAbility.java    |  50 ++++++++
 .../effects/common/DoWhenCostPaid.java        | 103 ++++++++++++++++
 .../common/SendOptionUsedEventEffect.java     |  42 -------
 Mage/src/main/java/mage/game/Game.java        |   3 +
 Mage/src/main/java/mage/game/GameImpl.java    |   8 ++
 25 files changed, 505 insertions(+), 1278 deletions(-)
 create mode 100644 Mage/src/main/java/mage/abilities/common/delayed/ReflexiveTriggeredAbility.java
 create mode 100644 Mage/src/main/java/mage/abilities/effects/common/DoWhenCostPaid.java
 delete mode 100644 Mage/src/main/java/mage/abilities/effects/common/SendOptionUsedEventEffect.java

diff --git a/Mage.Sets/src/mage/cards/b/BackForMore.java b/Mage.Sets/src/mage/cards/b/BackForMore.java
index 3ef46f510e..26ed102c20 100644
--- a/Mage.Sets/src/mage/cards/b/BackForMore.java
+++ b/Mage.Sets/src/mage/cards/b/BackForMore.java
@@ -2,18 +2,19 @@ package mage.cards.b;
 
 import mage.MageObjectReference;
 import mage.abilities.Ability;
-import mage.abilities.DelayedTriggeredAbility;
+import mage.abilities.common.delayed.ReflexiveTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
-import mage.abilities.effects.common.SendOptionUsedEventEffect;
 import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.*;
+import mage.constants.CardType;
+import mage.constants.Outcome;
+import mage.constants.TargetController;
+import mage.constants.Zone;
 import mage.filter.FilterPermanent;
 import mage.filter.StaticFilters;
 import mage.filter.common.FilterCreaturePermanent;
 import mage.game.Game;
-import mage.game.events.GameEvent;
 import mage.game.permanent.Permanent;
 import mage.players.Player;
 import mage.target.TargetPermanent;
@@ -48,6 +49,12 @@ public final class BackForMore extends CardImpl {
 
 class BackForMoreEffect extends OneShotEffect {
 
+    private static final FilterPermanent filter = new FilterCreaturePermanent("creature you don't control");
+
+    static {
+        filter.add(TargetController.NOT_YOU.getControllerPredicate());
+    }
+
     BackForMoreEffect() {
         super(Outcome.Benefit);
         staticText = "Return target creature card from your graveyard to the battlefield. " +
@@ -75,49 +82,13 @@ class BackForMoreEffect extends OneShotEffect {
         if (permanent == null) {
             return false;
         }
-        game.addDelayedTriggeredAbility(new BackForMoreReflexiveTriggeredAbility(
-                new MageObjectReference(permanent, game)
-        ), source);
-        return new SendOptionUsedEventEffect().apply(game, source);
-    }
-}
-
-class BackForMoreReflexiveTriggeredAbility extends DelayedTriggeredAbility {
-
-    private static final FilterPermanent filter = new FilterCreaturePermanent("creature you don't control");
-
-    static {
-        filter.add(TargetController.NOT_YOU.getControllerPredicate());
-    }
-
-    BackForMoreReflexiveTriggeredAbility(MageObjectReference mor) {
-        super(new BackForMoreDamageEffect(mor), Duration.OneUse, false);
-        this.addTarget(new TargetPermanent(0, 1, filter, false));
-    }
-
-    private BackForMoreReflexiveTriggeredAbility(final BackForMoreReflexiveTriggeredAbility ability) {
-        super(ability);
-    }
-
-    @Override
-    public BackForMoreReflexiveTriggeredAbility copy() {
-        return new BackForMoreReflexiveTriggeredAbility(this);
-    }
-
-    @Override
-    public boolean checkEventType(GameEvent event, Game game) {
-        return event.getType() == GameEvent.EventType.OPTION_USED;
-    }
-
-    @Override
-    public boolean checkTrigger(GameEvent event, Game game) {
-        return event.getPlayerId().equals(this.getControllerId())
-                && event.getSourceId().equals(this.getSourceId());
-    }
-
-    @Override
-    public String getRule() {
-        return "When you do, it fights up to one target creature you don't control.";
+        ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility(
+                new BackForMoreDamageEffect(new MageObjectReference(permanent, game)),
+                false, "it fights up to one target creature you don't control"
+        );
+        ability.addTarget(new TargetPermanent(0, 1, filter, false));
+        game.fireReflexiveTriggeredAbility(ability, source);
+        return true;
     }
 }
 
diff --git a/Mage.Sets/src/mage/cards/c/CabalTherapist.java b/Mage.Sets/src/mage/cards/c/CabalTherapist.java
index 2a6a3ac881..2ec2d13f0f 100644
--- a/Mage.Sets/src/mage/cards/c/CabalTherapist.java
+++ b/Mage.Sets/src/mage/cards/c/CabalTherapist.java
@@ -3,19 +3,20 @@ package mage.cards.c;
 import mage.MageInt;
 import mage.MageObject;
 import mage.abilities.Ability;
-import mage.abilities.DelayedTriggeredAbility;
 import mage.abilities.common.BeginningOfPreCombatMainTriggeredAbility;
+import mage.abilities.common.delayed.ReflexiveTriggeredAbility;
 import mage.abilities.costs.common.SacrificeTargetCost;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.ChooseACardNameEffect;
-import mage.abilities.effects.common.DoIfCostPaid;
-import mage.abilities.effects.common.SendOptionUsedEventEffect;
+import mage.abilities.effects.common.DoWhenCostPaid;
 import mage.abilities.keyword.MenaceAbility;
 import mage.cards.*;
-import mage.constants.*;
+import mage.constants.CardType;
+import mage.constants.Outcome;
+import mage.constants.SubType;
+import mage.constants.TargetController;
 import mage.filter.StaticFilters;
 import mage.game.Game;
-import mage.game.events.GameEvent;
 import mage.players.Player;
 import mage.target.TargetPlayer;
 import mage.target.common.TargetControlledPermanent;
@@ -39,16 +40,17 @@ public final class CabalTherapist extends CardImpl {
         this.addAbility(new MenaceAbility());
 
         // At the beginning of your precombat main phase, you may sacrifice a creature. When you do, choose a nonland card name, then target player reveals their hand and discards all cards with that name.
+        ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility(
+                new ChooseACardNameEffect(ChooseACardNameEffect.TypeOfName.NON_LAND_NAME),
+                false, "choose a nonland card name, then target player " +
+                "reveals their hand and discards all cards with that name."
+        );
+        ability.addEffect(new CabalTherapistDiscardEffect());
+        ability.addTarget(new TargetPlayer());
         this.addAbility(new BeginningOfPreCombatMainTriggeredAbility(
-                new DoIfCostPaid(
-                        new CabalTherapistCreateReflexiveTriggerEffect(),
-                        new SacrificeTargetCost(new TargetControlledPermanent(
-                                StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT
-                        )), "Sacrifice a creature?"
-                ).setText("you may sacrifice a creature. When you do, " +
-                        "choose a nonland card name, then target player reveals their hand " +
-                        "and discards all cards with that name."),
-                TargetController.YOU, false
+                new DoWhenCostPaid(ability, new SacrificeTargetCost(
+                        new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT)
+                ), "Sacrifice a creature?"), TargetController.YOU, false
         ));
     }
 
@@ -62,62 +64,6 @@ public final class CabalTherapist extends CardImpl {
     }
 }
 
-class CabalTherapistCreateReflexiveTriggerEffect extends OneShotEffect {
-
-    CabalTherapistCreateReflexiveTriggerEffect() {
-        super(Outcome.Benefit);
-    }
-
-    private CabalTherapistCreateReflexiveTriggerEffect(final CabalTherapistCreateReflexiveTriggerEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public CabalTherapistCreateReflexiveTriggerEffect copy() {
-        return new CabalTherapistCreateReflexiveTriggerEffect(this);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        game.addDelayedTriggeredAbility(new CabalTherapistReflexiveTriggeredAbility(), source);
-        return new SendOptionUsedEventEffect().apply(game, source);
-    }
-}
-
-class CabalTherapistReflexiveTriggeredAbility extends DelayedTriggeredAbility {
-
-    CabalTherapistReflexiveTriggeredAbility() {
-        super(new ChooseACardNameEffect(ChooseACardNameEffect.TypeOfName.NON_LAND_NAME), Duration.OneUse, true);
-        this.addEffect(new CabalTherapistDiscardEffect());
-        this.addTarget(new TargetPlayer());
-    }
-
-    private CabalTherapistReflexiveTriggeredAbility(final CabalTherapistReflexiveTriggeredAbility ability) {
-        super(ability);
-    }
-
-    @Override
-    public CabalTherapistReflexiveTriggeredAbility copy() {
-        return new CabalTherapistReflexiveTriggeredAbility(this);
-    }
-
-    @Override
-    public boolean checkEventType(GameEvent event, Game game) {
-        return event.getType() == GameEvent.EventType.OPTION_USED;
-    }
-
-    @Override
-    public boolean checkTrigger(GameEvent event, Game game) {
-        return event.getPlayerId().equals(this.getControllerId())
-                && event.getSourceId().equals(this.getSourceId());
-    }
-
-    @Override
-    public String getRule() {
-        return "Choose a nonland card name, then target player reveals their hand and discards all cards with that name.";
-    }
-}
-
 class CabalTherapistDiscardEffect extends OneShotEffect {
 
     CabalTherapistDiscardEffect() {
@@ -155,9 +101,8 @@ class CabalTherapistDiscardEffect extends OneShotEffect {
             if (CardUtil.haveSameNames(card.getName(), cardName)) {
                 return false;
             }
-            return true;
-        });
-        targetPlayer.discard(hand, source, game);
+        }
+        targetPlayer.revealCards(source, hand, game);
         return true;
     }
 
diff --git a/Mage.Sets/src/mage/cards/c/CavalierOfNight.java b/Mage.Sets/src/mage/cards/c/CavalierOfNight.java
index 47437215ba..e8776a3ea4 100644
--- a/Mage.Sets/src/mage/cards/c/CavalierOfNight.java
+++ b/Mage.Sets/src/mage/cards/c/CavalierOfNight.java
@@ -2,27 +2,25 @@ package mage.cards.c;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.DelayedTriggeredAbility;
 import mage.abilities.common.DiesTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.common.delayed.ReflexiveTriggeredAbility;
 import mage.abilities.costs.common.SacrificeTargetCost;
-import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.DestroyTargetEffect;
-import mage.abilities.effects.common.DoIfCostPaid;
+import mage.abilities.effects.common.DoWhenCostPaid;
 import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
-import mage.abilities.effects.common.SendOptionUsedEventEffect;
 import mage.abilities.keyword.LifelinkAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.*;
+import mage.constants.CardType;
+import mage.constants.ComparisonType;
+import mage.constants.SubType;
 import mage.filter.FilterCard;
 import mage.filter.common.FilterControlledCreaturePermanent;
 import mage.filter.common.FilterControlledPermanent;
 import mage.filter.common.FilterCreatureCard;
 import mage.filter.predicate.mageobject.ConvertedManaCostPredicate;
 import mage.filter.predicate.permanent.AnotherPredicate;
-import mage.game.Game;
-import mage.game.events.GameEvent;
 import mage.target.common.TargetCardInYourGraveyard;
 import mage.target.common.TargetControlledPermanent;
 import mage.target.common.TargetOpponentsCreaturePermanent;
@@ -56,10 +54,14 @@ public final class CavalierOfNight extends CardImpl {
         this.addAbility(LifelinkAbility.getInstance());
 
         // When Cavalier of Night enters the battlefield, you may sacrifice another creature. When you do, destroy target creature an opponent controls.
-        this.addAbility(new EntersBattlefieldTriggeredAbility(new DoIfCostPaid(
-                new CavalierOfNightCreateReflexiveTriggerEffect(),
-                new SacrificeTargetCost(new TargetControlledPermanent(filter))
-        ).setText("you may sacrifice another creature. When you do, destroy target creature an opponent controls.")));
+        ReflexiveTriggeredAbility triggeredAbility = new ReflexiveTriggeredAbility(
+                new DestroyTargetEffect(), false, "Sacrifice a creature?"
+        );
+        triggeredAbility.addTarget(new TargetOpponentsCreaturePermanent());
+        this.addAbility(new EntersBattlefieldTriggeredAbility(new DoWhenCostPaid(
+                triggeredAbility, new SacrificeTargetCost(new TargetControlledPermanent(filter)),
+                "destroy target creature an opponent controls"
+        )));
 
         // When Cavalier of Night dies, return target creature card with converted mana cost 3 or less from your graveyard to the battlefield.
         Ability ability = new DiesTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect());
@@ -76,58 +78,3 @@ public final class CavalierOfNight extends CardImpl {
         return new CavalierOfNight(this);
     }
 }
-
-class CavalierOfNightCreateReflexiveTriggerEffect extends OneShotEffect {
-
-    CavalierOfNightCreateReflexiveTriggerEffect() {
-        super(Outcome.Benefit);
-    }
-
-    private CavalierOfNightCreateReflexiveTriggerEffect(final CavalierOfNightCreateReflexiveTriggerEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public CavalierOfNightCreateReflexiveTriggerEffect copy() {
-        return new CavalierOfNightCreateReflexiveTriggerEffect(this);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        game.addDelayedTriggeredAbility(new CavalierOfNightReflexiveTriggeredAbility(), source);
-        return new SendOptionUsedEventEffect().apply(game, source);
-    }
-}
-
-class CavalierOfNightReflexiveTriggeredAbility extends DelayedTriggeredAbility {
-
-    CavalierOfNightReflexiveTriggeredAbility() {
-        super(new DestroyTargetEffect(), Duration.OneUse, true);
-        this.addTarget(new TargetOpponentsCreaturePermanent());
-    }
-
-    private CavalierOfNightReflexiveTriggeredAbility(final CavalierOfNightReflexiveTriggeredAbility ability) {
-        super(ability);
-    }
-
-    @Override
-    public CavalierOfNightReflexiveTriggeredAbility copy() {
-        return new CavalierOfNightReflexiveTriggeredAbility(this);
-    }
-
-    @Override
-    public boolean checkEventType(GameEvent event, Game game) {
-        return event.getType() == GameEvent.EventType.OPTION_USED;
-    }
-
-    @Override
-    public boolean checkTrigger(GameEvent event, Game game) {
-        return event.getPlayerId().equals(this.getControllerId())
-                && event.getSourceId().equals(this.getSourceId());
-    }
-
-    @Override
-    public String getRule() {
-        return "Destroy target creature an opponent controls";
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/d/DreamEater.java b/Mage.Sets/src/mage/cards/d/DreamEater.java
index de2fe37416..5c72856648 100644
--- a/Mage.Sets/src/mage/cards/d/DreamEater.java
+++ b/Mage.Sets/src/mage/cards/d/DreamEater.java
@@ -1,30 +1,28 @@
 package mage.cards.d;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.DelayedTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.common.delayed.ReflexiveTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.ReturnToHandTargetEffect;
-import mage.abilities.effects.common.SendOptionUsedEventEffect;
 import mage.abilities.effects.keyword.SurveilEffect;
-import mage.constants.SubType;
 import mage.abilities.keyword.FlashAbility;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.Outcome;
+import mage.constants.SubType;
 import mage.constants.TargetController;
 import mage.filter.FilterPermanent;
 import mage.filter.common.FilterNonlandPermanent;
 import mage.game.Game;
-import mage.game.events.GameEvent;
 import mage.target.TargetPermanent;
 
+import java.util.UUID;
+
 /**
- *
  * @author TheElk801
  */
 public final class DreamEater extends CardImpl {
@@ -44,14 +42,12 @@ public final class DreamEater extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
 
         // When Dream Eater enters the battlefield, surveil 4. When you do, you may return target nonland permanent an opponent controls to its owner's hand.
-        Ability ability = new EntersBattlefieldTriggeredAbility(
-                new SurveilEffect(4)
-        );
-        ability.addEffect(new DreamEaterCreateReflexiveTriggerEffect());
+        Ability ability = new EntersBattlefieldTriggeredAbility(new SurveilEffect(4));
+        ability.addEffect(new DreamEaterEffect());
         this.addAbility(ability);
     }
 
-    public DreamEater(final DreamEater card) {
+    private DreamEater(final DreamEater card) {
         super(card);
     }
 
@@ -61,31 +57,7 @@ public final class DreamEater extends CardImpl {
     }
 }
 
-class DreamEaterCreateReflexiveTriggerEffect extends OneShotEffect {
-
-    public DreamEaterCreateReflexiveTriggerEffect() {
-        super(Outcome.Benefit);
-        this.staticText = "When you do, you may return target "
-                + "nonland permanent an opponent controls to its owner's hand.";
-    }
-
-    public DreamEaterCreateReflexiveTriggerEffect(final DreamEaterCreateReflexiveTriggerEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public DreamEaterCreateReflexiveTriggerEffect copy() {
-        return new DreamEaterCreateReflexiveTriggerEffect(this);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        game.addDelayedTriggeredAbility(new DreamEaterReflexiveTriggeredAbility(), source);
-        return new SendOptionUsedEventEffect().apply(game, source);
-    }
-}
-
-class DreamEaterReflexiveTriggeredAbility extends DelayedTriggeredAbility {
+class DreamEaterEffect extends OneShotEffect {
 
     private static final FilterPermanent filter
             = new FilterNonlandPermanent("nonland permanent an opponent controls");
@@ -94,35 +66,29 @@ class DreamEaterReflexiveTriggeredAbility extends DelayedTriggeredAbility {
         filter.add(TargetController.OPPONENT.getControllerPredicate());
     }
 
-    public DreamEaterReflexiveTriggeredAbility() {
-        super(new ReturnToHandTargetEffect());
-        this.addTarget(new TargetPermanent(filter));
-        this.optional = true;
+    DreamEaterEffect() {
+        super(Outcome.Benefit);
+        this.staticText = "When you do, you may return target "
+                + "nonland permanent an opponent controls to its owner's hand.";
     }
 
-    public DreamEaterReflexiveTriggeredAbility(final DreamEaterReflexiveTriggeredAbility ability) {
-        super(ability);
+    private DreamEaterEffect(final DreamEaterEffect effect) {
+        super(effect);
     }
 
     @Override
-    public DreamEaterReflexiveTriggeredAbility copy() {
-        return new DreamEaterReflexiveTriggeredAbility(this);
+    public DreamEaterEffect copy() {
+        return new DreamEaterEffect(this);
     }
 
     @Override
-    public boolean checkEventType(GameEvent event, Game game) {
-        return event.getType() == GameEvent.EventType.OPTION_USED;
-    }
-
-    @Override
-    public boolean checkTrigger(GameEvent event, Game game) {
-        return event.getPlayerId().equals(this.getControllerId())
-                && event.getSourceId().equals(this.getSourceId());
-    }
-
-    @Override
-    public String getRule() {
-        return "When you surveil 4, you may return target nonland permanent "
-                + "an opponent controls to its owner's hand.";
+    public boolean apply(Game game, Ability source) {
+        ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility(
+                new ReturnToHandTargetEffect(), true,
+                "you may return target nonland permanent an opponent controls to its owner's hand"
+        );
+        ability.addTarget(new TargetPermanent(filter));
+        game.fireReflexiveTriggeredAbility(ability, source);
+        return true;
     }
 }
diff --git a/Mage.Sets/src/mage/cards/e/EscapeProtocol.java b/Mage.Sets/src/mage/cards/e/EscapeProtocol.java
index 9d4ddeaa64..e7335c60f1 100644
--- a/Mage.Sets/src/mage/cards/e/EscapeProtocol.java
+++ b/Mage.Sets/src/mage/cards/e/EscapeProtocol.java
@@ -1,24 +1,17 @@
 package mage.cards.e;
 
-import mage.abilities.Ability;
-import mage.abilities.DelayedTriggeredAbility;
 import mage.abilities.common.CycleControllerTriggeredAbility;
+import mage.abilities.common.delayed.ReflexiveTriggeredAbility;
 import mage.abilities.costs.mana.GenericManaCost;
-import mage.abilities.effects.OneShotEffect;
-import mage.abilities.effects.common.DoIfCostPaid;
+import mage.abilities.effects.common.DoWhenCostPaid;
 import mage.abilities.effects.common.ExileTargetForSourceEffect;
 import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetEffect;
-import mage.abilities.effects.common.SendOptionUsedEventEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.Duration;
-import mage.constants.Outcome;
 import mage.filter.FilterPermanent;
 import mage.filter.common.FilterControlledPermanent;
 import mage.filter.predicate.Predicates;
-import mage.game.Game;
-import mage.game.events.GameEvent;
 import mage.target.TargetPermanent;
 
 import java.util.UUID;
@@ -28,50 +21,6 @@ import java.util.UUID;
  */
 public final class EscapeProtocol extends CardImpl {
 
-    public EscapeProtocol(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}");
-
-        // Whenever you cycle a card, you may pay {1}. When you do, exile target artifact or creature you control, then return it to the battlefield under its owner's control.
-        this.addAbility(new CycleControllerTriggeredAbility(new DoIfCostPaid(
-                new EscapeProtocolEffect(), new GenericManaCost(1)
-        ).setText("you may pay {1}. When you do, exile target artifact or creature you control, " +
-                "then return it to the battlefield under its owner's control.")));
-    }
-
-    private EscapeProtocol(final EscapeProtocol card) {
-        super(card);
-    }
-
-    @Override
-    public EscapeProtocol copy() {
-        return new EscapeProtocol(this);
-    }
-}
-
-class EscapeProtocolEffect extends OneShotEffect {
-
-    EscapeProtocolEffect() {
-        super(Outcome.Benefit);
-    }
-
-    private EscapeProtocolEffect(final EscapeProtocolEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public EscapeProtocolEffect copy() {
-        return new EscapeProtocolEffect(this);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        game.addDelayedTriggeredAbility(new EscapeProtocolReflexiveTriggeredAbility(), source);
-        return new SendOptionUsedEventEffect().apply(game, source);
-    }
-}
-
-class EscapeProtocolReflexiveTriggeredAbility extends DelayedTriggeredAbility {
-
     private static final FilterPermanent filter
             = new FilterControlledPermanent("artifact or creature you control");
 
@@ -82,35 +31,28 @@ class EscapeProtocolReflexiveTriggeredAbility extends DelayedTriggeredAbility {
         ));
     }
 
-    EscapeProtocolReflexiveTriggeredAbility() {
-        super(new ExileTargetForSourceEffect(), Duration.OneUse, true);
-        this.addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect());
-        this.addTarget(new TargetPermanent(filter));
+    public EscapeProtocol(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}");
+
+        // Whenever you cycle a card, you may pay {1}. When you do, exile target artifact or creature you control, then return it to the battlefield under its owner's control.
+        ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility(
+                new ExileTargetForSourceEffect(), false,
+                "exile target artifact or creature you control, " +
+                        "then return it to the battlefield under its owner's control."
+        );
+        ability.addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect());
+        ability.addTarget(new TargetPermanent(filter));
+        this.addAbility(new CycleControllerTriggeredAbility(new DoWhenCostPaid(
+                ability, new GenericManaCost(1), "Pay {1}?"
+        )));
     }
 
-    private EscapeProtocolReflexiveTriggeredAbility(final EscapeProtocolReflexiveTriggeredAbility ability) {
-        super(ability);
+    private EscapeProtocol(final EscapeProtocol card) {
+        super(card);
     }
 
     @Override
-    public EscapeProtocolReflexiveTriggeredAbility copy() {
-        return new EscapeProtocolReflexiveTriggeredAbility(this);
-    }
-
-    @Override
-    public boolean checkEventType(GameEvent event, Game game) {
-        return event.getType() == GameEvent.EventType.OPTION_USED;
-    }
-
-    @Override
-    public boolean checkTrigger(GameEvent event, Game game) {
-        return event.getPlayerId().equals(this.getControllerId())
-                && event.getSourceId().equals(this.getSourceId());
-    }
-
-    @Override
-    public String getRule() {
-        return "Exile target artifact or creature you control, " +
-                "then return it to the battlefield under its owner's control.";
+    public EscapeProtocol copy() {
+        return new EscapeProtocol(this);
     }
 }
diff --git a/Mage.Sets/src/mage/cards/f/FirebladeArtist.java b/Mage.Sets/src/mage/cards/f/FirebladeArtist.java
index e11b412cc7..5568d06ee3 100644
--- a/Mage.Sets/src/mage/cards/f/FirebladeArtist.java
+++ b/Mage.Sets/src/mage/cards/f/FirebladeArtist.java
@@ -1,21 +1,18 @@
 package mage.cards.f;
 
 import mage.MageInt;
-import mage.abilities.Ability;
-import mage.abilities.DelayedTriggeredAbility;
 import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
+import mage.abilities.common.delayed.ReflexiveTriggeredAbility;
 import mage.abilities.costs.common.SacrificeTargetCost;
-import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.DamageTargetEffect;
-import mage.abilities.effects.common.DoIfCostPaid;
-import mage.abilities.effects.common.SendOptionUsedEventEffect;
+import mage.abilities.effects.common.DoWhenCostPaid;
 import mage.abilities.keyword.HasteAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.*;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.TargetController;
 import mage.filter.StaticFilters;
-import mage.game.Game;
-import mage.game.events.GameEvent;
 import mage.target.common.TargetControlledPermanent;
 import mage.target.common.TargetOpponentOrPlaneswalker;
 
@@ -38,15 +35,15 @@ public final class FirebladeArtist extends CardImpl {
         this.addAbility(HasteAbility.getInstance());
 
         // At the beginning of your upkeep, you may sacrifice a creature. When you do, Fireblade Artist deals 2 damage to target opponent or planeswalker.
+        ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility(
+                new DamageTargetEffect(2), false,
+                "{this} deals 2 damage to target opponent or planeswalker"
+        );
+        ability.addTarget(new TargetOpponentOrPlaneswalker());
         this.addAbility(new BeginningOfUpkeepTriggeredAbility(
-                new DoIfCostPaid(
-                        new FirebladeArtistCreateReflexiveTriggerEffect(),
-                        new SacrificeTargetCost(new TargetControlledPermanent(
-                                StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT
-                        )), "Sacrifice a creature to deal 2 damage to an opponent or planeswalker?"
-                ).setText("you may sacrifice a creature. When you do, " +
-                        "{this} deals 2 damage to target opponent or planeswalker."),
-                TargetController.YOU, false
+                new DoWhenCostPaid(ability, new SacrificeTargetCost(
+                        new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT)
+                ), "Sacrifice a creature?"), TargetController.YOU, false
         ));
     }
 
@@ -59,58 +56,3 @@ public final class FirebladeArtist extends CardImpl {
         return new FirebladeArtist(this);
     }
 }
-
-class FirebladeArtistCreateReflexiveTriggerEffect extends OneShotEffect {
-
-    FirebladeArtistCreateReflexiveTriggerEffect() {
-        super(Outcome.Benefit);
-    }
-
-    private FirebladeArtistCreateReflexiveTriggerEffect(final FirebladeArtistCreateReflexiveTriggerEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public FirebladeArtistCreateReflexiveTriggerEffect copy() {
-        return new FirebladeArtistCreateReflexiveTriggerEffect(this);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        game.addDelayedTriggeredAbility(new FirebladeArtistReflexiveTriggeredAbility(), source);
-        return new SendOptionUsedEventEffect().apply(game, source);
-    }
-}
-
-class FirebladeArtistReflexiveTriggeredAbility extends DelayedTriggeredAbility {
-
-    FirebladeArtistReflexiveTriggeredAbility() {
-        super(new DamageTargetEffect(2), Duration.OneUse, true);
-        this.addTarget(new TargetOpponentOrPlaneswalker());
-    }
-
-    private FirebladeArtistReflexiveTriggeredAbility(final FirebladeArtistReflexiveTriggeredAbility ability) {
-        super(ability);
-    }
-
-    @Override
-    public FirebladeArtistReflexiveTriggeredAbility copy() {
-        return new FirebladeArtistReflexiveTriggeredAbility(this);
-    }
-
-    @Override
-    public boolean checkEventType(GameEvent event, Game game) {
-        return event.getType() == GameEvent.EventType.OPTION_USED;
-    }
-
-    @Override
-    public boolean checkTrigger(GameEvent event, Game game) {
-        return event.getPlayerId().equals(this.getControllerId())
-                && event.getSourceId().equals(this.getSourceId());
-    }
-
-    @Override
-    public String getRule() {
-        return "{this} deals 2 damage to target opponent or planeswalker.";
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/f/FlaxenIntruder.java b/Mage.Sets/src/mage/cards/f/FlaxenIntruder.java
index 105e7d16c0..c64ba61968 100644
--- a/Mage.Sets/src/mage/cards/f/FlaxenIntruder.java
+++ b/Mage.Sets/src/mage/cards/f/FlaxenIntruder.java
@@ -1,24 +1,17 @@
 package mage.cards.f;
 
 import mage.MageInt;
-import mage.abilities.Ability;
-import mage.abilities.DelayedTriggeredAbility;
 import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
+import mage.abilities.common.delayed.ReflexiveTriggeredAbility;
 import mage.abilities.costs.common.SacrificeSourceCost;
-import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.abilities.effects.common.DestroyTargetEffect;
-import mage.abilities.effects.common.DoIfCostPaid;
-import mage.abilities.effects.common.SendOptionUsedEventEffect;
+import mage.abilities.effects.common.DoWhenCostPaid;
 import mage.cards.AdventureCard;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.Duration;
-import mage.constants.Outcome;
 import mage.constants.SubType;
 import mage.filter.StaticFilters;
-import mage.game.Game;
-import mage.game.events.GameEvent;
 import mage.game.permanent.token.BearToken;
 import mage.target.TargetPermanent;
 
@@ -38,9 +31,13 @@ public final class FlaxenIntruder extends AdventureCard {
         this.toughness = new MageInt(2);
 
         // Whenever Flaxen Intruder deals combat damage to a player, you may sacrifice it. When you do, destroy target artifact or enchantment.
-        this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new DoIfCostPaid(
-                new FlaxenIntruderCreateReflexiveTriggerEffect(), new SacrificeSourceCost()
-        ).setText("you may sacrifice it. When you do, destroy target artifact or enchantment."), false));
+        ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility(
+                new DestroyTargetEffect(), false, "destroy target artifact or enchantment"
+        );
+        ability.addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT));
+        this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new DoWhenCostPaid(
+                ability, new SacrificeSourceCost(), "Sacrifice {this}?"
+        ), false));
 
         // Welcome Home
         // Create three 2/2 green Bear creature tokens.
@@ -56,58 +53,3 @@ public final class FlaxenIntruder extends AdventureCard {
         return new FlaxenIntruder(this);
     }
 }
-
-class FlaxenIntruderCreateReflexiveTriggerEffect extends OneShotEffect {
-
-    FlaxenIntruderCreateReflexiveTriggerEffect() {
-        super(Outcome.Benefit);
-    }
-
-    private FlaxenIntruderCreateReflexiveTriggerEffect(final FlaxenIntruderCreateReflexiveTriggerEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public FlaxenIntruderCreateReflexiveTriggerEffect copy() {
-        return new FlaxenIntruderCreateReflexiveTriggerEffect(this);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        game.addDelayedTriggeredAbility(new FlaxenIntruderReflexiveTriggeredAbility(), source);
-        return new SendOptionUsedEventEffect().apply(game, source);
-    }
-}
-
-class FlaxenIntruderReflexiveTriggeredAbility extends DelayedTriggeredAbility {
-
-    FlaxenIntruderReflexiveTriggeredAbility() {
-        super(new DestroyTargetEffect(), Duration.OneUse, true);
-        this.addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT));
-    }
-
-    private FlaxenIntruderReflexiveTriggeredAbility(final FlaxenIntruderReflexiveTriggeredAbility ability) {
-        super(ability);
-    }
-
-    @Override
-    public FlaxenIntruderReflexiveTriggeredAbility copy() {
-        return new FlaxenIntruderReflexiveTriggeredAbility(this);
-    }
-
-    @Override
-    public boolean checkEventType(GameEvent event, Game game) {
-        return event.getType() == GameEvent.EventType.OPTION_USED;
-    }
-
-    @Override
-    public boolean checkTrigger(GameEvent event, Game game) {
-        return event.getPlayerId().equals(this.getControllerId())
-                && event.getSourceId().equals(this.getSourceId());
-    }
-
-    @Override
-    public String getRule() {
-        return "When you do, destroy target artifact or enchantment.";
-    }
-}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/h/HeartPiercerManticore.java b/Mage.Sets/src/mage/cards/h/HeartPiercerManticore.java
index ecf355dea2..371e4e83e3 100644
--- a/Mage.Sets/src/mage/cards/h/HeartPiercerManticore.java
+++ b/Mage.Sets/src/mage/cards/h/HeartPiercerManticore.java
@@ -1,32 +1,29 @@
 package mage.cards.h;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.DelayedTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.common.delayed.ReflexiveTriggeredAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.DamageTargetEffect;
-import mage.abilities.effects.common.SendOptionUsedEventEffect;
 import mage.abilities.keyword.EmbalmAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.Duration;
-import mage.constants.SubType;
 import mage.constants.Outcome;
+import mage.constants.SubType;
 import mage.filter.StaticFilters;
 import mage.game.Game;
-import mage.game.events.GameEvent;
 import mage.game.permanent.Permanent;
 import mage.players.Player;
 import mage.target.Target;
-import mage.target.common.TargetControlledCreaturePermanent;
 import mage.target.common.TargetAnyTarget;
+import mage.target.common.TargetControlledCreaturePermanent;
+
+import java.util.UUID;
 
 /**
- *
  * @author fireshoes
  */
 public final class HeartPiercerManticore extends CardImpl {
@@ -39,16 +36,13 @@ public final class HeartPiercerManticore extends CardImpl {
         this.toughness = new MageInt(3);
 
         // When Heart-Piercer Manticore enters the battlefield, you may sacrifice another creature. When you do, Heart-Piercer Manticore deals damage equal to that creature's power to any target.
-        this.addAbility(new EntersBattlefieldTriggeredAbility(
-                new HeartPiercerManticoreSacrificeEffect(), true
-        ));
+        this.addAbility(new EntersBattlefieldTriggeredAbility(new HeartPiercerManticoreSacrificeEffect(), true));
 
         // Embalm {5}{R}
         this.addAbility(new EmbalmAbility(new ManaCostsImpl("{5}{R}"), this));
-
     }
 
-    public HeartPiercerManticore(final HeartPiercerManticore card) {
+    private HeartPiercerManticore(final HeartPiercerManticore card) {
         super(card);
     }
 
@@ -60,13 +54,13 @@ public final class HeartPiercerManticore extends CardImpl {
 
 class HeartPiercerManticoreSacrificeEffect extends OneShotEffect {
 
-    public HeartPiercerManticoreSacrificeEffect() {
+    HeartPiercerManticoreSacrificeEffect() {
         super(Outcome.Damage);
         this.staticText = "sacrifice another creature. When you do, "
                 + "{this} deals damage equal to that creature's power to any target";
     }
 
-    public HeartPiercerManticoreSacrificeEffect(final HeartPiercerManticoreSacrificeEffect effect) {
+    private HeartPiercerManticoreSacrificeEffect(final HeartPiercerManticoreSacrificeEffect effect) {
         super(effect);
     }
 
@@ -78,54 +72,29 @@ class HeartPiercerManticoreSacrificeEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player controller = game.getPlayer(source.getControllerId());
-        if (controller != null) {
-            Target target = new TargetControlledCreaturePermanent(1, 1, StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE, true);
-            if (controller.choose(outcome, target, source.getSourceId(), game)) {
-                Permanent toSacrifice = game.getPermanent(target.getFirstTarget());
-                if (toSacrifice != null) {
-                    DelayedTriggeredAbility trigger = new HeartPiercerManticoreReflexiveTriggeredAbility(toSacrifice.getPower().getValue());
-                    if (toSacrifice.sacrifice(source.getSourceId(), game)) {
-                        game.addDelayedTriggeredAbility(trigger, source);
-                        return new SendOptionUsedEventEffect().apply(game, source);
-                    }
-                }
-            }
-            return true;
+        if (controller == null) {
+            return false;
         }
-        return false;
-    }
-}
-
-class HeartPiercerManticoreReflexiveTriggeredAbility extends DelayedTriggeredAbility {
-
-    public HeartPiercerManticoreReflexiveTriggeredAbility(int damage) {
-        super(new DamageTargetEffect(damage), Duration.OneUse, true);
-        this.addTarget(new TargetAnyTarget());
-    }
-
-    public HeartPiercerManticoreReflexiveTriggeredAbility(final HeartPiercerManticoreReflexiveTriggeredAbility ability) {
-        super(ability);
-    }
-
-    @Override
-    public HeartPiercerManticoreReflexiveTriggeredAbility copy() {
-        return new HeartPiercerManticoreReflexiveTriggeredAbility(this);
-    }
-
-    @Override
-    public boolean checkEventType(GameEvent event, Game game) {
-        return event.getType() == GameEvent.EventType.OPTION_USED;
-    }
-
-    @Override
-    public boolean checkTrigger(GameEvent event, Game game) {
-        return event.getPlayerId().equals(this.getControllerId())
-                && event.getSourceId().equals(this.getSourceId());
-    }
-
-    @Override
-    public String getRule() {
-        return "When you sacrifice a creature to {this}'s ability, "
-                + "{this} deals damage equal to that creature's power to any target";
+        Target target = new TargetControlledCreaturePermanent(
+                1, 1, StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE, true
+        );
+        if (!controller.choose(outcome, target, source.getSourceId(), game)) {
+            return false;
+        }
+        Permanent toSacrifice = game.getPermanent(target.getFirstTarget());
+        if (toSacrifice == null) {
+            return false;
+        }
+        int power = toSacrifice.getPower().getValue();
+        if (!toSacrifice.sacrifice(source.getSourceId(), game)) {
+            return false;
+        }
+        ReflexiveTriggeredAbility trigger = new ReflexiveTriggeredAbility(
+                new DamageTargetEffect(power), false,
+                "{this} deals damage equal to that creature's power to any target."
+        );
+        trigger.addTarget(new TargetAnyTarget());
+        game.fireReflexiveTriggeredAbility(trigger, source);
+        return true;
     }
 }
diff --git a/Mage.Sets/src/mage/cards/h/Hypothesizzle.java b/Mage.Sets/src/mage/cards/h/Hypothesizzle.java
index f866ac8f02..82681c07c5 100644
--- a/Mage.Sets/src/mage/cards/h/Hypothesizzle.java
+++ b/Mage.Sets/src/mage/cards/h/Hypothesizzle.java
@@ -1,26 +1,19 @@
 package mage.cards.h;
 
-import java.util.UUID;
-import mage.abilities.Ability;
-import mage.abilities.DelayedTriggeredAbility;
+import mage.abilities.common.delayed.ReflexiveTriggeredAbility;
 import mage.abilities.costs.common.DiscardCardCost;
-import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.DamageTargetEffect;
-import mage.abilities.effects.common.DoIfCostPaid;
+import mage.abilities.effects.common.DoWhenCostPaid;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
-import mage.abilities.effects.common.SendOptionUsedEventEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.Duration;
-import mage.constants.Outcome;
 import mage.filter.StaticFilters;
-import mage.game.Game;
-import mage.game.events.GameEvent;
 import mage.target.common.TargetCreaturePermanent;
 
+import java.util.UUID;
+
 /**
- *
  * @author TheElk801
  */
 public final class Hypothesizzle extends CardImpl {
@@ -30,15 +23,19 @@ public final class Hypothesizzle extends CardImpl {
 
         // Draw two cards. Then you may discard a nonland card. When you do, Hypothesizzle deals 4 damage to target creature.
         this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2));
-        this.getSpellAbility().addEffect(new DoIfCostPaid(
-                new HypothesizzleCreateReflexiveTriggerEffect(),
-                new DiscardCardCost(StaticFilters.FILTER_CARD_A_NON_LAND),
-                "Discard a nonland card to deal 4 damage?"
-        ).setText("Then you may discard a nonland card. "
-                + "When you do, {this} deals 4 damage to target creature."));
+
+        ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility(
+                new DamageTargetEffect(4), false,
+                "{this} deals 4 damage to target creature"
+        );
+        ability.addTarget(new TargetCreaturePermanent());
+        this.getSpellAbility().addEffect(new DoWhenCostPaid(
+                ability, new DiscardCardCost(StaticFilters.FILTER_CARD_A_NON_LAND),
+                "Discard a nonland card?"
+        ).concatBy("Then"));
     }
 
-    public Hypothesizzle(final Hypothesizzle card) {
+    private Hypothesizzle(final Hypothesizzle card) {
         super(card);
     }
 
@@ -47,60 +44,3 @@ public final class Hypothesizzle extends CardImpl {
         return new Hypothesizzle(this);
     }
 }
-
-class HypothesizzleCreateReflexiveTriggerEffect extends OneShotEffect {
-
-    public HypothesizzleCreateReflexiveTriggerEffect() {
-        super(Outcome.Benefit);
-        this.staticText = "When you do, it deals 4 damage to target creature";
-    }
-
-    public HypothesizzleCreateReflexiveTriggerEffect(final HypothesizzleCreateReflexiveTriggerEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public HypothesizzleCreateReflexiveTriggerEffect copy() {
-        return new HypothesizzleCreateReflexiveTriggerEffect(this);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        game.addDelayedTriggeredAbility(new HypothesizzleReflexiveTriggeredAbility(), source);
-        return new SendOptionUsedEventEffect().apply(game, source);
-    }
-}
-
-class HypothesizzleReflexiveTriggeredAbility extends DelayedTriggeredAbility {
-
-    public HypothesizzleReflexiveTriggeredAbility() {
-        super(new DamageTargetEffect(4), Duration.OneUse, true);
-        this.addTarget(new TargetCreaturePermanent());
-    }
-
-    public HypothesizzleReflexiveTriggeredAbility(final HypothesizzleReflexiveTriggeredAbility ability) {
-        super(ability);
-    }
-
-    @Override
-    public HypothesizzleReflexiveTriggeredAbility copy() {
-        return new HypothesizzleReflexiveTriggeredAbility(this);
-    }
-
-    @Override
-    public boolean checkEventType(GameEvent event, Game game) {
-        return event.getType() == GameEvent.EventType.OPTION_USED;
-    }
-
-    @Override
-    public boolean checkTrigger(GameEvent event, Game game) {
-        return event.getPlayerId().equals(this.getControllerId())
-                && event.getSourceId().equals(this.getSourceId());
-    }
-
-    @Override
-    public String getRule() {
-        return "When you discard a nonland card, "
-                + "{this} deals 4 damage to target creature";
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/i/IsarethTheAwakener.java b/Mage.Sets/src/mage/cards/i/IsarethTheAwakener.java
index 62cf6c7eff..ced07404f1 100644
--- a/Mage.Sets/src/mage/cards/i/IsarethTheAwakener.java
+++ b/Mage.Sets/src/mage/cards/i/IsarethTheAwakener.java
@@ -1,27 +1,19 @@
 package mage.cards.i;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.DelayedTriggeredAbility;
 import mage.abilities.common.AttacksTriggeredAbility;
+import mage.abilities.common.delayed.ReflexiveTriggeredAbility;
 import mage.abilities.costs.mana.GenericManaCost;
 import mage.abilities.costs.mana.ManaCosts;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.ReplacementEffectImpl;
-import mage.abilities.effects.common.SendOptionUsedEventEffect;
-import mage.constants.SubType;
-import mage.constants.SuperType;
 import mage.abilities.keyword.DeathtouchAbility;
 import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.CardType;
-import mage.constants.ComparisonType;
-import mage.constants.Duration;
-import mage.constants.Outcome;
-import mage.constants.Zone;
+import mage.constants.*;
 import mage.counters.CounterType;
 import mage.counters.Counters;
 import mage.filter.FilterCard;
@@ -33,8 +25,9 @@ import mage.game.events.ZoneChangeEvent;
 import mage.players.Player;
 import mage.target.common.TargetCardInYourGraveyard;
 
+import java.util.UUID;
+
 /**
- *
  * @author TheElk801
  */
 public final class IsarethTheAwakener extends CardImpl {
@@ -55,7 +48,7 @@ public final class IsarethTheAwakener extends CardImpl {
         this.addAbility(new AttacksTriggeredAbility(new IsarethTheAwakenerCreateReflexiveTriggerEffect(), false));
     }
 
-    public IsarethTheAwakener(final IsarethTheAwakener card) {
+    private IsarethTheAwakener(final IsarethTheAwakener card) {
         super(card);
     }
 
@@ -67,15 +60,17 @@ public final class IsarethTheAwakener extends CardImpl {
 
 class IsarethTheAwakenerCreateReflexiveTriggerEffect extends OneShotEffect {
 
-    public IsarethTheAwakenerCreateReflexiveTriggerEffect() {
+    private static final String rule = "return target creature card "
+            + "with converted mana cost X from your graveyard to the battlefield "
+            + "with a corpse counter on it. If that creature would leave the battlefield, "
+            + "exile it instead of putting it anywhere else.";
+
+    IsarethTheAwakenerCreateReflexiveTriggerEffect() {
         super(Outcome.Benefit);
-        this.staticText = "you may pay {X}. When you do, return target creature card "
-                + "with converted mana cost X from your graveyard to the battlefield "
-                + "with a corpse counter on it. If that creature would leave the battlefield, "
-                + "exile it instead of putting it anywhere else.";
+        this.staticText = "you may pay {X}. When you do, " + rule;
     }
 
-    public IsarethTheAwakenerCreateReflexiveTriggerEffect(final IsarethTheAwakenerCreateReflexiveTriggerEffect effect) {
+    private IsarethTheAwakenerCreateReflexiveTriggerEffect(final IsarethTheAwakenerCreateReflexiveTriggerEffect effect) {
         super(effect);
     }
 
@@ -97,67 +92,34 @@ class IsarethTheAwakenerCreateReflexiveTriggerEffect extends OneShotEffect {
         if (!cost.pay(source, game, source.getSourceId(), source.getControllerId(), false, null)) {
             return false;
         }
-        game.addDelayedTriggeredAbility(new IsarethTheAwakenerReflexiveTriggeredAbility(), source);
-        return new SendOptionUsedEventEffect(costX).apply(game, source);
-    }
-}
-
-class IsarethTheAwakenerReflexiveTriggeredAbility extends DelayedTriggeredAbility {
-
-    public IsarethTheAwakenerReflexiveTriggeredAbility() {
-        super(new IsarethTheAwakenerEffect(), Duration.OneUse, true);
-        this.addEffect(new IsarethTheAwakenerReplacementEffect());
-    }
-
-    public IsarethTheAwakenerReflexiveTriggeredAbility(final IsarethTheAwakenerReflexiveTriggeredAbility ability) {
-        super(ability);
-    }
-
-    @Override
-    public IsarethTheAwakenerReflexiveTriggeredAbility copy() {
-        return new IsarethTheAwakenerReflexiveTriggeredAbility(this);
-    }
-
-    @Override
-    public boolean checkEventType(GameEvent event, Game game) {
-        return event.getType() == GameEvent.EventType.OPTION_USED;
-    }
-
-    @Override
-    public boolean checkTrigger(GameEvent event, Game game) {
-        if (!event.getPlayerId().equals(this.getControllerId())
-                || !event.getSourceId().equals(this.getSourceId())) {
-            return false;
-        }
-        FilterCard filter = new FilterCreatureCard(
-                "creature card with converted mana cost "
-                + event.getAmount()
-                + " or less from your graveyard"
+        ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility(
+                new IsarethTheAwakenerEffect(), false, rule
         );
-        filter.add(new ConvertedManaCostPredicate(
-                ComparisonType.FEWER_THAN, event.getAmount() + 1
-        ));
-        this.getTargets().clear();
-        this.addTarget(new TargetCardInYourGraveyard(filter));
+        ability.addEffect(new IsarethTheAwakenerReplacementEffect());
+        ability.addTarget(new TargetCardInYourGraveyard(makeFilter(costX)));
+        game.fireReflexiveTriggeredAbility(ability, source);
         return true;
     }
 
-    @Override
-    public String getRule() {
-        return "When you pay {X}, return target creature card "
-                + "with converted mana cost X from your graveyard to the battlefield "
-                + "with a corpse counter on it. If that creature would leave the battlefield, "
-                + "exile it instead of putting it anywhere else.";
+    private static FilterCard makeFilter(int xValue) {
+        FilterCard filter = new FilterCreatureCard(
+                "creature card with converted mana cost " +
+                        xValue + " or less from your graveyard"
+        );
+        filter.add(new ConvertedManaCostPredicate(
+                ComparisonType.EQUAL_TO, xValue
+        ));
+        return filter;
     }
 }
 
 class IsarethTheAwakenerEffect extends OneShotEffect {
 
-    public IsarethTheAwakenerEffect() {
+    IsarethTheAwakenerEffect() {
         super(Outcome.PutCreatureInPlay);
     }
 
-    public IsarethTheAwakenerEffect(final IsarethTheAwakenerEffect effect) {
+    private IsarethTheAwakenerEffect(final IsarethTheAwakenerEffect effect) {
         super(effect);
     }
 
@@ -182,11 +144,11 @@ class IsarethTheAwakenerEffect extends OneShotEffect {
 
 class IsarethTheAwakenerReplacementEffect extends ReplacementEffectImpl {
 
-    public IsarethTheAwakenerReplacementEffect() {
+    IsarethTheAwakenerReplacementEffect() {
         super(Duration.Custom, Outcome.Exile);
     }
 
-    public IsarethTheAwakenerReplacementEffect(final IsarethTheAwakenerReplacementEffect effect) {
+    private IsarethTheAwakenerReplacementEffect(final IsarethTheAwakenerReplacementEffect effect) {
         super(effect);
     }
 
diff --git a/Mage.Sets/src/mage/cards/l/LavabrinkFloodgates.java b/Mage.Sets/src/mage/cards/l/LavabrinkFloodgates.java
index a3f4e93fd3..6dc1f35714 100644
--- a/Mage.Sets/src/mage/cards/l/LavabrinkFloodgates.java
+++ b/Mage.Sets/src/mage/cards/l/LavabrinkFloodgates.java
@@ -2,22 +2,23 @@ package mage.cards.l;
 
 import mage.Mana;
 import mage.abilities.Ability;
-import mage.abilities.DelayedTriggeredAbility;
 import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
+import mage.abilities.common.delayed.ReflexiveTriggeredAbility;
 import mage.abilities.costs.common.TapSourceCost;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.DamageAllEffect;
-import mage.abilities.effects.common.SendOptionUsedEventEffect;
 import mage.abilities.mana.SimpleManaAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.choices.Choice;
 import mage.choices.ChoiceImpl;
-import mage.constants.*;
+import mage.constants.CardType;
+import mage.constants.Outcome;
+import mage.constants.TargetController;
+import mage.constants.Zone;
 import mage.counters.CounterType;
 import mage.filter.StaticFilters;
 import mage.game.Game;
-import mage.game.events.GameEvent;
 import mage.game.permanent.Permanent;
 import mage.players.Player;
 
@@ -93,45 +94,17 @@ class LavabrinkFloodgatesEffect extends OneShotEffect {
                 break;
             case "Do nothing":
             default:
-                return false;
+                break;
         }
         if (permanent.getCounters(game).getCount(CounterType.DOOM) < 3
                 || !permanent.sacrifice(source.getSourceId(), game)) {
             return true;
         }
-        game.addDelayedTriggeredAbility(new LavabrinkFloodgatesReflexiveTriggeredAbility(), source);
-        return new SendOptionUsedEventEffect().apply(game, source);
-    }
-}
-
-class LavabrinkFloodgatesReflexiveTriggeredAbility extends DelayedTriggeredAbility {
-
-    LavabrinkFloodgatesReflexiveTriggeredAbility() {
-        super(new DamageAllEffect(6, StaticFilters.FILTER_PERMANENT_CREATURE), Duration.OneUse, true);
-    }
-
-    private LavabrinkFloodgatesReflexiveTriggeredAbility(final LavabrinkFloodgatesReflexiveTriggeredAbility ability) {
-        super(ability);
-    }
-
-    @Override
-    public LavabrinkFloodgatesReflexiveTriggeredAbility copy() {
-        return new LavabrinkFloodgatesReflexiveTriggeredAbility(this);
-    }
-
-    @Override
-    public boolean checkEventType(GameEvent event, Game game) {
-        return event.getType() == GameEvent.EventType.OPTION_USED;
-    }
-
-    @Override
-    public boolean checkTrigger(GameEvent event, Game game) {
-        return event.getPlayerId().equals(this.getControllerId())
-                && event.getSourceId().equals(this.getSourceId());
-    }
-
-    @Override
-    public String getRule() {
-        return "It deals 6 damage to each creature.";
+        game.fireReflexiveTriggeredAbility(new ReflexiveTriggeredAbility(
+                new DamageAllEffect(
+                        6, StaticFilters.FILTER_PERMANENT_CREATURE
+                ), false, "it deals 6 damage to each creature."
+        ), source);
+        return true;
     }
 }
diff --git a/Mage.Sets/src/mage/cards/n/NarsetOfTheAncientWay.java b/Mage.Sets/src/mage/cards/n/NarsetOfTheAncientWay.java
index 336202caa9..1ef622e1c1 100644
--- a/Mage.Sets/src/mage/cards/n/NarsetOfTheAncientWay.java
+++ b/Mage.Sets/src/mage/cards/n/NarsetOfTheAncientWay.java
@@ -3,25 +3,26 @@ package mage.cards.n;
 import mage.ConditionalMana;
 import mage.MageObject;
 import mage.abilities.Ability;
-import mage.abilities.DelayedTriggeredAbility;
 import mage.abilities.LoyaltyAbility;
 import mage.abilities.SpellAbility;
 import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
+import mage.abilities.common.delayed.ReflexiveTriggeredAbility;
 import mage.abilities.condition.Condition;
 import mage.abilities.costs.Cost;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.DamageTargetEffect;
 import mage.abilities.effects.common.GetEmblemEffect;
-import mage.abilities.effects.common.SendOptionUsedEventEffect;
 import mage.abilities.mana.conditional.ManaCondition;
 import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.choices.ChoiceColor;
-import mage.constants.*;
+import mage.constants.CardType;
+import mage.constants.Outcome;
+import mage.constants.SubType;
+import mage.constants.SuperType;
 import mage.game.Game;
 import mage.game.command.emblems.NarsetOfTheAncientWayEmblem;
-import mage.game.events.GameEvent;
 import mage.players.Player;
 import mage.target.common.TargetCreatureOrPlaneswalker;
 
@@ -141,41 +142,12 @@ class NarsetOfTheAncientWayDrawEffect extends OneShotEffect {
         if (card == null || card.isLand()) {
             return false;
         }
-        game.addDelayedTriggeredAbility(new NarsetOfTheAncientWayReflexiveTriggeredAbility(card.getConvertedManaCost()), source);
-        return new SendOptionUsedEventEffect().apply(game, source);
-    }
-}
-
-class NarsetOfTheAncientWayReflexiveTriggeredAbility extends DelayedTriggeredAbility {
-
-    NarsetOfTheAncientWayReflexiveTriggeredAbility(int cmc) {
-        super(new DamageTargetEffect(cmc), Duration.OneUse, true);
-        this.addTarget(new TargetCreatureOrPlaneswalker());
-    }
-
-    private NarsetOfTheAncientWayReflexiveTriggeredAbility(final NarsetOfTheAncientWayReflexiveTriggeredAbility ability) {
-        super(ability);
-    }
-
-    @Override
-    public NarsetOfTheAncientWayReflexiveTriggeredAbility copy() {
-        return new NarsetOfTheAncientWayReflexiveTriggeredAbility(this);
-    }
-
-    @Override
-    public boolean checkEventType(GameEvent event, Game game) {
-        return event.getType() == GameEvent.EventType.OPTION_USED;
-    }
-
-    @Override
-    public boolean checkTrigger(GameEvent event, Game game) {
-        return event.getPlayerId().equals(this.getControllerId())
-                && event.getSourceId().equals(this.getSourceId());
-    }
-
-    @Override
-    public String getRule() {
-        return "{this} deals damage to target creature or planeswalker " +
-                "equal to the discarded card's converted mana cost";
+        ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility(
+                new DamageTargetEffect(card.getConvertedManaCost()), false, "{this} deals damage " +
+                "to target creature or planeswalker equal to the discarded card's converted mana cost"
+        );
+        ability.addTarget(new TargetCreatureOrPlaneswalker());
+        game.fireReflexiveTriggeredAbility(ability, source);
+        return true;
     }
 }
diff --git a/Mage.Sets/src/mage/cards/s/SavaiThundermane.java b/Mage.Sets/src/mage/cards/s/SavaiThundermane.java
index 921dfdb90e..544f27681c 100644
--- a/Mage.Sets/src/mage/cards/s/SavaiThundermane.java
+++ b/Mage.Sets/src/mage/cards/s/SavaiThundermane.java
@@ -1,23 +1,16 @@
 package mage.cards.s;
 
 import mage.MageInt;
-import mage.abilities.Ability;
-import mage.abilities.DelayedTriggeredAbility;
 import mage.abilities.common.CycleControllerTriggeredAbility;
+import mage.abilities.common.delayed.ReflexiveTriggeredAbility;
 import mage.abilities.costs.mana.GenericManaCost;
-import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.DamageTargetEffect;
-import mage.abilities.effects.common.DoIfCostPaid;
+import mage.abilities.effects.common.DoWhenCostPaid;
 import mage.abilities.effects.common.GainLifeEffect;
-import mage.abilities.effects.common.SendOptionUsedEventEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.Duration;
-import mage.constants.Outcome;
 import mage.constants.SubType;
-import mage.game.Game;
-import mage.game.events.GameEvent;
 import mage.target.common.TargetCreaturePermanent;
 
 import java.util.UUID;
@@ -35,12 +28,15 @@ public final class SavaiThundermane extends CardImpl {
         this.power = new MageInt(3);
         this.toughness = new MageInt(2);
 
-        // Whenever you cycle a card, you may pay {2}. When you do Savai Thundermane deals 2 damage to target creature and you gain 2 life.
+        // Whenever you cycle a card, you may pay {2}. When you do, Savai Thundermane deals 2 damage to target creature and you gain 2 life.
+        ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility(
+                new DamageTargetEffect(2), false,
+                "{this} deals 2 damage to target creature and you gain 2 life"
+        );
+        ability.addEffect(new GainLifeEffect(2));
+        ability.addTarget(new TargetCreaturePermanent());
         this.addAbility(new CycleControllerTriggeredAbility(
-                new DoIfCostPaid(
-                        new SavaiThundermaneCreateReflexiveTriggerEffect(), new GenericManaCost(2),
-                        "Pay {2} to deal 2 damage and gain 2 life?"
-                ).setText("you may pay {2}. When you do, {this} deals 2 damage to target creature and you gain 2 life")
+                new DoWhenCostPaid(ability, new GenericManaCost(2), "Pay {2}?")
         ));
     }
 
@@ -53,59 +49,3 @@ public final class SavaiThundermane extends CardImpl {
         return new SavaiThundermane(this);
     }
 }
-
-class SavaiThundermaneCreateReflexiveTriggerEffect extends OneShotEffect {
-
-    SavaiThundermaneCreateReflexiveTriggerEffect() {
-        super(Outcome.Benefit);
-    }
-
-    private SavaiThundermaneCreateReflexiveTriggerEffect(final SavaiThundermaneCreateReflexiveTriggerEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public SavaiThundermaneCreateReflexiveTriggerEffect copy() {
-        return new SavaiThundermaneCreateReflexiveTriggerEffect(this);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        game.addDelayedTriggeredAbility(new SavaiThundermaneReflexiveTriggeredAbility(), source);
-        return new SendOptionUsedEventEffect().apply(game, source);
-    }
-}
-
-class SavaiThundermaneReflexiveTriggeredAbility extends DelayedTriggeredAbility {
-
-    SavaiThundermaneReflexiveTriggeredAbility() {
-        super(new DamageTargetEffect(2), Duration.OneUse, true);
-        this.addEffect(new GainLifeEffect(2));
-        this.addTarget(new TargetCreaturePermanent());
-    }
-
-    private SavaiThundermaneReflexiveTriggeredAbility(final SavaiThundermaneReflexiveTriggeredAbility ability) {
-        super(ability);
-    }
-
-    @Override
-    public SavaiThundermaneReflexiveTriggeredAbility copy() {
-        return new SavaiThundermaneReflexiveTriggeredAbility(this);
-    }
-
-    @Override
-    public boolean checkEventType(GameEvent event, Game game) {
-        return event.getType() == GameEvent.EventType.OPTION_USED;
-    }
-
-    @Override
-    public boolean checkTrigger(GameEvent event, Game game) {
-        return event.getPlayerId().equals(this.getControllerId())
-                && event.getSourceId().equals(this.getSourceId());
-    }
-
-    @Override
-    public String getRule() {
-        return "{this} deals 2 damage to target creature and you gain 2 life";
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/s/SkyriderPatrol.java b/Mage.Sets/src/mage/cards/s/SkyriderPatrol.java
index c7119a8183..1ab4b0e839 100644
--- a/Mage.Sets/src/mage/cards/s/SkyriderPatrol.java
+++ b/Mage.Sets/src/mage/cards/s/SkyriderPatrol.java
@@ -1,37 +1,38 @@
 package mage.cards.s;
 
-import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.Ability;
-import mage.abilities.DelayedTriggeredAbility;
 import mage.abilities.common.BeginningOfCombatTriggeredAbility;
+import mage.abilities.common.delayed.ReflexiveTriggeredAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
-import mage.abilities.effects.OneShotEffect;
-import mage.abilities.effects.common.DoIfCostPaid;
-import mage.abilities.effects.common.SendOptionUsedEventEffect;
+import mage.abilities.effects.common.DoWhenCostPaid;
 import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
 import mage.abilities.effects.common.counter.AddCountersTargetEffect;
-import mage.constants.SubType;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.Duration;
-import mage.constants.Outcome;
+import mage.constants.SubType;
 import mage.constants.TargetController;
 import mage.counters.CounterType;
 import mage.filter.common.FilterControlledCreaturePermanent;
 import mage.filter.predicate.permanent.AnotherPredicate;
-import mage.game.Game;
-import mage.game.events.GameEvent;
 import mage.target.TargetPermanent;
 
+import java.util.UUID;
+
 /**
- *
  * @author TheElk801
  */
 public final class SkyriderPatrol extends CardImpl {
 
+    private static final FilterControlledCreaturePermanent filter
+            = new FilterControlledCreaturePermanent("another creature you control");
+
+    static {
+        filter.add(AnotherPredicate.instance);
+    }
+
     public SkyriderPatrol(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{U}");
 
@@ -44,20 +45,19 @@ public final class SkyriderPatrol extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
 
         // At the beginning of combat on your turn, you may pay {G}{U}. When you do, put a +1/+1 counter on another target creature you control, and that creature gains flying until end of turn.
-        this.addAbility(new BeginningOfCombatTriggeredAbility(
-                new DoIfCostPaid(
-                        new SkyriderPatrolCreateReflexiveTriggerEffect(),
-                        new ManaCostsImpl("{G}{U}"),
-                        "Pay {G}{U} to put a +1/+1 counter on another"
-                        + " creature you control and give it flying?"
-                ).setText("you may pay {G}{U}. When you do, "
-                        + "put a +1/+1 counter on another target creature you control, "
-                        + "and that creature gains flying until end of turn."),
-                TargetController.YOU, false
-        ));
+        ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility(
+                new AddCountersTargetEffect(CounterType.P1P1.createInstance()), false,
+                "put a +1/+1 counter on another target creature you control, " +
+                        "and that creature gains flying until end of turn"
+        );
+        ability.addEffect(new GainAbilityTargetEffect(FlyingAbility.getInstance(), Duration.EndOfTurn));
+        ability.addTarget(new TargetPermanent(filter));
+        this.addAbility(new BeginningOfCombatTriggeredAbility(new DoWhenCostPaid(
+                ability, new ManaCostsImpl("{G}{U}"), "Pay {G}{U}?"
+        ), TargetController.YOU, false));
     }
 
-    public SkyriderPatrol(final SkyriderPatrol card) {
+    private SkyriderPatrol(final SkyriderPatrol card) {
         super(card);
     }
 
@@ -66,71 +66,3 @@ public final class SkyriderPatrol extends CardImpl {
         return new SkyriderPatrol(this);
     }
 }
-
-class SkyriderPatrolCreateReflexiveTriggerEffect extends OneShotEffect {
-
-    public SkyriderPatrolCreateReflexiveTriggerEffect() {
-        super(Outcome.Benefit);
-    }
-
-    public SkyriderPatrolCreateReflexiveTriggerEffect(final SkyriderPatrolCreateReflexiveTriggerEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public SkyriderPatrolCreateReflexiveTriggerEffect copy() {
-        return new SkyriderPatrolCreateReflexiveTriggerEffect(this);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        game.addDelayedTriggeredAbility(new SkyriderPatrolReflexiveTriggeredAbility(), source);
-        return new SendOptionUsedEventEffect().apply(game, source);
-    }
-}
-
-class SkyriderPatrolReflexiveTriggeredAbility extends DelayedTriggeredAbility {
-
-    private static final FilterControlledCreaturePermanent filter
-            = new FilterControlledCreaturePermanent("another creature you control");
-
-    static {
-        filter.add(AnotherPredicate.instance);
-    }
-
-    public SkyriderPatrolReflexiveTriggeredAbility() {
-        super(new AddCountersTargetEffect(CounterType.P1P1.createInstance()), Duration.OneUse, true);
-        this.addEffect(new GainAbilityTargetEffect(
-                FlyingAbility.getInstance(),
-                Duration.EndOfTurn
-        ));
-        this.addTarget(new TargetPermanent(filter));
-    }
-
-    public SkyriderPatrolReflexiveTriggeredAbility(final SkyriderPatrolReflexiveTriggeredAbility ability) {
-        super(ability);
-    }
-
-    @Override
-    public SkyriderPatrolReflexiveTriggeredAbility copy() {
-        return new SkyriderPatrolReflexiveTriggeredAbility(this);
-    }
-
-    @Override
-    public boolean checkEventType(GameEvent event, Game game) {
-        return event.getType() == GameEvent.EventType.OPTION_USED;
-    }
-
-    @Override
-    public boolean checkTrigger(GameEvent event, Game game) {
-        return event.getPlayerId().equals(this.getControllerId())
-                && event.getSourceId().equals(this.getSourceId());
-    }
-
-    @Override
-    public String getRule() {
-        return "When you pay {G}{U}, put a +1/+1 counter "
-                + "on another target creature you control, "
-                + "and that creature gains flying until end of turn.";
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/s/SorinImperiousBloodlord.java b/Mage.Sets/src/mage/cards/s/SorinImperiousBloodlord.java
index 038ddf9ead..8c720fcb43 100644
--- a/Mage.Sets/src/mage/cards/s/SorinImperiousBloodlord.java
+++ b/Mage.Sets/src/mage/cards/s/SorinImperiousBloodlord.java
@@ -1,12 +1,15 @@
 package mage.cards.s;
 
 import mage.abilities.Ability;
-import mage.abilities.DelayedTriggeredAbility;
 import mage.abilities.LoyaltyAbility;
 import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
+import mage.abilities.common.delayed.ReflexiveTriggeredAbility;
 import mage.abilities.costs.common.SacrificeTargetCost;
 import mage.abilities.effects.OneShotEffect;
-import mage.abilities.effects.common.*;
+import mage.abilities.effects.common.DamageTargetEffect;
+import mage.abilities.effects.common.DoWhenCostPaid;
+import mage.abilities.effects.common.GainLifeEffect;
+import mage.abilities.effects.common.PutCardFromHandOntoBattlefieldEffect;
 import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
 import mage.abilities.keyword.DeathtouchAbility;
 import mage.abilities.keyword.LifelinkAbility;
@@ -21,7 +24,6 @@ import mage.filter.FilterCard;
 import mage.filter.common.FilterControlledPermanent;
 import mage.filter.common.FilterCreatureCard;
 import mage.game.Game;
-import mage.game.events.GameEvent;
 import mage.game.permanent.Permanent;
 import mage.target.common.TargetAnyTarget;
 import mage.target.common.TargetControlledCreaturePermanent;
@@ -58,11 +60,16 @@ public final class SorinImperiousBloodlord extends CardImpl {
         this.addAbility(ability);
 
         // +1: You may sacrifice a Vampire. When you do, Sorin, Imperious Bloodlord deals 3 damage to any target and you gain 3 life.
-        this.addAbility(new LoyaltyAbility(new DoIfCostPaid(
-                new SorinImperiousBloodlordCreateReflexiveTriggerEffect(),
-                new SacrificeTargetCost(new TargetControlledPermanent(filter))
-        ).setText("You may sacrifice a Vampire. " +
-                "When you do, {this} deals 3 damage to any target and you gain 3 life."
+        ReflexiveTriggeredAbility triggeredAbility = new ReflexiveTriggeredAbility(
+                new DamageTargetEffect(3), false,
+                "{this} deals 3 damage to any target and you gain 3 life"
+        );
+        triggeredAbility.addEffect(new GainLifeEffect(3));
+        triggeredAbility.addTarget(new TargetAnyTarget());
+        this.addAbility(new LoyaltyAbility(new DoWhenCostPaid(
+                triggeredAbility,
+                new SacrificeTargetCost(new TargetControlledPermanent(filter)),
+                "Sacrifice a Vampire?"
         ), 1));
 
         // −3: You may put a Vampire creature card from your hand onto the battlefield.
@@ -110,58 +117,3 @@ class SorinImperiousBloodlordEffect extends OneShotEffect {
         return true;
     }
 }
-
-class SorinImperiousBloodlordCreateReflexiveTriggerEffect extends OneShotEffect {
-
-    SorinImperiousBloodlordCreateReflexiveTriggerEffect() {
-        super(Benefit);
-    }
-
-    private SorinImperiousBloodlordCreateReflexiveTriggerEffect(final SorinImperiousBloodlordCreateReflexiveTriggerEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public SorinImperiousBloodlordCreateReflexiveTriggerEffect copy() {
-        return new SorinImperiousBloodlordCreateReflexiveTriggerEffect(this);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        game.addDelayedTriggeredAbility(new SorinImperiousBloodlordReflexiveTriggeredAbility(), source);
-        return new SendOptionUsedEventEffect().apply(game, source);
-    }
-}
-
-class SorinImperiousBloodlordReflexiveTriggeredAbility extends DelayedTriggeredAbility {
-    SorinImperiousBloodlordReflexiveTriggeredAbility() {
-        super(new DamageTargetEffect(3), Duration.OneUse, true);
-        this.addEffect(new GainLifeEffect(3));
-        this.addTarget(new TargetAnyTarget());
-    }
-
-    private SorinImperiousBloodlordReflexiveTriggeredAbility(final SorinImperiousBloodlordReflexiveTriggeredAbility ability) {
-        super(ability);
-    }
-
-    @Override
-    public SorinImperiousBloodlordReflexiveTriggeredAbility copy() {
-        return new SorinImperiousBloodlordReflexiveTriggeredAbility(this);
-    }
-
-    @Override
-    public boolean checkEventType(GameEvent event, Game game) {
-        return event.getType() == GameEvent.EventType.OPTION_USED;
-    }
-
-    @Override
-    public boolean checkTrigger(GameEvent event, Game game) {
-        return event.getPlayerId().equals(this.getControllerId())
-                && event.getSourceId().equals(this.getSourceId());
-    }
-
-    @Override
-    public String getRule() {
-        return "{this} deals 3 damage to any target and you gain 3 life";
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/s/SparktongueDragon.java b/Mage.Sets/src/mage/cards/s/SparktongueDragon.java
index ae8dae4410..5396b970d1 100644
--- a/Mage.Sets/src/mage/cards/s/SparktongueDragon.java
+++ b/Mage.Sets/src/mage/cards/s/SparktongueDragon.java
@@ -1,28 +1,21 @@
 package mage.cards.s;
 
-import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.Ability;
-import mage.abilities.DelayedTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.common.delayed.ReflexiveTriggeredAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
-import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.DamageTargetEffect;
-import mage.abilities.effects.common.DoIfCostPaid;
-import mage.abilities.effects.common.SendOptionUsedEventEffect;
-import mage.constants.SubType;
+import mage.abilities.effects.common.DoWhenCostPaid;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.Duration;
-import mage.constants.Outcome;
-import mage.game.Game;
-import mage.game.events.GameEvent;
+import mage.constants.SubType;
 import mage.target.common.TargetAnyTarget;
 
+import java.util.UUID;
+
 /**
- *
  * @author TheElk801
  */
 public final class SparktongueDragon extends CardImpl {
@@ -38,16 +31,18 @@ public final class SparktongueDragon extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
 
         // When Sparktongue Dragon enters the battlefield, you may pay {2}{R}. When you do, it deals 3 damage to any target.
-        this.addAbility(new EntersBattlefieldTriggeredAbility(
-                new DoIfCostPaid(
-                        new SparktongueDragonCreateReflexiveTriggerEffect(),
-                        new ManaCostsImpl("{2}{R}"),
-                        "Pay {2}{R} to deal 3 damage?"
-                ).setText("you may pay {2}{R}. When you do, it deals 3 damage to any target")
-        ));
+        ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility(
+                new DamageTargetEffect(3), false,
+                "it deals 3 damage to any target"
+        );
+        ability.addTarget(new TargetAnyTarget());
+        this.addAbility(new EntersBattlefieldTriggeredAbility(new DoWhenCostPaid(
+                ability, new ManaCostsImpl("{2}{R}"),
+                "Pay {2}{R} to deal 3 damage?"
+        )));
     }
 
-    public SparktongueDragon(final SparktongueDragon card) {
+    private SparktongueDragon(final SparktongueDragon card) {
         super(card);
     }
 
@@ -56,59 +51,3 @@ public final class SparktongueDragon extends CardImpl {
         return new SparktongueDragon(this);
     }
 }
-
-class SparktongueDragonCreateReflexiveTriggerEffect extends OneShotEffect {
-
-    public SparktongueDragonCreateReflexiveTriggerEffect() {
-        super(Outcome.Benefit);
-        this.staticText = "When you do, it deals 3 damage to any target";
-    }
-
-    public SparktongueDragonCreateReflexiveTriggerEffect(final SparktongueDragonCreateReflexiveTriggerEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public SparktongueDragonCreateReflexiveTriggerEffect copy() {
-        return new SparktongueDragonCreateReflexiveTriggerEffect(this);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        game.addDelayedTriggeredAbility(new SparktongueDragonReflexiveTriggeredAbility(), source);
-        return new SendOptionUsedEventEffect().apply(game, source);
-    }
-}
-
-class SparktongueDragonReflexiveTriggeredAbility extends DelayedTriggeredAbility {
-
-    public SparktongueDragonReflexiveTriggeredAbility() {
-        super(new DamageTargetEffect(3), Duration.OneUse, true);
-        this.addTarget(new TargetAnyTarget());
-    }
-
-    public SparktongueDragonReflexiveTriggeredAbility(final SparktongueDragonReflexiveTriggeredAbility ability) {
-        super(ability);
-    }
-
-    @Override
-    public SparktongueDragonReflexiveTriggeredAbility copy() {
-        return new SparktongueDragonReflexiveTriggeredAbility(this);
-    }
-
-    @Override
-    public boolean checkEventType(GameEvent event, Game game) {
-        return event.getType() == GameEvent.EventType.OPTION_USED;
-    }
-
-    @Override
-    public boolean checkTrigger(GameEvent event, Game game) {
-        return event.getPlayerId().equals(this.getControllerId())
-                && event.getSourceId().equals(this.getSourceId());
-    }
-
-    @Override
-    public String getRule() {
-        return "When you pay {2}{R}, {this} deals 3 damage to any target";
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/t/TheRoyalScions.java b/Mage.Sets/src/mage/cards/t/TheRoyalScions.java
index 83fda327c0..291bbe9b25 100644
--- a/Mage.Sets/src/mage/cards/t/TheRoyalScions.java
+++ b/Mage.Sets/src/mage/cards/t/TheRoyalScions.java
@@ -1,9 +1,9 @@
 package mage.cards.t;
 
 import mage.abilities.Ability;
-import mage.abilities.DelayedTriggeredAbility;
 import mage.abilities.LoyaltyAbility;
 import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
+import mage.abilities.common.delayed.ReflexiveTriggeredAbility;
 import mage.abilities.dynamicvalue.common.CardsInControllerHandCount;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.OneShotEffect;
@@ -18,7 +18,6 @@ import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.*;
 import mage.game.Game;
-import mage.game.events.GameEvent;
 import mage.target.common.TargetAnyTarget;
 import mage.target.common.TargetCreaturePermanent;
 
@@ -89,41 +88,12 @@ class TheRoyalScionsCreateReflexiveTriggerEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         effect.apply(game, source);
-        game.addDelayedTriggeredAbility(new TheRoyalScionsReflexiveTriggeredAbility(), source);
-        game.fireEvent(GameEvent.getEvent(GameEvent.EventType.OPTION_USED, source.getOriginalId(), source.getSourceId(), source.getControllerId(), 0));
+        ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility(
+                new DamageTargetEffect(CardsInControllerHandCount.instance), false,
+                "{this} deals damage to any target equal to the number of cards in your hand"
+        );
+        ability.addTarget(new TargetAnyTarget());
+        game.fireReflexiveTriggeredAbility(ability, source);
         return true;
     }
 }
-
-class TheRoyalScionsReflexiveTriggeredAbility extends DelayedTriggeredAbility {
-
-    TheRoyalScionsReflexiveTriggeredAbility() {
-        super(new DamageTargetEffect(CardsInControllerHandCount.instance), Duration.OneUse, true);
-        this.addTarget(new TargetAnyTarget());
-    }
-
-    private TheRoyalScionsReflexiveTriggeredAbility(final TheRoyalScionsReflexiveTriggeredAbility ability) {
-        super(ability);
-    }
-
-    @Override
-    public TheRoyalScionsReflexiveTriggeredAbility copy() {
-        return new TheRoyalScionsReflexiveTriggeredAbility(this);
-    }
-
-    @Override
-    public boolean checkEventType(GameEvent event, Game game) {
-        return event.getType() == GameEvent.EventType.OPTION_USED;
-    }
-
-    @Override
-    public boolean checkTrigger(GameEvent event, Game game) {
-        return event.getPlayerId().equals(this.getControllerId())
-                && event.getSourceId().equals(this.getSourceId());
-    }
-
-    @Override
-    public String getRule() {
-        return "When you do, {this} deals damage to any target equal to the number of cards in your hand.";
-    }
-}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/v/ViviensInvocation.java b/Mage.Sets/src/mage/cards/v/ViviensInvocation.java
index 23d46f0a18..b3ed065a12 100644
--- a/Mage.Sets/src/mage/cards/v/ViviensInvocation.java
+++ b/Mage.Sets/src/mage/cards/v/ViviensInvocation.java
@@ -1,30 +1,24 @@
 package mage.cards.v;
 
-import java.util.UUID;
 import mage.MageObjectReference;
 import mage.abilities.Ability;
-import mage.abilities.DelayedTriggeredAbility;
+import mage.abilities.common.delayed.ReflexiveTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
-import mage.abilities.effects.common.SendOptionUsedEventEffect;
-import mage.cards.Card;
-import mage.cards.CardImpl;
-import mage.cards.CardSetInfo;
-import mage.cards.Cards;
-import mage.cards.CardsImpl;
+import mage.cards.*;
 import mage.constants.CardType;
-import mage.constants.Duration;
 import mage.constants.Outcome;
 import mage.constants.Zone;
+import mage.filter.FilterCard;
 import mage.filter.common.FilterCreatureCard;
 import mage.game.Game;
-import mage.game.events.GameEvent;
 import mage.game.permanent.Permanent;
 import mage.players.Player;
 import mage.target.TargetCard;
 import mage.target.common.TargetOpponentsCreaturePermanent;
 
+import java.util.UUID;
+
 /**
- *
  * @author TheElk801
  */
 public final class ViviensInvocation extends CardImpl {
@@ -36,7 +30,7 @@ public final class ViviensInvocation extends CardImpl {
         this.getSpellAbility().addEffect(new ViviensInvocationEffect());
     }
 
-    public ViviensInvocation(final ViviensInvocation card) {
+    private ViviensInvocation(final ViviensInvocation card) {
         super(card);
     }
 
@@ -48,11 +42,9 @@ public final class ViviensInvocation extends CardImpl {
 
 class ViviensInvocationEffect extends OneShotEffect {
 
-    public ViviensInvocationEffect(final ViviensInvocationEffect effect) {
-        super(effect);
-    }
+    private static final FilterCard filter = new FilterCreatureCard("creature card to put on the battlefield");
 
-    public ViviensInvocationEffect() {
+    ViviensInvocationEffect() {
         super(Outcome.PutCreatureInPlay);
         this.staticText = "Look at the top seven cards of your library. "
                 + "You may put a creature card from among them onto the battlefield. "
@@ -61,6 +53,10 @@ class ViviensInvocationEffect extends OneShotEffect {
                 + "it deals damage equal to its power to target creature an opponent controls";
     }
 
+    private ViviensInvocationEffect(final ViviensInvocationEffect effect) {
+        super(effect);
+    }
+
     @Override
     public boolean apply(Game game, Ability source) {
         Player controller = game.getPlayer(source.getControllerId());
@@ -68,31 +64,32 @@ class ViviensInvocationEffect extends OneShotEffect {
             return false;
         }
         Cards cards = new CardsImpl(controller.getLibrary().getTopCards(game, 7));
-        if (!cards.isEmpty()) {
-            TargetCard target = new TargetCard(
-                    Zone.LIBRARY,
-                    new FilterCreatureCard("creature card to put on the battlefield")
-            );
-            target.setNotTarget(true);
-            if (controller.choose(Outcome.PutCreatureInPlay, cards, target, game)) {
-                Card card = cards.get(target.getFirstTarget(), game);
-                if (card != null) {
-                    cards.remove(card);
-                    controller.moveCards(card, Zone.BATTLEFIELD, source, game);
-                    Permanent permanent = game.getPermanent(card.getId());
-                    if (permanent != null) {
-                        game.addDelayedTriggeredAbility(
-                                new ViviensInvocationReflexiveTriggeredAbility(
-                                        new MageObjectReference(permanent, game)
-                                ), source);
-                        new SendOptionUsedEventEffect().apply(game, source);
-                    }
-                }
-            }
-            if (!cards.isEmpty()) {
-                controller.putCardsOnBottomOfLibrary(cards, game, source, false);
-            }
+        if (cards.isEmpty()) {
+            return true;
         }
+        TargetCard target = new TargetCard(Zone.LIBRARY, filter);
+        target.setNotTarget(true);
+        controller.choose(Outcome.PutCreatureInPlay, cards, target, game);
+        Card card = cards.get(target.getFirstTarget(), game);
+        if (card == null) {
+            controller.putCardsOnBottomOfLibrary(cards, game, source, false);
+            return true;
+        }
+        if (controller.moveCards(card, Zone.BATTLEFIELD, source, game)) {
+            cards.remove(card);
+        }
+        Permanent permanent = game.getPermanent(card.getId());
+        if (permanent == null) {
+            controller.putCardsOnBottomOfLibrary(cards, game, source, false);
+            return true;
+        }
+        ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility(
+                new ViviensInvocationDamageEffect(new MageObjectReference(permanent, game)), false,
+                "it deals damage equals to its power to target creature an opponent controls"
+        );
+        ability.addTarget(new TargetOpponentsCreaturePermanent());
+        game.fireReflexiveTriggeredAbility(ability, source);
+        controller.putCardsOnBottomOfLibrary(cards, game, source, false);
         return true;
     }
 
@@ -102,50 +99,16 @@ class ViviensInvocationEffect extends OneShotEffect {
     }
 }
 
-class ViviensInvocationReflexiveTriggeredAbility extends DelayedTriggeredAbility {
-
-    public ViviensInvocationReflexiveTriggeredAbility(MageObjectReference mor) {
-        super(new ViviensInvocationDamageEffect(mor), Duration.OneUse, false);
-        this.addTarget(new TargetOpponentsCreaturePermanent());
-    }
-
-    public ViviensInvocationReflexiveTriggeredAbility(final ViviensInvocationReflexiveTriggeredAbility ability) {
-        super(ability);
-    }
-
-    @Override
-    public ViviensInvocationReflexiveTriggeredAbility copy() {
-        return new ViviensInvocationReflexiveTriggeredAbility(this);
-    }
-
-    @Override
-    public boolean checkEventType(GameEvent event, Game game) {
-        return event.getType() == GameEvent.EventType.OPTION_USED;
-    }
-
-    @Override
-    public boolean checkTrigger(GameEvent event, Game game) {
-        return event.getPlayerId().equals(this.getControllerId())
-                && event.getSourceId().equals(this.getSourceId());
-    }
-
-    @Override
-    public String getRule() {
-        return "When a creature is put onto the battlefield with {this}, "
-                + "it deals damage equal to its power to target creature an opponent controls";
-    }
-}
-
 class ViviensInvocationDamageEffect extends OneShotEffect {
 
     private final MageObjectReference mor;
 
-    public ViviensInvocationDamageEffect(MageObjectReference mor) {
+    ViviensInvocationDamageEffect(MageObjectReference mor) {
         super(Outcome.Benefit);
         this.mor = mor;
     }
 
-    public ViviensInvocationDamageEffect(final ViviensInvocationDamageEffect effect) {
+    private ViviensInvocationDamageEffect(final ViviensInvocationDamageEffect effect) {
         super(effect);
         mor = effect.mor;
     }
diff --git a/Mage.Sets/src/mage/cards/w/WildbornPreserver.java b/Mage.Sets/src/mage/cards/w/WildbornPreserver.java
index 0fe9b9256a..49ba457eaf 100644
--- a/Mage.Sets/src/mage/cards/w/WildbornPreserver.java
+++ b/Mage.Sets/src/mage/cards/w/WildbornPreserver.java
@@ -2,8 +2,8 @@ package mage.cards.w;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.DelayedTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility;
+import mage.abilities.common.delayed.ReflexiveTriggeredAbility;
 import mage.abilities.costs.mana.GenericManaCost;
 import mage.abilities.costs.mana.ManaCosts;
 import mage.abilities.costs.mana.ManaCostsImpl;
@@ -14,7 +14,6 @@ import mage.abilities.keyword.ReachAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.Duration;
 import mage.constants.Outcome;
 import mage.constants.SubType;
 import mage.counters.CounterType;
@@ -23,7 +22,6 @@ import mage.filter.common.FilterCreaturePermanent;
 import mage.filter.predicate.Predicates;
 import mage.filter.predicate.permanent.AnotherPredicate;
 import mage.game.Game;
-import mage.game.events.GameEvent;
 import mage.players.Player;
 
 import java.util.UUID;
@@ -74,11 +72,11 @@ class WildbornPreserverCreateReflexiveTriggerEffect extends OneShotEffect {
 
     WildbornPreserverCreateReflexiveTriggerEffect() {
         super(Outcome.Benefit);
+        staticText = "you may pay {X}. When you do, put X +1/+1 counters on {this}";
     }
 
     private WildbornPreserverCreateReflexiveTriggerEffect(final WildbornPreserverCreateReflexiveTriggerEffect effect) {
         super(effect);
-        staticText = "you may pay {X}. When you do, put X +1/+1 counters on {this}";
     }
 
     @Override
@@ -101,40 +99,10 @@ class WildbornPreserverCreateReflexiveTriggerEffect extends OneShotEffect {
         if (!cost.pay(source, game, source.getSourceId(), source.getControllerId(), false, null)) {
             return false;
         }
-        game.addDelayedTriggeredAbility(new WildbornPreserverReflexiveTriggeredAbility(costX), source);
-        game.fireEvent(GameEvent.getEvent(GameEvent.EventType.OPTION_USED, source.getOriginalId(), source.getSourceId(), source.getControllerId(), 0));
+        game.fireReflexiveTriggeredAbility(new ReflexiveTriggeredAbility(
+                new AddCountersSourceEffect(CounterType.P1P1.createInstance(costX)),
+                false, "put X +1/+1 counters on {this}"
+        ), source);
         return true;
     }
 }
-
-class WildbornPreserverReflexiveTriggeredAbility extends DelayedTriggeredAbility {
-
-    WildbornPreserverReflexiveTriggeredAbility(int counters) {
-        super(new AddCountersSourceEffect(CounterType.P1P1.createInstance(counters)), Duration.OneUse, true);
-    }
-
-    private WildbornPreserverReflexiveTriggeredAbility(final WildbornPreserverReflexiveTriggeredAbility ability) {
-        super(ability);
-    }
-
-    @Override
-    public WildbornPreserverReflexiveTriggeredAbility copy() {
-        return new WildbornPreserverReflexiveTriggeredAbility(this);
-    }
-
-    @Override
-    public boolean checkEventType(GameEvent event, Game game) {
-        return event.getType() == GameEvent.EventType.OPTION_USED;
-    }
-
-    @Override
-    public boolean checkTrigger(GameEvent event, Game game) {
-        return event.getPlayerId().equals(this.getControllerId())
-                && event.getSourceId().equals(this.getSourceId());
-    }
-
-    @Override
-    public String getRule() {
-        return "When you do, put X +1/+1 counters on {this}.";
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/y/YannikScavengingSentinel.java b/Mage.Sets/src/mage/cards/y/YannikScavengingSentinel.java
index 7efdb18ffc..a0f527cdec 100644
--- a/Mage.Sets/src/mage/cards/y/YannikScavengingSentinel.java
+++ b/Mage.Sets/src/mage/cards/y/YannikScavengingSentinel.java
@@ -2,12 +2,11 @@ package mage.cards.y;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.DelayedTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.common.delayed.OnLeaveReturnExiledToBattlefieldAbility;
+import mage.abilities.common.delayed.ReflexiveTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.ExileTargetEffect;
-import mage.abilities.effects.common.SendOptionUsedEventEffect;
 import mage.abilities.effects.common.counter.DistributeCountersEffect;
 import mage.abilities.keyword.PartnerWithAbility;
 import mage.abilities.keyword.VigilanceAbility;
@@ -19,7 +18,6 @@ import mage.filter.FilterPermanent;
 import mage.filter.common.FilterControlledCreaturePermanent;
 import mage.filter.predicate.permanent.AnotherPredicate;
 import mage.game.Game;
-import mage.game.events.GameEvent;
 import mage.game.permanent.Permanent;
 import mage.players.Player;
 import mage.target.TargetPermanent;
@@ -109,43 +107,15 @@ class YannikScavengingSentinelEffect extends OneShotEffect {
         ), permanent.getIdName()).setTargetPointer(new FixedTarget(permanent, game)).apply(game, source);
         game.addDelayedTriggeredAbility(new OnLeaveReturnExiledToBattlefieldAbility(), source);
         if (game.getState().getZone(permanent.getId()) != Zone.BATTLEFIELD) {
-            game.addDelayedTriggeredAbility(new YannikScavengingSentinelReflexiveTriggeredAbility(power), source);
-            return new SendOptionUsedEventEffect().apply(game, source);
+            ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility(
+                    new DistributeCountersEffect(
+                            CounterType.P1P1, power, false, ""
+                    ), false, "distribute X +1/+1 counters among any number of target creatures, " +
+                    "where X is the exiled creature's power"
+            );
+            ability.addTarget(new TargetCreaturePermanentAmount(power));
+            game.fireReflexiveTriggeredAbility(ability, source);
         }
         return true;
     }
 }
-
-class YannikScavengingSentinelReflexiveTriggeredAbility extends DelayedTriggeredAbility {
-
-    YannikScavengingSentinelReflexiveTriggeredAbility(int power) {
-        super(new DistributeCountersEffect(CounterType.P1P1, power, false, ""), Duration.OneUse, true);
-        this.addTarget(new TargetCreaturePermanentAmount(power));
-    }
-
-    private YannikScavengingSentinelReflexiveTriggeredAbility(final YannikScavengingSentinelReflexiveTriggeredAbility ability) {
-        super(ability);
-    }
-
-    @Override
-    public YannikScavengingSentinelReflexiveTriggeredAbility copy() {
-        return new YannikScavengingSentinelReflexiveTriggeredAbility(this);
-    }
-
-    @Override
-    public boolean checkEventType(GameEvent event, Game game) {
-        return event.getType() == GameEvent.EventType.OPTION_USED;
-    }
-
-    @Override
-    public boolean checkTrigger(GameEvent event, Game game) {
-        return event.getPlayerId().equals(this.getControllerId())
-                && event.getSourceId().equals(this.getSourceId());
-    }
-
-    @Override
-    public String getRule() {
-        return "Distribute X +1/+1 counters among any number of target creatures, " +
-                "where X is the exiled creature's power.";
-    }
-}
diff --git a/Mage/src/main/java/mage/abilities/common/delayed/ReflexiveTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/delayed/ReflexiveTriggeredAbility.java
new file mode 100644
index 0000000000..08fd88c78f
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/common/delayed/ReflexiveTriggeredAbility.java
@@ -0,0 +1,50 @@
+package mage.abilities.common.delayed;
+
+import mage.abilities.DelayedTriggeredAbility;
+import mage.abilities.effects.Effect;
+import mage.constants.Duration;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+
+/**
+ * @author TheElk801
+ */
+public class ReflexiveTriggeredAbility extends DelayedTriggeredAbility {
+
+    private final String text;
+
+    public ReflexiveTriggeredAbility(Effect effect, boolean optional, String text) {
+        super(effect, Duration.EndOfTurn, true, optional);
+        this.text = text;
+    }
+
+    protected ReflexiveTriggeredAbility(final ReflexiveTriggeredAbility ability) {
+        super(ability);
+        this.text = ability.text;
+    }
+
+    @Override
+    public boolean checkEventType(GameEvent event, Game game) {
+        return event.getType() == GameEvent.EventType.OPTION_USED;
+    }
+
+    @Override
+    public boolean checkTrigger(GameEvent event, Game game) {
+        return event.getPlayerId().equals(this.getControllerId())
+                && event.getSourceId().equals(this.getSourceId());
+    }
+
+    @Override
+    public String getRule() {
+        return text.substring(0, 1).toUpperCase() + text.substring(1) + '.';
+    }
+
+    public String getText() {
+        return text;
+    }
+
+    @Override
+    public ReflexiveTriggeredAbility copy() {
+        return new ReflexiveTriggeredAbility(this);
+    }
+}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/DoWhenCostPaid.java b/Mage/src/main/java/mage/abilities/effects/common/DoWhenCostPaid.java
new file mode 100644
index 0000000000..7744212cf1
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/effects/common/DoWhenCostPaid.java
@@ -0,0 +1,103 @@
+package mage.abilities.effects.common;
+
+import mage.MageObject;
+import mage.abilities.Ability;
+import mage.abilities.Mode;
+import mage.abilities.common.delayed.ReflexiveTriggeredAbility;
+import mage.abilities.costs.Cost;
+import mage.abilities.effects.OneShotEffect;
+import mage.constants.Outcome;
+import mage.game.Game;
+import mage.players.Player;
+import mage.util.CardUtil;
+
+import java.util.Locale;
+
+public class DoWhenCostPaid extends OneShotEffect {
+
+    private final ReflexiveTriggeredAbility ability;
+    private final Cost cost;
+    private final String chooseUseText;
+    private final boolean optional;
+
+    public DoWhenCostPaid(ReflexiveTriggeredAbility ability, Cost cost, String chooseUseText) {
+        this(ability, cost, chooseUseText, true);
+    }
+
+    public DoWhenCostPaid(ReflexiveTriggeredAbility ability, Cost cost, String chooseUseText, boolean optional) {
+        super(Outcome.Benefit);
+        this.ability = ability;
+        this.cost = cost;
+        this.chooseUseText = chooseUseText;
+        this.optional = optional;
+    }
+
+    private DoWhenCostPaid(final DoWhenCostPaid effect) {
+        super(effect);
+        this.ability = effect.ability.copy();
+        this.cost = effect.cost.copy();
+        this.chooseUseText = effect.chooseUseText;
+        this.optional = effect.optional;
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Player player = game.getPlayer(source.getControllerId());
+        MageObject mageObject = game.getObject(source.getSourceId());
+        if (player == null || mageObject == null) {
+            return false;
+        }
+        String message = CardUtil.replaceSourceName(chooseUseText, mageObject.getLogName());
+        Outcome payOutcome = ability.getEffects().getOutcome(source, this.outcome);
+        if (!cost.canPay(source, source.getSourceId(), player.getId(), game)
+                || (optional && !player.chooseUse(payOutcome, message, source, game))) {
+            return false;
+        }
+        cost.clearPaid();
+        int bookmark = game.bookmarkState();
+        if (cost.pay(source, game, source.getSourceId(), player.getId(), false)) {
+            game.fireReflexiveTriggeredAbility(ability, source);
+            player.resetStoredBookmark(game);
+        }
+        return true;
+    }
+
+    public Cost getCost() {
+        return cost;
+    }
+
+    @Override
+    public String getText(Mode mode) {
+        if (!staticText.isEmpty()) {
+            return staticText;
+        }
+        return (optional ? "you may " : "") + getCostText() + ". When you do, " + ability.getText();
+    }
+
+    private String getCostText() {
+        StringBuilder sb = new StringBuilder();
+        String costText = cost.getText();
+        if (costText != null
+                && !costText.toLowerCase(Locale.ENGLISH).startsWith("put")
+                && !costText.toLowerCase(Locale.ENGLISH).startsWith("return")
+                && !costText.toLowerCase(Locale.ENGLISH).startsWith("exile")
+                && !costText.toLowerCase(Locale.ENGLISH).startsWith("discard")
+                && !costText.toLowerCase(Locale.ENGLISH).startsWith("sacrifice")
+                && !costText.toLowerCase(Locale.ENGLISH).startsWith("remove")
+                && !costText.toLowerCase(Locale.ENGLISH).startsWith("pay")) {
+            sb.append("pay ");
+        }
+        return sb.append(costText).toString();
+    }
+
+    @Override
+    public void setValue(String key, Object value) {
+        super.setValue(key, value);
+        ability.getEffects().setValue(key, value);
+    }
+
+    @Override
+    public DoWhenCostPaid copy() {
+        return new DoWhenCostPaid(this);
+    }
+}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/SendOptionUsedEventEffect.java b/Mage/src/main/java/mage/abilities/effects/common/SendOptionUsedEventEffect.java
deleted file mode 100644
index 462f650bae..0000000000
--- a/Mage/src/main/java/mage/abilities/effects/common/SendOptionUsedEventEffect.java
+++ /dev/null
@@ -1,42 +0,0 @@
-
-package mage.abilities.effects.common;
-
-import mage.abilities.Ability;
-import mage.abilities.effects.OneShotEffect;
-import mage.constants.Outcome;
-import mage.game.Game;
-import mage.game.events.GameEvent;
-
-/**
- *
- * @author LevelX2
- */
-public class SendOptionUsedEventEffect extends OneShotEffect {
-
-    private final int value;
-
-    public SendOptionUsedEventEffect() {
-        this(0);
-    }
-
-    public SendOptionUsedEventEffect(int value) {
-        super(Outcome.Detriment);
-        this.value = value;
-    }
-
-    public SendOptionUsedEventEffect(final SendOptionUsedEventEffect effect) {
-        super(effect);
-        this.value = effect.value;
-    }
-
-    @Override
-    public SendOptionUsedEventEffect copy() {
-        return new SendOptionUsedEventEffect(this);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        game.fireEvent(GameEvent.getEvent(GameEvent.EventType.OPTION_USED, source.getOriginalId(), source.getSourceId(), source.getControllerId(), value));
-        return true;
-    }
-}
diff --git a/Mage/src/main/java/mage/game/Game.java b/Mage/src/main/java/mage/game/Game.java
index b41080c668..ebf4dad93b 100644
--- a/Mage/src/main/java/mage/game/Game.java
+++ b/Mage/src/main/java/mage/game/Game.java
@@ -6,6 +6,7 @@ import mage.abilities.Ability;
 import mage.abilities.ActivatedAbility;
 import mage.abilities.DelayedTriggeredAbility;
 import mage.abilities.TriggeredAbility;
+import mage.abilities.common.delayed.ReflexiveTriggeredAbility;
 import mage.abilities.effects.ContinuousEffect;
 import mage.abilities.effects.ContinuousEffects;
 import mage.abilities.effects.PreventionEffectData;
@@ -398,6 +399,8 @@ public interface Game extends MageItem, Serializable {
 
     UUID addDelayedTriggeredAbility(DelayedTriggeredAbility delayedAbility, Ability source);
 
+    UUID fireReflexiveTriggeredAbility(ReflexiveTriggeredAbility reflexiveAbility, Ability source);
+
     void applyEffects();
 
     boolean checkStateAndTriggered();
diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java
index b5470ba96f..018559d14f 100644
--- a/Mage/src/main/java/mage/game/GameImpl.java
+++ b/Mage/src/main/java/mage/game/GameImpl.java
@@ -6,6 +6,7 @@ import mage.abilities.*;
 import mage.abilities.common.AttachableToRestrictedAbility;
 import mage.abilities.common.CantHaveMoreThanAmountCountersSourceAbility;
 import mage.abilities.common.SagaAbility;
+import mage.abilities.common.delayed.ReflexiveTriggeredAbility;
 import mage.abilities.effects.ContinuousEffect;
 import mage.abilities.effects.ContinuousEffects;
 import mage.abilities.effects.Effect;
@@ -1748,6 +1749,13 @@ public abstract class GameImpl implements Game, Serializable {
         return newAbility.getId();
     }
 
+    @Override
+    public UUID fireReflexiveTriggeredAbility(ReflexiveTriggeredAbility reflexiveAbility, Ability source) {
+        UUID uuid = this.addDelayedTriggeredAbility(reflexiveAbility, source);
+        this.fireEvent(GameEvent.getEvent(GameEvent.EventType.OPTION_USED, source.getOriginalId(), source.getSourceId(), source.getControllerId()));
+        return uuid;
+    }
+
     /**
      * 116.5. Each time a player would get priority, the game first performs all
      * applicable state-based actions as a single event (see rule 704,

From 55a8e34f7a1210c2d84a43fac20c9d3cc34b7612 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 4 May 2020 20:53:52 -0400
Subject: [PATCH 013/586] fixed a merge conflict

---
 Mage.Sets/src/mage/cards/c/CabalTherapist.java | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/c/CabalTherapist.java b/Mage.Sets/src/mage/cards/c/CabalTherapist.java
index 2ec2d13f0f..56ccea0d38 100644
--- a/Mage.Sets/src/mage/cards/c/CabalTherapist.java
+++ b/Mage.Sets/src/mage/cards/c/CabalTherapist.java
@@ -101,8 +101,9 @@ class CabalTherapistDiscardEffect extends OneShotEffect {
             if (CardUtil.haveSameNames(card.getName(), cardName)) {
                 return false;
             }
-        }
-        targetPlayer.revealCards(source, hand, game);
+            return true;
+        });
+        targetPlayer.discard(hand, source, game);
         return true;
     }
 

From 1e8f006448d9e177d5c91d45fbff5253d83a2a5e Mon Sep 17 00:00:00 2001
From: emerald000 <emerald000@hotmail.com>
Date: Mon, 4 May 2020 22:57:50 -0400
Subject: [PATCH 014/586] Streamline London Mulligan UX. - Choose cards to put
 on bottom directly from hand. - Put cards on bottom in order chosen instead
 of asking afterwards.

---
 .../mage/game/mulligan/LondonMulligan.java    | 35 +++++++------------
 1 file changed, 13 insertions(+), 22 deletions(-)

diff --git a/Mage/src/main/java/mage/game/mulligan/LondonMulligan.java b/Mage/src/main/java/mage/game/mulligan/LondonMulligan.java
index 33a213ca23..86917145d8 100644
--- a/Mage/src/main/java/mage/game/mulligan/LondonMulligan.java
+++ b/Mage/src/main/java/mage/game/mulligan/LondonMulligan.java
@@ -1,13 +1,12 @@
 package mage.game.mulligan;
 
-import mage.cards.Cards;
 import mage.cards.CardsImpl;
 import mage.constants.Outcome;
-import mage.constants.Zone;
 import mage.filter.FilterCard;
 import mage.game.Game;
 import mage.players.Player;
-import mage.target.TargetCard;
+import mage.target.Target;
+import mage.target.common.TargetCardInHand;
 
 import java.util.HashMap;
 import java.util.Map;
@@ -15,8 +14,8 @@ import java.util.UUID;
 
 public class LondonMulligan extends Mulligan {
 
-    protected Map<UUID, Integer> startingHandSizes = new HashMap<>();
-    protected Map<UUID, Integer> openingHandSizes = new HashMap<>();
+    private final Map<UUID, Integer> startingHandSizes = new HashMap<>();
+    private final Map<UUID, Integer> openingHandSizes = new HashMap<>();
 
     public LondonMulligan(int freeMulligans) {
         super(freeMulligans);
@@ -93,27 +92,20 @@ public class LondonMulligan extends Mulligan {
         openingHandSizes.put(playerId, openingHandSizes.get(playerId) - deduction);
         int newHandSize = openingHandSizes.get(player.getId());
         if (deduction == 0) {
-            game.fireInformEvent(new StringBuilder(player.getLogName())
-                    .append(" mulligans for free.")
-                    .toString());
+            game.fireInformEvent(player.getLogName() +
+                    " mulligans for free.");
         } else {
-            game.fireInformEvent(new StringBuilder(player.getLogName())
-                    .append(" mulligans")
-                    .append(" down to ")
-                    .append(newHandSize)
-                    .append(newHandSize == 1 ? " card" : " cards").toString());
+            game.fireInformEvent(player.getLogName() +
+                    " mulligans down to " +
+                    newHandSize +
+                    (newHandSize == 1 ? " card" : " cards"));
         }
         player.drawCards(numCards, null, game);
 
-        if (player.getHand().size() > newHandSize) {
-            int cardsToDiscard = player.getHand().size() - newHandSize;
-            Cards cards = new CardsImpl();
-            cards.addAll(player.getHand());
-            TargetCard target = new TargetCard(cardsToDiscard, cardsToDiscard, Zone.HAND,
-                    new FilterCard("card" + (cardsToDiscard > 1 ? "s" : "") + " to PUT on the BOTTOM of your library (Discard for Mulligan)"));
-            player.chooseTarget(Outcome.Neutral, cards, target, null, game);
+        while (player.getHand().size() > newHandSize) {
+            Target target = new TargetCardInHand(new FilterCard("card (" + (player.getHand().size() - newHandSize) + " more) to put on the bottom of your library"));
+            player.chooseTarget(Outcome.Discard, target, null, game);
             player.putCardsOnBottomOfLibrary(new CardsImpl(target.getTargets()), game, null, true);
-            cards.removeAll(target.getTargets());
         }
     }
 
@@ -128,5 +120,4 @@ public class LondonMulligan extends Mulligan {
         mulligan.startingHandSizes.putAll(startingHandSizes);
         return mulligan;
     }
-
 }

From 9097ae5afea6eb6b24c11ded8063efb251f5c2da Mon Sep 17 00:00:00 2001
From: emerald000 <emerald000@hotmail.com>
Date: Mon, 4 May 2020 23:22:37 -0400
Subject: [PATCH 015/586] Fix Quartzwood Crasher combat damage tracking.

Fixes #6512.
---
 .../src/mage/cards/q/QuartzwoodCrasher.java   | 25 +++++++++----------
 1 file changed, 12 insertions(+), 13 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/q/QuartzwoodCrasher.java b/Mage.Sets/src/mage/cards/q/QuartzwoodCrasher.java
index bc7cd43eac..01f05f155e 100644
--- a/Mage.Sets/src/mage/cards/q/QuartzwoodCrasher.java
+++ b/Mage.Sets/src/mage/cards/q/QuartzwoodCrasher.java
@@ -50,7 +50,7 @@ public final class QuartzwoodCrasher extends CardImpl {
 
 class QuartzwoodCrasherTriggeredAbility extends TriggeredAbilityImpl {
 
-    private final Set<UUID> damagedPlayerIds = new HashSet();
+    private final Set<UUID> damagedPlayerIds = new HashSet<>();
 
     QuartzwoodCrasherTriggeredAbility() {
         super(Zone.BATTLEFIELD, new QuartzwoodCrasherEffect(), false);
@@ -78,17 +78,16 @@ class QuartzwoodCrasherTriggeredAbility extends TriggeredAbilityImpl {
             damagedPlayerIds.clear();
             return false;
         }
-        if (event.getType() != GameEvent.EventType.DAMAGED_PLAYER
-                && !((DamagedPlayerEvent) event).isCombatDamage()) {
-            return false;
-        }
-        Permanent creature = game.getPermanent(event.getSourceId());
-        if (creature != null && creature.isControlledBy(controllerId)
-                && creature.getAbilities(game).containsKey(TrampleAbility.getInstance().getId())
-                && !damagedPlayerIds.contains(event.getTargetId())) {
-            damagedPlayerIds.add(event.getTargetId());
-            this.getEffects().setTargetPointer(new FixedTarget(event.getTargetId(), game));
-            return true;
+        if (event.getType() == GameEvent.EventType.DAMAGED_PLAYER
+                && ((DamagedPlayerEvent) event).isCombatDamage()) {
+            Permanent creature = game.getPermanent(event.getSourceId());
+            if (creature != null && creature.isControlledBy(controllerId)
+                    && creature.hasAbility(TrampleAbility.getInstance().getId(), game)
+                    && !damagedPlayerIds.contains(event.getTargetId())) {
+                damagedPlayerIds.add(event.getTargetId());
+                this.getEffects().setTargetPointer(new FixedTarget(event.getTargetId(), game));
+                return true;
+            }
         }
         return false;
     }
@@ -127,7 +126,7 @@ class QuartzwoodCrasherEffect extends OneShotEffect {
 
 class QuartzwoodCrasherWatcher extends Watcher {
 
-    private final Map<UUID, Map<UUID, Integer>> damageMap = new HashMap();
+    private final Map<UUID, Map<UUID, Integer>> damageMap = new HashMap<>();
 
     QuartzwoodCrasherWatcher() {
         super(WatcherScope.GAME);

From 75bc19d4a739a0ba4a2c3108c11871b9f367c1b6 Mon Sep 17 00:00:00 2001
From: emerald000 <emerald000@hotmail.com>
Date: Tue, 5 May 2020 01:24:34 -0400
Subject: [PATCH 016/586] Add a button on the deck editor to change your cards
 to the oldest versions.

---
 .../java/mage/client/cards/DragCardGrid.java  | 46 +++++++++++++++++++
 .../mage/cards/repository/CardRepository.java | 32 +++++++++++++
 2 files changed, 78 insertions(+)

diff --git a/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java b/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java
index 9a7e144f47..8d231eb265 100644
--- a/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java
+++ b/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java
@@ -562,6 +562,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
     JButton selectByButton;
     JButton analyseButton;
     JButton blingButton;
+    JButton oldVersionButton;
 
     // Popup for toolbar
     final JPopupMenu filterPopup;
@@ -716,6 +717,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
         selectByButton = new JButton("Select By");
         analyseButton = new JButton("M"); // "Mana" button
         blingButton = new JButton("B"); // "Bling" button
+        oldVersionButton = new JButton("O"); // "Old version" button
 
         // Name and count label
         deckNameAndCountLabel = new JLabel();
@@ -740,6 +742,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
         toolbarInner.add(visibilityButton);
         toolbarInner.add(analyseButton);
         toolbarInner.add(blingButton);
+        toolbarInner.add(oldVersionButton);
         toolbar.add(toolbarInner, BorderLayout.WEST);
         JPanel sliderPanel = new JPanel(new GridBagLayout());
         sliderPanel.setOpaque(false);
@@ -982,6 +985,11 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
 
         blingButton.addActionListener(evt -> blingDeck());
 
+        // Old version button - Switch cards to the oldest non-promo printing. In case of multiples in a set, take the lowest card number.
+        oldVersionButton.setToolTipText("Switch cards to the oldest non-promo printing");
+
+        oldVersionButton.addActionListener(evt -> oldVersionDeck());
+
         // Filter popup
         filterPopup = new JPopupMenu();
         filterPopup.setPreferredSize(new Dimension(300, 300));
@@ -1567,6 +1575,44 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
         }
     }
 
+    private void oldVersionDeck() {
+        if (this.mode != Constants.DeckEditorMode.FREE_BUILDING) {
+            return;
+        }
+
+        if (JOptionPane.showConfirmDialog(null, "Are you sure you want to switch your card versions to the oldest ones?", "WARNING",
+                JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) {
+            return;
+        }
+
+        List<List<List<CardView>>> newCardGrid = new ArrayList<>();
+        for (List<List<CardView>> gridRow : cardGrid) {
+            List<List<CardView>> newGridRow = new ArrayList<>();
+            for (List<CardView> stack : gridRow) {
+                List<CardView> newStack = new ArrayList<>();
+                for (CardView card : stack) {
+                    CardInfo oldestCardInfo = CardRepository.instance.findOldestNonPromoVersionCard(card.getName());
+                    if (oldestCardInfo != null) {
+                        CardView oldestCardView = new CardView(oldestCardInfo.getMockCard());
+                        this.removeCardView(card);
+                        eventSource.fireEvent(card, ClientEventType.REMOVE_SPECIFIC_CARD);
+                        this.addCardView(oldestCardView, false);
+                        eventSource.fireEvent(oldestCardView, ClientEventType.ADD_SPECIFIC_CARD);
+                        newStack.add(oldestCardView);
+                    } else {
+                        newStack.add(card);
+                    }
+                }
+                newGridRow.add(newStack);
+            }
+            newCardGrid.add(newGridRow);
+        }
+        cardGrid = newCardGrid;
+        layoutGrid();
+        cardScroll.revalidate();
+        repaint();
+    }
+
     // Update the contents of the card grid
     public void setCards(CardsView cardsView, DeckCardLayout layout, BigCard bigCard) {
         if (bigCard != null) {
diff --git a/Mage/src/main/java/mage/cards/repository/CardRepository.java b/Mage/src/main/java/mage/cards/repository/CardRepository.java
index d77c31bd1a..c43e32dd27 100644
--- a/Mage/src/main/java/mage/cards/repository/CardRepository.java
+++ b/Mage/src/main/java/mage/cards/repository/CardRepository.java
@@ -12,6 +12,7 @@ import com.j256.ormlite.support.DatabaseConnection;
 import com.j256.ormlite.table.TableUtils;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
+import mage.constants.SetType;
 import mage.constants.SuperType;
 import mage.game.events.Listener;
 import mage.util.RandomUtil;
@@ -485,6 +486,37 @@ public enum CardRepository {
         return Collections.emptyList();
     }
 
+    public CardInfo findOldestNonPromoVersionCard(String name) {
+        List<CardInfo> allVersions = this.findCards(name);
+        if (!allVersions.isEmpty()) {
+            allVersions.sort(new OldestNonPromoComparator());
+            return allVersions.get(0);
+        } else {
+            return null;
+        }
+    }
+
+    static class OldestNonPromoComparator implements Comparator<CardInfo> {
+        @Override
+        public int compare(CardInfo a, CardInfo b) {
+            ExpansionInfo aSet = ExpansionRepository.instance.getSetByCode(a.getSetCode());
+            ExpansionInfo bSet = ExpansionRepository.instance.getSetByCode(b.getSetCode());
+            if (aSet.getType() == SetType.PROMOTIONAL && bSet.getType() != SetType.PROMOTIONAL) {
+                return 1;
+            }
+            if (bSet.getType() == SetType.PROMOTIONAL && aSet.getType() != SetType.PROMOTIONAL) {
+                return -1;
+            }
+            if (aSet.getReleaseDate().after(bSet.getReleaseDate())) {
+                return 1;
+            }
+            if (aSet.getReleaseDate().before(bSet.getReleaseDate())) {
+                return -1;
+            }
+            return Integer.compare(a.getCardNumberAsInt(), b.getCardNumberAsInt());
+        }
+    }
+
     public long getContentVersionFromDB() {
         try {
             ConnectionSource connectionSource = new JdbcConnectionSource(JDBC_URL);

From 6a294a58d99eb29df3e5fb6ec8ae13101129b588 Mon Sep 17 00:00:00 2001
From: Thomas ARBLAY <thomas.arblay@smile.fr>
Date: Tue, 5 May 2020 14:31:44 +0200
Subject: [PATCH 017/586] Fix text for Marrow Gnawer

---
 Mage.Sets/src/mage/cards/m/MarrowGnawer.java | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/m/MarrowGnawer.java b/Mage.Sets/src/mage/cards/m/MarrowGnawer.java
index cdf48890ef..3f136ef486 100644
--- a/Mage.Sets/src/mage/cards/m/MarrowGnawer.java
+++ b/Mage.Sets/src/mage/cards/m/MarrowGnawer.java
@@ -2,7 +2,6 @@
 
 package mage.cards.m;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleActivatedAbility;
@@ -21,6 +20,8 @@ import mage.filter.common.FilterCreaturePermanent;
 import mage.game.permanent.token.RatToken;
 import mage.target.common.TargetControlledPermanent;
 
+import java.util.UUID;
+
 /**
  *
  * @author LevelX
@@ -51,7 +52,8 @@ public final class MarrowGnawer extends CardImpl {
 
         // {T}, Sacrifice a Rat: create X 1/1 black Rat creature tokens, where X is the number of Rats you control.
         Ability ability;
-        ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new RatToken(),new PermanentsOnBattlefieldCount(filter3)), new SacrificeTargetCost(new TargetControlledPermanent(filterSacrifice)));
+        String abilityText = "create X 1/1 black Rat creature tokens, where X is the number of Rats you control.";
+        ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new RatToken(),new PermanentsOnBattlefieldCount(filter3)).setText(abilityText), new SacrificeTargetCost(new TargetControlledPermanent(filterSacrifice)));
         ability.addCost(new TapSourceCost());
         this.addAbility(ability);
     }

From b4fd4aab58e43a7de4eceeee92bd701beecce455 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 5 May 2020 10:04:07 -0400
Subject: [PATCH 018/586] fixed bookmark usage in DoWhenCostPaid

---
 .../main/java/mage/abilities/effects/common/DoWhenCostPaid.java | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Mage/src/main/java/mage/abilities/effects/common/DoWhenCostPaid.java b/Mage/src/main/java/mage/abilities/effects/common/DoWhenCostPaid.java
index 7744212cf1..71602a0099 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/DoWhenCostPaid.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/DoWhenCostPaid.java
@@ -58,7 +58,9 @@ public class DoWhenCostPaid extends OneShotEffect {
         if (cost.pay(source, game, source.getSourceId(), player.getId(), false)) {
             game.fireReflexiveTriggeredAbility(ability, source);
             player.resetStoredBookmark(game);
+            return true;
         }
+        game.restoreState(bookmark, DoWhenCostPaid.class.getName());
         return true;
     }
 

From a904b7c890503053095e327b3cdc0bfaca6aac44 Mon Sep 17 00:00:00 2001
From: emerald000 <emerald000@hotmail.com>
Date: Tue, 5 May 2020 23:15:45 -0400
Subject: [PATCH 019/586] Fix StubPlayer handling of London mulligans and fix
 related tests.

---
 .../test/mulligan/LondonMulliganTest.java     | 135 ++++++++++++++++--
 .../main/java/mage/players/StubPlayer.java    |  21 ++-
 2 files changed, 136 insertions(+), 20 deletions(-)

diff --git a/Mage.Tests/src/test/java/org/mage/test/mulligan/LondonMulliganTest.java b/Mage.Tests/src/test/java/org/mage/test/mulligan/LondonMulliganTest.java
index ed9a7c8abb..03dc1a6747 100644
--- a/Mage.Tests/src/test/java/org/mage/test/mulligan/LondonMulliganTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/mulligan/LondonMulliganTest.java
@@ -91,7 +91,7 @@ public class LondonMulliganTest extends MulliganTestBase {
         });
         scenario.discardBottom(count -> {
             scenario.assertSizes(7, 33);
-            assertEquals(2, count);
+            assertEquals(1, count);
             assertEquals(hand1, new HashSet<>(scenario.getLibraryRangeSize(19, 7)));
             assertEquals(discarded, scenario.getLibraryRangeSize(26, 1));
             assertEquals(hand2, new HashSet<>(scenario.getLibraryRangeSize(27, 6)));
@@ -101,11 +101,23 @@ public class LondonMulliganTest extends MulliganTestBase {
             remainingHand.addAll(Sets.difference(scenario.getHand(), new HashSet<>(discarded)));
             return discarded;
         });
+        scenario.discardBottom(count -> {
+            scenario.assertSizes(6, 34);
+            assertEquals(1, count);
+            assertEquals(hand1, new HashSet<>(scenario.getLibraryRangeSize(19, 7)));
+            assertEquals(discarded, scenario.getNBottomOfLibrary(1));
+            assertEquals(hand2, new HashSet<>(scenario.getLibraryRangeSize(27, 6)));
+            discarded.clear();
+            remainingHand.clear();
+            scenario.getHand().stream().limit(count).forEach(discarded::add);
+            remainingHand.addAll(Sets.difference(scenario.getHand(), new HashSet<>(discarded)));
+            return discarded;
+        });
         scenario.mulligan(() -> {
             scenario.assertSizes(5, 35);
             assertEquals(hand1, new HashSet<>(scenario.getLibraryRangeSize(19, 7)));
             assertEquals(hand2, new HashSet<>(scenario.getLibraryRangeSize(27, 6)));
-            assertEquals(discarded, scenario.getNBottomOfLibrary(2));
+            assertEquals(discarded, scenario.getNBottomOfLibrary(1));
             hand3.addAll(scenario.getHand());
             return false;
         });
@@ -115,7 +127,7 @@ public class LondonMulliganTest extends MulliganTestBase {
             assertEquals(hand1, new HashSet<>(scenario.getLibraryRangeSize(19, 7)));
             assertEquals(hand2, new HashSet<>(scenario.getLibraryRangeSize(27, 6)));
             assertEquals(hand3, scenario.getHand());
-            assertEquals(discarded, scenario.getNBottomOfLibrary(2));
+            assertEquals(discarded, scenario.getNBottomOfLibrary(1));
         });
     }
 
@@ -221,7 +233,12 @@ public class LondonMulliganTest extends MulliganTestBase {
         });
         scenario.discardBottom(count -> {
             scenario.assertSizes(7, 33);
-            assertEquals(2, count);
+            assertEquals(1, count);
+            return scenario.getHand().stream().limit(count).collect(Collectors.toList());
+        });
+        scenario.discardBottom(count -> {
+            scenario.assertSizes(6, 34);
+            assertEquals(1, count);
             return scenario.getHand().stream().limit(count).collect(Collectors.toList());
         });
         scenario.mulligan(() -> {
@@ -230,7 +247,17 @@ public class LondonMulliganTest extends MulliganTestBase {
         });
         scenario.discardBottom(count -> {
             scenario.assertSizes(7, 33);
-            assertEquals(3, count);
+            assertEquals(1, count);
+            return scenario.getHand().stream().limit(count).collect(Collectors.toList());
+        });
+        scenario.discardBottom(count -> {
+            scenario.assertSizes(6, 34);
+            assertEquals(1, count);
+            return scenario.getHand().stream().limit(count).collect(Collectors.toList());
+        });
+        scenario.discardBottom(count -> {
+            scenario.assertSizes(5, 35);
+            assertEquals(1, count);
             return scenario.getHand().stream().limit(count).collect(Collectors.toList());
         });
         scenario.mulligan(() -> {
@@ -239,7 +266,22 @@ public class LondonMulliganTest extends MulliganTestBase {
         });
         scenario.discardBottom(count -> {
             scenario.assertSizes(7, 33);
-            assertEquals(4, count);
+            assertEquals(1, count);
+            return scenario.getHand().stream().limit(count).collect(Collectors.toList());
+        });
+        scenario.discardBottom(count -> {
+            scenario.assertSizes(6, 34);
+            assertEquals(1, count);
+            return scenario.getHand().stream().limit(count).collect(Collectors.toList());
+        });
+        scenario.discardBottom(count -> {
+            scenario.assertSizes(5, 35);
+            assertEquals(1, count);
+            return scenario.getHand().stream().limit(count).collect(Collectors.toList());
+        });
+        scenario.discardBottom(count -> {
+            scenario.assertSizes(4, 36);
+            assertEquals(1, count);
             return scenario.getHand().stream().limit(count).collect(Collectors.toList());
         });
         scenario.mulligan(() -> {
@@ -248,7 +290,27 @@ public class LondonMulliganTest extends MulliganTestBase {
         });
         scenario.discardBottom(count -> {
             scenario.assertSizes(7, 33);
-            assertEquals(5, count);
+            assertEquals(1, count);
+            return scenario.getHand().stream().limit(count).collect(Collectors.toList());
+        });
+        scenario.discardBottom(count -> {
+            scenario.assertSizes(6, 34);
+            assertEquals(1, count);
+            return scenario.getHand().stream().limit(count).collect(Collectors.toList());
+        });
+        scenario.discardBottom(count -> {
+            scenario.assertSizes(5, 35);
+            assertEquals(1, count);
+            return scenario.getHand().stream().limit(count).collect(Collectors.toList());
+        });
+        scenario.discardBottom(count -> {
+            scenario.assertSizes(4, 36);
+            assertEquals(1, count);
+            return scenario.getHand().stream().limit(count).collect(Collectors.toList());
+        });
+        scenario.discardBottom(count -> {
+            scenario.assertSizes(3, 37);
+            assertEquals(1, count);
             return scenario.getHand().stream().limit(count).collect(Collectors.toList());
         });
         scenario.mulligan(() -> {
@@ -257,7 +319,32 @@ public class LondonMulliganTest extends MulliganTestBase {
         });
         scenario.discardBottom(count -> {
             scenario.assertSizes(7, 33);
-            assertEquals(6, count);
+            assertEquals(1, count);
+            return scenario.getHand().stream().limit(count).collect(Collectors.toList());
+        });
+        scenario.discardBottom(count -> {
+            scenario.assertSizes(6, 34);
+            assertEquals(1, count);
+            return scenario.getHand().stream().limit(count).collect(Collectors.toList());
+        });
+        scenario.discardBottom(count -> {
+            scenario.assertSizes(5, 35);
+            assertEquals(1, count);
+            return scenario.getHand().stream().limit(count).collect(Collectors.toList());
+        });
+        scenario.discardBottom(count -> {
+            scenario.assertSizes(4, 36);
+            assertEquals(1, count);
+            return scenario.getHand().stream().limit(count).collect(Collectors.toList());
+        });
+        scenario.discardBottom(count -> {
+            scenario.assertSizes(3, 37);
+            assertEquals(1, count);
+            return scenario.getHand().stream().limit(count).collect(Collectors.toList());
+        });
+        scenario.discardBottom(count -> {
+            scenario.assertSizes(2, 38);
+            assertEquals(1, count);
             return scenario.getHand().stream().limit(count).collect(Collectors.toList());
         });
         scenario.mulligan(() -> {
@@ -266,7 +353,37 @@ public class LondonMulliganTest extends MulliganTestBase {
         });
         scenario.discardBottom(count -> {
             scenario.assertSizes(7, 33);
-            assertEquals(7, count);
+            assertEquals(1, count);
+            return scenario.getHand().stream().limit(count).collect(Collectors.toList());
+        });
+        scenario.discardBottom(count -> {
+            scenario.assertSizes(6, 34);
+            assertEquals(1, count);
+            return scenario.getHand().stream().limit(count).collect(Collectors.toList());
+        });
+        scenario.discardBottom(count -> {
+            scenario.assertSizes(5, 35);
+            assertEquals(1, count);
+            return scenario.getHand().stream().limit(count).collect(Collectors.toList());
+        });
+        scenario.discardBottom(count -> {
+            scenario.assertSizes(4, 36);
+            assertEquals(1, count);
+            return scenario.getHand().stream().limit(count).collect(Collectors.toList());
+        });
+        scenario.discardBottom(count -> {
+            scenario.assertSizes(3, 37);
+            assertEquals(1, count);
+            return scenario.getHand().stream().limit(count).collect(Collectors.toList());
+        });
+        scenario.discardBottom(count -> {
+            scenario.assertSizes(2, 38);
+            assertEquals(1, count);
+            return scenario.getHand().stream().limit(count).collect(Collectors.toList());
+        });
+        scenario.discardBottom(count -> {
+            scenario.assertSizes(1, 39);
+            assertEquals(1, count);
             return scenario.getHand().stream().limit(count).collect(Collectors.toList());
         });
         scenario.run(() -> {
diff --git a/Mage/src/main/java/mage/players/StubPlayer.java b/Mage/src/main/java/mage/players/StubPlayer.java
index 6891338002..6b1fe798d3 100644
--- a/Mage/src/main/java/mage/players/StubPlayer.java
+++ b/Mage/src/main/java/mage/players/StubPlayer.java
@@ -13,6 +13,7 @@ import mage.cards.decks.Deck;
 import mage.choices.Choice;
 import mage.constants.Outcome;
 import mage.constants.RangeOfInfluence;
+import mage.filter.FilterMana;
 import mage.game.Game;
 import mage.game.combat.CombatGroup;
 import mage.game.draft.Draft;
@@ -25,13 +26,12 @@ import mage.target.TargetCard;
 import mage.target.TargetPlayer;
 
 import java.io.Serializable;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
 import static com.google.common.collect.Iterables.getOnlyElement;
-import static java.util.stream.Collectors.toList;
-import mage.filter.FilterMana;
 
 public class StubPlayer extends PlayerImpl implements Player {
 
@@ -54,15 +54,10 @@ public class StubPlayer extends PlayerImpl implements Player {
 
     @Override
     public boolean chooseTarget(Outcome outcome, Cards cards, TargetCard target, Ability source, Game game) {
-        if (target.getFilter().getMessage() != null && target.getFilter().getMessage().endsWith("(Discard for Mulligan)")) {
-            chooseDiscardBottom(game, target.getMinNumberOfTargets(), cards.getCards(game)
-                    .stream().map(MageItem::getId).collect(toList())).forEach(cardId -> target.add(cardId, game));
-        } else {
-            UUID cardId = getOnlyElement(cards.getCards(game)).getId();
-            if (chooseScry(game, cardId)) {
-                target.add(cardId, game);
-                return true;
-            }
+        UUID cardId = getOnlyElement(cards.getCards(game)).getId();
+        if (chooseScry(game, cardId)) {
+            target.add(cardId, game);
+            return true;
         }
         return false;
     }
@@ -111,6 +106,10 @@ public class StubPlayer extends PlayerImpl implements Player {
 
     @Override
     public boolean chooseTarget(Outcome outcome, Target target, Ability source, Game game) {
+        if (target.getFilter().getMessage() != null && target.getFilter().getMessage().endsWith(" more) to put on the bottom of your library")) {
+            chooseDiscardBottom(game, target.getMinNumberOfTargets(), new ArrayList<>(target.possibleTargets(null, null, game)))
+                    .forEach(cardId -> target.add(cardId, game));
+        }
         return false;
     }
 

From 771d54aa39dd90d5a3642f31c348c5372dd93da9 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Thu, 7 May 2020 15:21:15 +0400
Subject: [PATCH 020/586] * Triggered abilities with modes - fixed that player
 can skip mode selection (example: skip lose game with Demonic Pact, see
 #6524);

---
 .../Mage.Player.Human/src/mage/player/human/HumanPlayer.java   | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java
index bc08e0aa8d..10632fbf1d 100644
--- a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java
+++ b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java
@@ -2141,13 +2141,14 @@ public class HumanPlayer extends PlayerImpl {
                         // cancel choice (remove all selections)
                         if (Modes.CHOOSE_OPTION_CANCEL_ID.equals(response.getUUID())) {
                             modes.getSelectedModes().clear();
-                            return null;
                         }
                     } else if (canEndChoice) {
                         // end choice by done button in feedback panel
                         // disable after done option implemented
                         // done = true;
                     }
+
+                    // triggered abilities can't be skipped by cancel or wrong answer
                     if (source.getAbilityType() != AbilityType.TRIGGERED) {
                         done = true;
                     }

From 48049dbfc956f7d984709018eab5e140900081f0 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Thu, 7 May 2020 20:50:15 +0400
Subject: [PATCH 021/586] Tests: improved verify test for token's constructor;

---
 Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java
index f6598e0993..23adfada9f 100644
--- a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java
+++ b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java
@@ -680,9 +680,8 @@ public class VerifyCardDataTest {
         for (Class<? extends TokenImpl> tokenClass : publicTokens) {
             String className = extractShortClass(tokenClass);
             Token token = (Token) createNewObject(tokenClass);
-            //Assert.assertNotNull("Can't create token by default constructor", token);
             if (token == null) {
-                Assert.fail("Can't create token by default constructor: " + className);
+                errorsList.add("error, token must have default constructor with zero params: " + tokenClass.getName());
             } else if (tokDataNamesIndex.getOrDefault(token.getName(), "").isEmpty()) {
                 errorsList.add("error, can't find data in card-pictures-tok.txt for token: " + tokenClass.getName() + " -> " + token.getName());
             }
@@ -963,7 +962,6 @@ public class VerifyCardDataTest {
     }
 
     @Test
-    @Ignore
     public void showCardInfo() throws Exception {
         // debug only: show direct card info (takes it from class file, not from db repository)
         String cardName = "Essence Capture";

From da1c9cc38e55acd5426000dc4fa3004cd662e444 Mon Sep 17 00:00:00 2001
From: gp66 <garyprince66@hotmail.com>
Date: Thu, 7 May 2020 12:34:47 -0500
Subject: [PATCH 022/586] missing token images in thb and war

---
 Mage.Client/src/main/resources/card-pictures-tok.txt | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/Mage.Client/src/main/resources/card-pictures-tok.txt b/Mage.Client/src/main/resources/card-pictures-tok.txt
index dd753ba0cf..6c46c6c064 100644
--- a/Mage.Client/src/main/resources/card-pictures-tok.txt
+++ b/Mage.Client/src/main/resources/card-pictures-tok.txt
@@ -1269,6 +1269,7 @@
 |Generate|TOK:GRN|Soldier|||SoldierLifelinkToken|
 |Generate|TOK:WAR|Angel|||AngelVigilanceToken|
 |Generate|TOK:WAR|Assassin|||AssassinToken2|
+|Generate|TOK:WAR|Citizen|||PlanewideCelebrationToken|
 |Generate|TOK:WAR|Devil|||DevilToken|
 |Generate|TOK:WAR|Dragon|||DragonToken|
 |Generate|TOK:WAR|Goblin|||GoblinToken|
@@ -1357,8 +1358,8 @@
 |Generate|TOK:THB|Spider|||SpiderToken|
 |Generate|TOK:THB|Wolf|||WolfToken|
 |Generate|TOK:THB|Nightmare|||AshiokNightmareMuseToken|
-|Generate|TOK:THB|GoldToken|||Gold|
-|Generate|TOK:THB|ArtifactWallToken|||Wall|
+|Generate|TOK:THB|Gold|||GoldToken|
+|Generate|TOK:THB|Wall|||ArtifactWallToken|
 |Generate|TOK:IKO|Beast|||BeastToken|
 |Generate|TOK:IKO|Cat Bird|||CatBirdToken|
 |Generate|TOK:IKO|Cat|||CatToken|

From 87b40007a860724ebc1e8949225bc2baee2d0946 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 7 May 2020 18:19:46 -0400
Subject: [PATCH 023/586] fixed Rielle, the Everwise not triggering properly
 (fixes #6530)

---
 Mage.Sets/src/mage/cards/r/RielleTheEverwise.java | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/r/RielleTheEverwise.java b/Mage.Sets/src/mage/cards/r/RielleTheEverwise.java
index 17b4eb219e..58c90e1f7c 100644
--- a/Mage.Sets/src/mage/cards/r/RielleTheEverwise.java
+++ b/Mage.Sets/src/mage/cards/r/RielleTheEverwise.java
@@ -99,7 +99,7 @@ class RielleTheEverwiseTriggeredAbility extends TriggeredAbilityImpl {
 
 class RielleTheEverwiseWatcher extends Watcher {
 
-    private final Map<UUID, Integer> discardedThisTurn = new HashMap();
+    private final Map<UUID, Integer> discardedThisTurn = new HashMap<>();
 
     RielleTheEverwiseWatcher() {
         super(WatcherScope.GAME);
@@ -107,8 +107,9 @@ class RielleTheEverwiseWatcher extends Watcher {
 
     @Override
     public void watch(GameEvent event, Game game) {
-        if (event.getAmount() > 0) {
-            discardedThisTurn.compute(event.getPlayerId(), (u, i) -> i == null ? 1 : i + 1);
+        if (event.getType() == GameEvent.EventType.DISCARDED_CARDS
+                && event.getAmount() > 0) {
+            discardedThisTurn.merge(event.getPlayerId(), 1, Integer::sum);
         }
     }
 

From f64549213f4811591cb50caeb3d3504740127a10 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 7 May 2020 22:23:37 -0400
Subject: [PATCH 024/586] fixed a filter

---
 Mage.Sets/src/mage/cards/s/SengirAutocrat.java | 17 +++++++----------
 1 file changed, 7 insertions(+), 10 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/s/SengirAutocrat.java b/Mage.Sets/src/mage/cards/s/SengirAutocrat.java
index 7420097d0a..89b3deabc1 100644
--- a/Mage.Sets/src/mage/cards/s/SengirAutocrat.java
+++ b/Mage.Sets/src/mage/cards/s/SengirAutocrat.java
@@ -1,9 +1,6 @@
-
 package mage.cards.s;
 
-import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.common.LeavesBattlefieldTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
@@ -12,17 +9,18 @@ import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.SubType;
-import mage.filter.common.FilterCreaturePermanent;
+import mage.filter.FilterPermanent;
 import mage.filter.predicate.permanent.TokenPredicate;
 import mage.game.permanent.token.SerfToken;
 
+import java.util.UUID;
+
 /**
- *
  * @author Quercitron
  */
 public final class SengirAutocrat extends CardImpl {
 
-    private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Serf tokens");
+    private static final FilterPermanent filter = new FilterPermanent("Serf tokens");
 
     static {
         filter.add(SubType.SERF.getPredicate());
@@ -37,11 +35,10 @@ public final class SengirAutocrat extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Sengir Autocrat enters the battlefield, create three 0/1 black Serf creature tokens.
-        Ability ability = new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new SerfToken(), 3));
-        this.addAbility(ability);
+        this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new SerfToken(), 3)));
+
         // When Sengir Autocrat leaves the battlefield, exile all Serf tokens.
-        ability = new LeavesBattlefieldTriggeredAbility(new ExileAllEffect(filter), false);
-        this.addAbility(ability);
+        this.addAbility(new LeavesBattlefieldTriggeredAbility(new ExileAllEffect(filter), false));
     }
 
     public SengirAutocrat(final SengirAutocrat card) {

From f36792be935ed95dd4d46ccce2d007f7776400de Mon Sep 17 00:00:00 2001
From: SpeedProg <speedprogde@googlemail.com>
Date: Fri, 8 May 2020 21:29:42 +0200
Subject: [PATCH 025/586] make jboss remoting2 work with openjdk11

---
 .../main/java/mage/interfaces/MageServer.java | 11 +++----
 .../src/main/java/mage/remote/Connection.java |  5 +--
 .../java/mage/remote/CustomThreadPool.java    | 32 +++++++++++++++++++
 .../main/java/mage/remote/SessionImpl.java    | 10 +++---
 .../main/java/mage/server/MageServerImpl.java | 32 +++++++++----------
 5 files changed, 61 insertions(+), 29 deletions(-)
 create mode 100644 Mage.Common/src/main/java/mage/remote/CustomThreadPool.java

diff --git a/Mage.Common/src/main/java/mage/interfaces/MageServer.java b/Mage.Common/src/main/java/mage/interfaces/MageServer.java
index 52c95550c5..059f21ce63 100644
--- a/Mage.Common/src/main/java/mage/interfaces/MageServer.java
+++ b/Mage.Common/src/main/java/mage/interfaces/MageServer.java
@@ -16,7 +16,6 @@ import mage.utils.MageVersion;
 import mage.view.*;
 
 import java.util.List;
-import java.util.Optional;
 import java.util.Set;
 import java.util.UUID;
 
@@ -84,7 +83,7 @@ public interface MageServer {
 
     boolean isTableOwner(String sessionId, UUID roomId, UUID tableId) throws MageException;
 
-    Optional<TableView> getTable(UUID roomId, UUID tableId) throws MageException;
+    TableView getTable(UUID roomId, UUID tableId) throws MageException;
 
     List<TableView> getTables(UUID roomId) throws MageException;
 
@@ -95,13 +94,13 @@ public interface MageServer {
 
     void leaveChat(UUID chatId, String sessionId) throws MageException;
 
-    Optional<UUID> getTableChatId(UUID tableId) throws MageException;
+    UUID getTableChatId(UUID tableId) throws MageException;
 
-    Optional<UUID> getGameChatId(UUID gameId) throws MageException;
+    UUID getGameChatId(UUID gameId) throws MageException;
 
-    Optional<UUID> getRoomChatId(UUID roomId) throws MageException;
+    UUID getRoomChatId(UUID roomId) throws MageException;
 
-    Optional<UUID> getTournamentChatId(UUID tournamentId) throws MageException;
+    UUID getTournamentChatId(UUID tournamentId) throws MageException;
 
     //room methods
     UUID getMainRoomId() throws MageException;
diff --git a/Mage.Common/src/main/java/mage/remote/Connection.java b/Mage.Common/src/main/java/mage/remote/Connection.java
index ecc7dd4416..f3bf4296ee 100644
--- a/Mage.Common/src/main/java/mage/remote/Connection.java
+++ b/Mage.Common/src/main/java/mage/remote/Connection.java
@@ -42,6 +42,7 @@ public class Connection {
 //    private UserSkipPrioritySteps userSkipPrioritySteps;
     private static final String serialization = "?serializationtype=jboss";
     private static final String transport = "bisocket";
+    private static final String threadpool = "onewayThreadPool=mage.remote.CustomThreadPool";
 
     private final String parameter;
 
@@ -78,13 +79,13 @@ public class Connection {
             try {
                 InetAddress inet = getLocalAddress();
                 if (inet != null) {
-                    return transport + "://" + inet.getHostAddress() + ':' + port + '/' + serialization + parameter;
+                    return transport + "://" + inet.getHostAddress() + ':' + port + '/' + serialization + "&" + threadpool + parameter;
                 }
             } catch (SocketException ex) {
                 // just use localhost if can't find local ip
             }
         }
-        return transport + "://" + host + ':' + port + '/' + serialization + parameter;
+        return transport + "://" + host + ':' + port + '/' + serialization + "&" + threadpool + parameter;
     }
 
     public ProxyType getProxyType() {
diff --git a/Mage.Common/src/main/java/mage/remote/CustomThreadPool.java b/Mage.Common/src/main/java/mage/remote/CustomThreadPool.java
new file mode 100644
index 0000000000..456f47bcd7
--- /dev/null
+++ b/Mage.Common/src/main/java/mage/remote/CustomThreadPool.java
@@ -0,0 +1,32 @@
+package mage.remote;
+
+import java.lang.reflect.Field;
+import java.util.concurrent.ThreadPoolExecutor;
+
+import org.apache.log4j.Logger;
+import org.jboss.util.threadpool.BasicThreadPool;
+
+public class CustomThreadPool extends BasicThreadPool {
+    private static final Logger logger = Logger.getLogger(SessionImpl.class);
+
+    @Override
+    public void setMaximumPoolSize(int size) {
+        /*
+         * I really don't want to implement a whole new threadpool
+         * just to fix this and the executor is private
+         */
+        try {
+            Field executorField = BasicThreadPool.class.getField("executor");
+            executorField.setAccessible(true);
+            ThreadPoolExecutor executor = (ThreadPoolExecutor) executorField.get(this);
+            synchronized (executor) {
+                executor.setMaximumPoolSize(size);
+                executor.setCorePoolSize(size);
+            }
+        } catch (NoSuchFieldException | SecurityException e) {
+            logger.error("Failed to get field executor from BasicThreadPool", e);
+        } catch (IllegalArgumentException | IllegalAccessException e) {
+            logger.error("Failed to get executor object from BasicThreadPool", e);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Mage.Common/src/main/java/mage/remote/SessionImpl.java b/Mage.Common/src/main/java/mage/remote/SessionImpl.java
index dd073af9a9..861ad752ec 100644
--- a/Mage.Common/src/main/java/mage/remote/SessionImpl.java
+++ b/Mage.Common/src/main/java/mage/remote/SessionImpl.java
@@ -647,7 +647,7 @@ public class SessionImpl implements Session {
     public Optional<UUID> getRoomChatId(UUID roomId) {
         try {
             if (isConnected()) {
-                return server.getRoomChatId(roomId);
+                return Optional.of(server.getRoomChatId(roomId));
             }
         } catch (MageException ex) {
             handleMageException(ex);
@@ -659,7 +659,7 @@ public class SessionImpl implements Session {
     public Optional<UUID> getTableChatId(UUID tableId) {
         try {
             if (isConnected()) {
-                return server.getTableChatId(tableId);
+                return Optional.of(server.getTableChatId(tableId));
             }
         } catch (MageException ex) {
             handleMageException(ex);
@@ -671,7 +671,7 @@ public class SessionImpl implements Session {
     public Optional<UUID> getGameChatId(UUID gameId) {
         try {
             if (isConnected()) {
-                return server.getGameChatId(gameId);
+                return Optional.of(server.getGameChatId(gameId));
             }
         } catch (MageException ex) {
             handleMageException(ex);
@@ -685,7 +685,7 @@ public class SessionImpl implements Session {
     public Optional<TableView> getTable(UUID roomId, UUID tableId) {
         try {
             if (isConnected()) {
-                return server.getTable(roomId, tableId);
+                return Optional.of(server.getTable(roomId, tableId));
             }
         } catch (MageException ex) {
             handleMageException(ex);
@@ -829,7 +829,7 @@ public class SessionImpl implements Session {
     public Optional<UUID> getTournamentChatId(UUID tournamentId) {
         try {
             if (isConnected()) {
-                return server.getTournamentChatId(tournamentId);
+                return Optional.of(server.getTournamentChatId(tournamentId));
             }
         } catch (MageException ex) {
             handleMageException(ex);
diff --git a/Mage.Server/src/main/java/mage/server/MageServerImpl.java b/Mage.Server/src/main/java/mage/server/MageServerImpl.java
index 386c8c2a66..9387231256 100644
--- a/Mage.Server/src/main/java/mage/server/MageServerImpl.java
+++ b/Mage.Server/src/main/java/mage/server/MageServerImpl.java
@@ -397,15 +397,15 @@ public class MageServerImpl implements MageServer {
 
     @Override
     //FIXME: why no sessionId here???
-    public Optional<TableView> getTable(UUID roomId, UUID tableId) throws MageException {
+    public TableView getTable(UUID roomId, UUID tableId) throws MageException {
         try {
             Optional<GamesRoom> room = GamesRoomManager.instance.getRoom(roomId);
-            return room.flatMap(r -> r.getTable(tableId));
+            return room.flatMap(r -> r.getTable(tableId)).orElse(null);
 
         } catch (Exception ex) {
             handleException(ex);
         }
-        return Optional.empty();
+        return null;
     }
 
     @Override
@@ -536,18 +536,18 @@ public class MageServerImpl implements MageServer {
 
     @Override
     //FIXME: why no sessionId here???
-    public Optional<UUID> getRoomChatId(UUID roomId) throws MageException {
+    public UUID getRoomChatId(UUID roomId) throws MageException {
         try {
             Optional<GamesRoom> room = GamesRoomManager.instance.getRoom(roomId);
             if (!room.isPresent()) {
                 logger.error("roomId not found : " + roomId);
-                return Optional.empty();
+                return null;
             }
-            return Optional.of(room.get().getChatId());
+            return room.get().getChatId();
         } catch (Exception ex) {
             handleException(ex);
         }
-        return Optional.empty();
+        return null;
     }
 
     @Override
@@ -602,13 +602,13 @@ public class MageServerImpl implements MageServer {
 
     @Override
     //FIXME: why no sessionId here???
-    public Optional<UUID> getTableChatId(UUID tableId) throws MageException {
+    public UUID getTableChatId(UUID tableId) throws MageException {
         try {
-            return TableManager.instance.getChatId(tableId);
+            return TableManager.instance.getChatId(tableId).orElse(null);
         } catch (Exception ex) {
             handleException(ex);
         }
-        return Optional.empty();
+        return null;
     }
 
     @Override
@@ -646,24 +646,24 @@ public class MageServerImpl implements MageServer {
 
     @Override
     //FIXME: why no sessionId here???
-    public Optional<UUID> getGameChatId(UUID gameId) throws MageException {
+    public UUID getGameChatId(UUID gameId) throws MageException {
         try {
-            return GameManager.instance.getChatId(gameId);
+            return GameManager.instance.getChatId(gameId).orElse(null);
         } catch (Exception ex) {
             handleException(ex);
         }
-        return Optional.empty();
+        return null;
     }
 
     @Override
     //FIXME: why no sessionId here???
-    public Optional<UUID> getTournamentChatId(UUID tournamentId) throws MageException {
+    public UUID getTournamentChatId(UUID tournamentId) throws MageException {
         try {
-            return TournamentManager.instance.getChatId(tournamentId);
+            return TournamentManager.instance.getChatId(tournamentId).orElse(null);
         } catch (Exception ex) {
             handleException(ex);
         }
-        return Optional.empty();
+        return null;
     }
 
     @Override

From 8f04b9fb14792e0b589d087d2ad804dcf89b6c39 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Fri, 8 May 2020 18:07:36 -0400
Subject: [PATCH 026/586] fixed Cavalier of Flame discard triggers

---
 Mage.Sets/src/mage/cards/c/CavalierOfFlame.java | 10 +++-------
 1 file changed, 3 insertions(+), 7 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/c/CavalierOfFlame.java b/Mage.Sets/src/mage/cards/c/CavalierOfFlame.java
index 01e5b3e070..4fbf766bc0 100644
--- a/Mage.Sets/src/mage/cards/c/CavalierOfFlame.java
+++ b/Mage.Sets/src/mage/cards/c/CavalierOfFlame.java
@@ -16,6 +16,7 @@ import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
 import mage.abilities.keyword.HasteAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
+import mage.cards.CardsImpl;
 import mage.constants.*;
 import mage.filter.FilterPermanent;
 import mage.filter.StaticFilters;
@@ -103,14 +104,9 @@ class CavalierOfFlameEffect extends OneShotEffect {
         }
         TargetCardInHand target = new TargetCardInHand(0, player.getHand().size(), StaticFilters.FILTER_CARD);
         if (player.choose(Outcome.Discard, player.getHand(), target, game)) {
-            int counter = target
-                    .getTargets()
-                    .stream()
-                    .map(uuid -> game.getCard(uuid))
-                    .mapToInt(card -> card != null && player.discard(card, source, game) ? 1 : 0)
-                    .sum();
+            int counter = player.discard(new CardsImpl(target.getTargets()), source, game).size();
             player.drawCards(counter, source.getSourceId(), game);
         }
         return true;
     }
-}
\ No newline at end of file
+}

From 8f7e91183f987ff95f708e999930ea38641d91c0 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Fri, 8 May 2020 18:10:32 -0400
Subject: [PATCH 027/586] fixed Abandon Hope targeting any player

---
 Mage.Sets/src/mage/cards/a/AbandonHope.java | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/a/AbandonHope.java b/Mage.Sets/src/mage/cards/a/AbandonHope.java
index 635917a898..fab2833581 100644
--- a/Mage.Sets/src/mage/cards/a/AbandonHope.java
+++ b/Mage.Sets/src/mage/cards/a/AbandonHope.java
@@ -1,4 +1,3 @@
-
 package mage.cards.a;
 
 import mage.abilities.Ability;
@@ -15,8 +14,8 @@ import mage.constants.TargetController;
 import mage.constants.Zone;
 import mage.filter.StaticFilters;
 import mage.game.Game;
-import mage.target.TargetPlayer;
 import mage.target.common.TargetCardInHand;
+import mage.target.common.TargetOpponent;
 
 import java.util.UUID;
 
@@ -29,18 +28,20 @@ public final class AbandonHope extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{1}{B}");
 
         // As an additional cost to cast Abandon Hope, discard X cards.
-        Ability ability = new SimpleStaticAbility(Zone.ALL, new InfoEffect("As an additional cost to cast this spell, discard X cards"));
+        Ability ability = new SimpleStaticAbility(
+                Zone.ALL, new InfoEffect("As an additional cost to cast this spell, discard X cards")
+        );
         ability.setRuleAtTheTop(true);
         this.addAbility(ability);
 
         // Look at target opponent's hand and choose X cards from it. That player discards those cards.
         ManacostVariableValue manaX = ManacostVariableValue.instance;
         this.getSpellAbility().addEffect(new DiscardCardYouChooseTargetEffect(manaX, TargetController.ANY));
-        this.getSpellAbility().addTarget(new TargetPlayer());
+        this.getSpellAbility().addTarget(new TargetOpponent());
         this.getSpellAbility().setCostAdjuster(AbandonHopeAdjuster.instance);
     }
 
-    public AbandonHope(final AbandonHope card) {
+    private AbandonHope(final AbandonHope card) {
         super(card);
     }
 
@@ -60,4 +61,4 @@ enum AbandonHopeAdjuster implements CostAdjuster {
             ability.addCost(new DiscardTargetCost(new TargetCardInHand(xValue, xValue, StaticFilters.FILTER_CARD_CARDS)));
         }
     }
-}
\ No newline at end of file
+}

From 96449ad5b96a0f684209b98bd141f572d62674be Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Fri, 8 May 2020 18:11:39 -0400
Subject: [PATCH 028/586] fixed Ana Battlemage only targeting opponents

---
 Mage.Sets/src/mage/cards/a/AnaBattlemage.java | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/a/AnaBattlemage.java b/Mage.Sets/src/mage/cards/a/AnaBattlemage.java
index 38d6c7af25..bed405a197 100644
--- a/Mage.Sets/src/mage/cards/a/AnaBattlemage.java
+++ b/Mage.Sets/src/mage/cards/a/AnaBattlemage.java
@@ -20,8 +20,8 @@ import mage.filter.predicate.permanent.TappedPredicate;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
 import mage.players.Player;
+import mage.target.TargetPlayer;
 import mage.target.common.TargetCreaturePermanent;
-import mage.target.common.TargetOpponent;
 
 import java.util.UUID;
 
@@ -49,7 +49,7 @@ public final class AnaBattlemage extends CardImpl {
         this.addAbility(kickerAbility);
         // When Ana Battlemage enters the battlefield, if it was kicked with its {2}{U} kicker, target player discards three cards.
         TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new DiscardTargetEffect(3));
-        ability.addTarget(new TargetOpponent());
+        ability.addTarget(new TargetPlayer());
         this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new KickedCostCondition("{2}{U}"),
                 "When {this} enters the battlefield, if it was kicked with its {2}{U} kicker, target player discards three cards."));
         // When Ana Battlemage enters the battlefield, if it was kicked with its {1}{B} kicker, tap target untapped creature and that creature deals damage equal to its power to its controller.

From 4e79a64aa9c3e30d6da59904a5a003257cdfefc8 Mon Sep 17 00:00:00 2001
From: Thomas ARBLAY <thomas.arblay@smile.fr>
Date: Mon, 11 May 2020 12:03:57 +0200
Subject: [PATCH 029/586] Fix text for Breya, Etherium Shaper and Hadana's
 Climb

---
 Mage.Sets/src/mage/cards/b/BreyaEtheriumShaper.java  |  5 +++--
 Mage.Sets/src/mage/cards/w/WingedTempleOfOrazca.java | 11 ++++-------
 2 files changed, 7 insertions(+), 9 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/b/BreyaEtheriumShaper.java b/Mage.Sets/src/mage/cards/b/BreyaEtheriumShaper.java
index ba36808b69..23796bd56c 100644
--- a/Mage.Sets/src/mage/cards/b/BreyaEtheriumShaper.java
+++ b/Mage.Sets/src/mage/cards/b/BreyaEtheriumShaper.java
@@ -1,7 +1,6 @@
 
 package mage.cards.b;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.Mode;
@@ -22,6 +21,8 @@ import mage.target.common.TargetControlledPermanent;
 import mage.target.common.TargetCreaturePermanent;
 import mage.target.common.TargetPlayerOrPlaneswalker;
 
+import java.util.UUID;
+
 /**
  *
  * @author fireshoes
@@ -44,7 +45,7 @@ public final class BreyaEtheriumShaper extends CardImpl {
                 Zone.BATTLEFIELD,
                 new DamageTargetEffect(3),
                 new GenericManaCost(2));
-        ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(2, 2, new FilterControlledArtifactPermanent("two artifacts"), true)));
+        ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(2, 2, new FilterControlledArtifactPermanent("artifacts"), true)));
         ability.addTarget(new TargetPlayerOrPlaneswalker());
 
         // Target creature gets -4/-4 until end of turn.
diff --git a/Mage.Sets/src/mage/cards/w/WingedTempleOfOrazca.java b/Mage.Sets/src/mage/cards/w/WingedTempleOfOrazca.java
index 65d66dd4c1..8d08df6040 100644
--- a/Mage.Sets/src/mage/cards/w/WingedTempleOfOrazca.java
+++ b/Mage.Sets/src/mage/cards/w/WingedTempleOfOrazca.java
@@ -1,7 +1,6 @@
 
 package mage.cards.w;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.common.SimpleStaticAbility;
@@ -16,16 +15,14 @@ import mage.abilities.keyword.FlyingAbility;
 import mage.abilities.mana.AnyColorManaAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.CardType;
-import mage.constants.Duration;
-import mage.constants.Outcome;
-import mage.constants.SuperType;
-import mage.constants.Zone;
+import mage.constants.*;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
 import mage.target.common.TargetControlledCreaturePermanent;
 import mage.target.targetpointer.FixedTarget;
 
+import java.util.UUID;
+
 /**
  *
  * @author LevelX2
@@ -68,7 +65,7 @@ class WingedTempleOfOrazcaEffect extends OneShotEffect {
 
     public WingedTempleOfOrazcaEffect() {
         super(Outcome.Benefit);
-        this.staticText = "it gains flying and gets +X/+X until end of turn, where X is its power";
+        this.staticText = "target creature you control gains flying and gets +X/+X until end of turn, where X is its power";
     }
 
     public WingedTempleOfOrazcaEffect(final WingedTempleOfOrazcaEffect effect) {

From d0a2e26e02a02aef13b9d5b119c87fcf5e438539 Mon Sep 17 00:00:00 2001
From: Thomas ARBLAY <thomas.arblay@smile.fr>
Date: Mon, 11 May 2020 15:30:18 +0200
Subject: [PATCH 030/586] Fix the issue where Stormwild Capridor don't output a
 prevention text in chat

---
 .../src/mage/cards/s/StormwildCapridor.java   | 22 +++++++------------
 1 file changed, 8 insertions(+), 14 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/s/StormwildCapridor.java b/Mage.Sets/src/mage/cards/s/StormwildCapridor.java
index de000f194d..67d2bb9dea 100644
--- a/Mage.Sets/src/mage/cards/s/StormwildCapridor.java
+++ b/Mage.Sets/src/mage/cards/s/StormwildCapridor.java
@@ -3,6 +3,7 @@ package mage.cards.s;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.effects.PreventionEffectData;
 import mage.abilities.effects.PreventionEffectImpl;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
@@ -13,9 +14,7 @@ import mage.constants.SubType;
 import mage.counters.CounterType;
 import mage.game.Game;
 import mage.game.events.DamageCreatureEvent;
-import mage.game.events.DamageEvent;
 import mage.game.events.GameEvent;
-import mage.game.events.PreventDamageEvent;
 import mage.game.permanent.Permanent;
 
 import java.util.UUID;
@@ -74,19 +73,14 @@ class StormwildCapridorEffect extends PreventionEffectImpl {
 
     @Override
     public boolean replaceEvent(GameEvent event, Ability source, Game game) {
-        boolean retValue = false;
-        GameEvent preventEvent = new PreventDamageEvent(source.getFirstTarget(), source.getSourceId(), source.getControllerId(), event.getAmount(), ((DamageEvent) event).isCombatDamage());
-        int damage = event.getAmount();
-        if (!game.replaceEvent(preventEvent)) {
-            event.setAmount(0);
-            game.fireEvent(GameEvent.getEvent(GameEvent.EventType.PREVENTED_DAMAGE, source.getFirstTarget(), source.getSourceId(), source.getControllerId(), damage));
-            retValue = true;
+        PreventionEffectData preventionEffectData = preventDamageAction(event, source, game);
+        if (preventionEffectData.getPreventedDamage() > 0) {
+            Permanent permanent = game.getPermanent(source.getSourceId());
+            if (permanent != null) {
+                permanent.addCounters(CounterType.P1P1.createInstance(preventionEffectData.getPreventedDamage()), source, game);
+            }
         }
-        Permanent permanent = game.getPermanent(source.getSourceId());
-        if (permanent != null) {
-            permanent.addCounters(CounterType.P1P1.createInstance(damage), source, game);
-        }
-        return retValue;
+        return preventionEffectData.isReplaced();
     }
 
     @Override

From 235c5aa86f2de3b01ccdeea821839e45278b7a54 Mon Sep 17 00:00:00 2001
From: etpalmer63 <etpalmer@math.sc.edu>
Date: Mon, 11 May 2020 10:52:34 -0700
Subject: [PATCH 031/586] Added method to SupportAbility class to allow for
 removal of 'other' text in rule generation

---
 .../src/mage/cards/t/TogetherForever.java      |  2 +-
 .../mage/abilities/keyword/SupportAbility.java | 18 ++++++++++++++++++
 2 files changed, 19 insertions(+), 1 deletion(-)

diff --git a/Mage.Sets/src/mage/cards/t/TogetherForever.java b/Mage.Sets/src/mage/cards/t/TogetherForever.java
index ed2a9aca89..7ce28b8a1e 100644
--- a/Mage.Sets/src/mage/cards/t/TogetherForever.java
+++ b/Mage.Sets/src/mage/cards/t/TogetherForever.java
@@ -41,7 +41,7 @@ public final class TogetherForever extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}{W}");
 
         // When Together Forever enters the battlefield, support 2. (Put a +1/+1 counter on each of up to two other target creatures.)
-        this.addAbility(new SupportAbility(this, 2));
+        this.addAbility(new SupportAbility(this, 2, false));
 
         // {1}: Choose target creature with a counter on it. When that creature dies this turn, return that card to its owner's hand.
         Ability ability = new SimpleActivatedAbility(new TogetherForeverEffect(), new GenericManaCost(1));
diff --git a/Mage/src/main/java/mage/abilities/keyword/SupportAbility.java b/Mage/src/main/java/mage/abilities/keyword/SupportAbility.java
index 4b4d591f88..a525b6ed53 100644
--- a/Mage/src/main/java/mage/abilities/keyword/SupportAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/SupportAbility.java
@@ -31,6 +31,24 @@ public class SupportAbility extends EntersBattlefieldTriggeredAbility {
         }
     }
 
+    /*
+     * For enchanments, the text should not include the word "other". This method
+     * allows the otherPermanent choice to be selected in the call to SupportAbility
+     * and removes the "other" text from rule creation.
+     */
+    public SupportAbility(Card card, int amount, boolean otherPermanent) {
+        super(new SupportEffect(card, amount, otherPermanent));
+        if (!card.isInstant() && !card.isSorcery()) {
+            FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures");
+            if (card.isCreature()) {
+                filter.add(AnotherPredicate.instance);
+                filter.setMessage("other target creatures");
+            }
+            addTarget(new TargetCreaturePermanent(0, amount, filter, false));
+        }
+    }
+
+
     public SupportAbility(final SupportAbility ability) {
         super(ability);
     }

From 3865c52974ca40f2f5295140e4610d5ae26d7cf9 Mon Sep 17 00:00:00 2001
From: gp66 <garyprince66@hotmail.com>
Date: Wed, 13 May 2020 15:44:26 -0500
Subject: [PATCH 032/586] missing token images in thb and war

---
 .../mage/game/permanent/token/DinosaurBeastToken.java | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/Mage/src/main/java/mage/game/permanent/token/DinosaurBeastToken.java b/Mage/src/main/java/mage/game/permanent/token/DinosaurBeastToken.java
index ad0026141e..1dd4a66672 100644
--- a/Mage/src/main/java/mage/game/permanent/token/DinosaurBeastToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/DinosaurBeastToken.java
@@ -10,6 +10,17 @@ package mage.game.permanent.token;
  */
 public final class DinosaurBeastToken extends TokenImpl {
 
+    public DinosaurBeastToken() {
+        super("Dinosaur Beast", "X/X green Dinosaur Beast creature token with trample");
+        cardType.add(CardType.CREATURE);
+        color.setGreen(true);
+        subtype.add(SubType.DINOSAUR);
+        subtype.add(SubType.BEAST);
+        power = new MageInt(0);
+        toughness = new MageInt(0);
+        addAbility(TrampleAbility.getInstance());
+    }
+
     public DinosaurBeastToken(int xValue) {
         super("Dinosaur Beast", "X/X green Dinosaur Beast creature token with trample");
         cardType.add(CardType.CREATURE);

From 162907b9da234b2fd7f0ac764c16f8c14fc60497 Mon Sep 17 00:00:00 2001
From: gp66 <garyprince66@hotmail.com>
Date: Wed, 13 May 2020 15:44:40 -0500
Subject: [PATCH 033/586] missing token images in thb and war

---
 .../permanent/token/PurphorossInterventionToken.java  | 11 +++++++++++
 .../java/mage/game/permanent/token/SharkToken.java    | 10 ++++++++++
 .../java/mage/game/permanent/token/TokenImpl.java     |  2 ++
 3 files changed, 23 insertions(+)

diff --git a/Mage/src/main/java/mage/game/permanent/token/PurphorossInterventionToken.java b/Mage/src/main/java/mage/game/permanent/token/PurphorossInterventionToken.java
index 2934b37f99..9c960e786b 100644
--- a/Mage/src/main/java/mage/game/permanent/token/PurphorossInterventionToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/PurphorossInterventionToken.java
@@ -11,6 +11,17 @@ import mage.constants.SubType;
  */
 public final class PurphorossInterventionToken extends TokenImpl {
 
+    public PurphorossInterventionToken() {
+        super("Elemental", "X/1 red Elemental creature token with trample and haste");
+        this.cardType.add(CardType.CREATURE);
+        this.subtype.add(SubType.ELEMENTAL);
+        this.color.setRed(true);
+        this.power = new MageInt(0);
+        this.toughness = new MageInt(1);
+        this.addAbility(TrampleAbility.getInstance());
+        this.addAbility(HasteAbility.getInstance());
+    }
+
     public PurphorossInterventionToken(int power) {
         super("Elemental", "X/1 red Elemental creature token with trample and haste");
         this.cardType.add(CardType.CREATURE);
diff --git a/Mage/src/main/java/mage/game/permanent/token/SharkToken.java b/Mage/src/main/java/mage/game/permanent/token/SharkToken.java
index d5c6f6e818..d71978f054 100644
--- a/Mage/src/main/java/mage/game/permanent/token/SharkToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/SharkToken.java
@@ -10,6 +10,16 @@ import mage.constants.SubType;
  */
 public final class SharkToken extends TokenImpl {
 
+    public SharkToken() {
+        super("Shark", "X/X blue Shark creature token with flying");
+        cardType.add(CardType.CREATURE);
+        color.setBlue(true);
+        subtype.add(SubType.SHARK);
+        power = new MageInt(0);
+        toughness = new MageInt(0);
+        addAbility(FlyingAbility.getInstance());
+    }
+
     public SharkToken(int xValue) {
         super("Shark", "X/X blue Shark creature token with flying");
         cardType.add(CardType.CREATURE);
diff --git a/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java b/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java
index 61d10a484f..922d5893db 100644
--- a/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java
+++ b/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java
@@ -53,6 +53,8 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
         }
     }
 
+    public TokenImpl() { }
+
     public TokenImpl(String name, String description) {
         this.name = name;
         this.description = description;

From 877abe087a260d0f5a8942ac819eee9cb62579ec Mon Sep 17 00:00:00 2001
From: gp66 <garyprince66@hotmail.com>
Date: Fri, 15 May 2020 12:57:28 -0500
Subject: [PATCH 034/586] removed copy/paste code

---
 .../mage/game/permanent/token/DinosaurBeastToken.java  | 10 +---------
 .../permanent/token/PurphorossInterventionToken.java   | 10 +---------
 .../java/mage/game/permanent/token/SharkToken.java     |  9 +--------
 3 files changed, 3 insertions(+), 26 deletions(-)

diff --git a/Mage/src/main/java/mage/game/permanent/token/DinosaurBeastToken.java b/Mage/src/main/java/mage/game/permanent/token/DinosaurBeastToken.java
index 1dd4a66672..17f93178ef 100644
--- a/Mage/src/main/java/mage/game/permanent/token/DinosaurBeastToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/DinosaurBeastToken.java
@@ -11,16 +11,8 @@ package mage.game.permanent.token;
 public final class DinosaurBeastToken extends TokenImpl {
 
     public DinosaurBeastToken() {
-        super("Dinosaur Beast", "X/X green Dinosaur Beast creature token with trample");
-        cardType.add(CardType.CREATURE);
-        color.setGreen(true);
-        subtype.add(SubType.DINOSAUR);
-        subtype.add(SubType.BEAST);
-        power = new MageInt(0);
-        toughness = new MageInt(0);
-        addAbility(TrampleAbility.getInstance());
+        this(0);
     }
-
     public DinosaurBeastToken(int xValue) {
         super("Dinosaur Beast", "X/X green Dinosaur Beast creature token with trample");
         cardType.add(CardType.CREATURE);
diff --git a/Mage/src/main/java/mage/game/permanent/token/PurphorossInterventionToken.java b/Mage/src/main/java/mage/game/permanent/token/PurphorossInterventionToken.java
index 9c960e786b..2517b79aa0 100644
--- a/Mage/src/main/java/mage/game/permanent/token/PurphorossInterventionToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/PurphorossInterventionToken.java
@@ -12,16 +12,8 @@ import mage.constants.SubType;
 public final class PurphorossInterventionToken extends TokenImpl {
 
     public PurphorossInterventionToken() {
-        super("Elemental", "X/1 red Elemental creature token with trample and haste");
-        this.cardType.add(CardType.CREATURE);
-        this.subtype.add(SubType.ELEMENTAL);
-        this.color.setRed(true);
-        this.power = new MageInt(0);
-        this.toughness = new MageInt(1);
-        this.addAbility(TrampleAbility.getInstance());
-        this.addAbility(HasteAbility.getInstance());
+        this(0);
     }
-
     public PurphorossInterventionToken(int power) {
         super("Elemental", "X/1 red Elemental creature token with trample and haste");
         this.cardType.add(CardType.CREATURE);
diff --git a/Mage/src/main/java/mage/game/permanent/token/SharkToken.java b/Mage/src/main/java/mage/game/permanent/token/SharkToken.java
index d71978f054..0cbe1538b7 100644
--- a/Mage/src/main/java/mage/game/permanent/token/SharkToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/SharkToken.java
@@ -11,15 +11,8 @@ import mage.constants.SubType;
 public final class SharkToken extends TokenImpl {
 
     public SharkToken() {
-        super("Shark", "X/X blue Shark creature token with flying");
-        cardType.add(CardType.CREATURE);
-        color.setBlue(true);
-        subtype.add(SubType.SHARK);
-        power = new MageInt(0);
-        toughness = new MageInt(0);
-        addAbility(FlyingAbility.getInstance());
+        this(0);
     }
-
     public SharkToken(int xValue) {
         super("Shark", "X/X blue Shark creature token with flying");
         cardType.add(CardType.CREATURE);

From 25db5f11eb6de0a9026a4a978abde035ac6dd182 Mon Sep 17 00:00:00 2001
From: etpalmer63 <etpalmer@math.sc.edu>
Date: Fri, 15 May 2020 11:29:51 -0700
Subject: [PATCH 035/586] Changed old constructor to call the new one to
 prevent code duplication

---
 .../abilities/keyword/SupportAbility.java     | 20 +++++--------------
 1 file changed, 5 insertions(+), 15 deletions(-)

diff --git a/Mage/src/main/java/mage/abilities/keyword/SupportAbility.java b/Mage/src/main/java/mage/abilities/keyword/SupportAbility.java
index a525b6ed53..63fec0c718 100644
--- a/Mage/src/main/java/mage/abilities/keyword/SupportAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/SupportAbility.java
@@ -19,22 +19,9 @@ import mage.target.common.TargetCreaturePermanent;
  */
 public class SupportAbility extends EntersBattlefieldTriggeredAbility {
 
-    public SupportAbility(Card card, int amount) {
-        super(new SupportEffect(card, amount, true));
-        if (!card.isInstant() && !card.isSorcery()) {
-            FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures");
-            if (card.isCreature()) {
-                filter.add(AnotherPredicate.instance);
-                filter.setMessage("other target creatures");
-            }
-            addTarget(new TargetCreaturePermanent(0, amount, filter, false));
-        }
-    }
-
     /*
-     * For enchanments, the text should not include the word "other". This method
-     * allows the otherPermanent choice to be selected in the call to SupportAbility
-     * and removes the "other" text from rule creation.
+     * For enchantments, the text should not include the word "other".
+     * The otherPermanent choice removes the word "other" from rule text creation.
      */
     public SupportAbility(Card card, int amount, boolean otherPermanent) {
         super(new SupportEffect(card, amount, otherPermanent));
@@ -49,6 +36,9 @@ public class SupportAbility extends EntersBattlefieldTriggeredAbility {
     }
 
 
+    public SupportAbility(Card card, int amount){ this( card, amount, true); }
+
+
     public SupportAbility(final SupportAbility ability) {
         super(ability);
     }

From 6a582ed669fb9796458089fdde57cd43ed901d34 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Fri, 15 May 2020 20:43:55 -0400
Subject: [PATCH 036/586] added IKO promos

---
 .../src/mage/sets/IkoriaLairOfBehemoths.java  | 284 ++++++++++++------
 Utils/mtg-cards-data.txt                      | 122 ++++++++
 2 files changed, 319 insertions(+), 87 deletions(-)

diff --git a/Mage.Sets/src/mage/sets/IkoriaLairOfBehemoths.java b/Mage.Sets/src/mage/sets/IkoriaLairOfBehemoths.java
index 56e8e82cdb..dcc3d8dcd9 100644
--- a/Mage.Sets/src/mage/sets/IkoriaLairOfBehemoths.java
+++ b/Mage.Sets/src/mage/sets/IkoriaLairOfBehemoths.java
@@ -80,8 +80,10 @@ public final class IkoriaLairOfBehemoths extends ExpansionSet {
         cards.add(new SetCardInfo("Alert Heedbonder", 218, Rarity.UNCOMMON, mage.cards.a.AlertHeedbonder.class));
         cards.add(new SetCardInfo("Almighty Brushwagg", 143, Rarity.COMMON, mage.cards.a.AlmightyBrushwagg.class));
         cards.add(new SetCardInfo("Anticipate", 40, Rarity.COMMON, mage.cards.a.Anticipate.class));
-        cards.add(new SetCardInfo("Archipelagore", 41, Rarity.UNCOMMON, mage.cards.a.Archipelagore.class));
-        cards.add(new SetCardInfo("Auspicious Starrix", 144, Rarity.UNCOMMON, mage.cards.a.AuspiciousStarrix.class));
+        cards.add(new SetCardInfo("Archipelagore", 283, Rarity.UNCOMMON, mage.cards.a.Archipelagore.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Archipelagore", 41, Rarity.UNCOMMON, mage.cards.a.Archipelagore.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Auspicious Starrix", 144, Rarity.UNCOMMON, mage.cards.a.AuspiciousStarrix.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Auspicious Starrix", 294, Rarity.UNCOMMON, mage.cards.a.AuspiciousStarrix.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Avian Oddity", 42, Rarity.UNCOMMON, mage.cards.a.AvianOddity.class));
         cards.add(new SetCardInfo("Back for More", 177, Rarity.UNCOMMON, mage.cards.b.BackForMore.class));
         cards.add(new SetCardInfo("Barrier Breach", 145, Rarity.UNCOMMON, mage.cards.b.BarrierBreach.class));
@@ -94,8 +96,10 @@ public final class IkoriaLairOfBehemoths extends ExpansionSet {
         cards.add(new SetCardInfo("Blood Curdle", 75, Rarity.COMMON, mage.cards.b.BloodCurdle.class));
         cards.add(new SetCardInfo("Bloodfell Caves", 243, Rarity.COMMON, mage.cards.b.BloodfellCaves.class));
         cards.add(new SetCardInfo("Blossoming Sands", 244, Rarity.COMMON, mage.cards.b.BlossomingSands.class));
-        cards.add(new SetCardInfo("Bonders' Enclave", 245, Rarity.RARE, mage.cards.b.BondersEnclave.class));
-        cards.add(new SetCardInfo("Boneyard Lurker", 178, Rarity.UNCOMMON, mage.cards.b.BoneyardLurker.class));
+        cards.add(new SetCardInfo("Bonders' Enclave", 245, Rarity.RARE, mage.cards.b.BondersEnclave.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Bonders' Enclave", 363, Rarity.RARE, mage.cards.b.BondersEnclave.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Boneyard Lurker", 178, Rarity.UNCOMMON, mage.cards.b.BoneyardLurker.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Boneyard Lurker", 298, Rarity.UNCOMMON, mage.cards.b.BoneyardLurker.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Boon of the Wish-Giver", 43, Rarity.UNCOMMON, mage.cards.b.BoonOfTheWishGiver.class));
         cards.add(new SetCardInfo("Boot Nipper", 76, Rarity.COMMON, mage.cards.b.BootNipper.class));
         cards.add(new SetCardInfo("Bristling Boar", 146, Rarity.COMMON, mage.cards.b.BristlingBoar.class));
@@ -103,126 +107,182 @@ public final class IkoriaLairOfBehemoths extends ExpansionSet {
         cards.add(new SetCardInfo("Call of the Death-Dweller", 78, Rarity.UNCOMMON, mage.cards.c.CallOfTheDeathDweller.class));
         cards.add(new SetCardInfo("Capture Sphere", 44, Rarity.COMMON, mage.cards.c.CaptureSphere.class));
         cards.add(new SetCardInfo("Cathartic Reunion", 110, Rarity.COMMON, mage.cards.c.CatharticReunion.class));
-        cards.add(new SetCardInfo("Cavern Whisperer", 79, Rarity.COMMON, mage.cards.c.CavernWhisperer.class));
+        cards.add(new SetCardInfo("Cavern Whisperer", 287, Rarity.COMMON, mage.cards.c.CavernWhisperer.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Cavern Whisperer", 79, Rarity.COMMON, mage.cards.c.CavernWhisperer.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Channeled Force", 180, Rarity.UNCOMMON, mage.cards.c.ChanneledForce.class));
         cards.add(new SetCardInfo("Charge of the Forever-Beast", 147, Rarity.UNCOMMON, mage.cards.c.ChargeOfTheForeverBeast.class));
         cards.add(new SetCardInfo("Checkpoint Officer", 5, Rarity.COMMON, mage.cards.c.CheckpointOfficer.class));
-        cards.add(new SetCardInfo("Chevill, Bane of Monsters", 181, Rarity.MYTHIC, mage.cards.c.ChevillBaneOfMonsters.class));
+        cards.add(new SetCardInfo("Chevill, Bane of Monsters", 181, Rarity.MYTHIC, mage.cards.c.ChevillBaneOfMonsters.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Chevill, Bane of Monsters", 330, Rarity.MYTHIC, mage.cards.c.ChevillBaneOfMonsters.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Chittering Harvester", 288, Rarity.UNCOMMON, mage.cards.c.ChitteringHarvester.class));
         cards.add(new SetCardInfo("Chittering Harvester", 80, Rarity.UNCOMMON, mage.cards.c.ChitteringHarvester.class));
         cards.add(new SetCardInfo("Clash of Titans", 111, Rarity.UNCOMMON, mage.cards.c.ClashOfTitans.class));
         cards.add(new SetCardInfo("Cloudpiercer", 112, Rarity.COMMON, mage.cards.c.Cloudpiercer.class));
-        cards.add(new SetCardInfo("Colossification", 148, Rarity.RARE, mage.cards.c.Colossification.class));
+        cards.add(new SetCardInfo("Cloudpiercer", 291, Rarity.COMMON, mage.cards.c.Cloudpiercer.class));
+        cards.add(new SetCardInfo("Colossification", 148, Rarity.RARE, mage.cards.c.Colossification.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Colossification", 327, Rarity.RARE, mage.cards.c.Colossification.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Colossification", 364, Rarity.RARE, mage.cards.c.Colossification.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Convolute", 45, Rarity.COMMON, mage.cards.c.Convolute.class));
         cards.add(new SetCardInfo("Coordinated Charge", 6, Rarity.COMMON, mage.cards.c.CoordinatedCharge.class));
         cards.add(new SetCardInfo("Corpse Churn", 81, Rarity.COMMON, mage.cards.c.CorpseChurn.class));
         cards.add(new SetCardInfo("Crystacean", 46, Rarity.COMMON, mage.cards.c.Crystacean.class));
-        cards.add(new SetCardInfo("Crystalline Giant", 234, Rarity.RARE, mage.cards.c.CrystallineGiant.class));
-        cards.add(new SetCardInfo("Cubwarden", 7, Rarity.RARE, mage.cards.c.Cubwarden.class));
+        cards.add(new SetCardInfo("Crystalline Giant", 234, Rarity.RARE, mage.cards.c.CrystallineGiant.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Crystalline Giant", 361, Rarity.RARE, mage.cards.c.CrystallineGiant.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Crystalline Giant", 387, Rarity.RARE, mage.cards.c.CrystallineGiant.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Cubwarden", 279, Rarity.RARE, mage.cards.c.Cubwarden.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Cubwarden", 7, Rarity.RARE, mage.cards.c.Cubwarden.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Cunning Nightbonder", 219, Rarity.UNCOMMON, mage.cards.c.CunningNightbonder.class));
         cards.add(new SetCardInfo("Dark Bargain", 82, Rarity.COMMON, mage.cards.d.DarkBargain.class));
         cards.add(new SetCardInfo("Daysquad Marshal", 8, Rarity.COMMON, mage.cards.d.DaysquadMarshal.class));
         cards.add(new SetCardInfo("Dead Weight", 83, Rarity.COMMON, mage.cards.d.DeadWeight.class));
-        cards.add(new SetCardInfo("Death's Oasis", 182, Rarity.RARE, mage.cards.d.DeathsOasis.class));
+        cards.add(new SetCardInfo("Death's Oasis", 182, Rarity.RARE, mage.cards.d.DeathsOasis.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Death's Oasis", 331, Rarity.RARE, mage.cards.d.DeathsOasis.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Dire Tactics", 183, Rarity.UNCOMMON, mage.cards.d.DireTactics.class));
-        cards.add(new SetCardInfo("Dirge Bat", 84, Rarity.RARE, mage.cards.d.DirgeBat.class));
+        cards.add(new SetCardInfo("Dirge Bat", 289, Rarity.RARE, mage.cards.d.DirgeBat.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Dirge Bat", 386, Rarity.RARE, mage.cards.d.DirgeBat.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Dirge Bat", 84, Rarity.RARE, mage.cards.d.DirgeBat.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Dismal Backwater", 246, Rarity.COMMON, mage.cards.d.DismalBackwater.class));
         cards.add(new SetCardInfo("Divine Arrow", 9, Rarity.COMMON, mage.cards.d.DivineArrow.class));
         cards.add(new SetCardInfo("Drannith Healer", 10, Rarity.COMMON, mage.cards.d.DrannithHealer.class));
-        cards.add(new SetCardInfo("Drannith Magistrate", 11, Rarity.RARE, mage.cards.d.DrannithMagistrate.class));
+        cards.add(new SetCardInfo("Drannith Magistrate", 11, Rarity.RARE, mage.cards.d.DrannithMagistrate.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Drannith Magistrate", 314, Rarity.RARE, mage.cards.d.DrannithMagistrate.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Drannith Stinger", 113, Rarity.COMMON, mage.cards.d.DrannithStinger.class));
-        cards.add(new SetCardInfo("Dreamtail Heron", 47, Rarity.COMMON, mage.cards.d.DreamtailHeron.class));
+        cards.add(new SetCardInfo("Dreamtail Heron", 284, Rarity.COMMON, mage.cards.d.DreamtailHeron.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Dreamtail Heron", 47, Rarity.COMMON, mage.cards.d.DreamtailHeron.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Durable Coilbug", 85, Rarity.COMMON, mage.cards.d.DurableCoilbug.class));
         cards.add(new SetCardInfo("Duskfang Mentor", 86, Rarity.UNCOMMON, mage.cards.d.DuskfangMentor.class));
         cards.add(new SetCardInfo("Easy Prey", 87, Rarity.UNCOMMON, mage.cards.e.EasyPrey.class));
-        cards.add(new SetCardInfo("Eerie Ultimatum", 184, Rarity.RARE, mage.cards.e.EerieUltimatum.class));
-        cards.add(new SetCardInfo("Emergent Ultimatum", 185, Rarity.RARE, mage.cards.e.EmergentUltimatum.class));
+        cards.add(new SetCardInfo("Eerie Ultimatum", 184, Rarity.RARE, mage.cards.e.EerieUltimatum.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Eerie Ultimatum", 332, Rarity.RARE, mage.cards.e.EerieUltimatum.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Emergent Ultimatum", 185, Rarity.RARE, mage.cards.e.EmergentUltimatum.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Emergent Ultimatum", 333, Rarity.RARE, mage.cards.e.EmergentUltimatum.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Escape Protocol", 48, Rarity.UNCOMMON, mage.cards.e.EscapeProtocol.class));
         cards.add(new SetCardInfo("Essence Scatter", 49, Rarity.COMMON, mage.cards.e.EssenceScatter.class));
-        cards.add(new SetCardInfo("Everquill Phoenix", 114, Rarity.RARE, mage.cards.e.EverquillPhoenix.class));
+        cards.add(new SetCardInfo("Everquill Phoenix", 114, Rarity.RARE, mage.cards.e.EverquillPhoenix.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Everquill Phoenix", 292, Rarity.RARE, mage.cards.e.EverquillPhoenix.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Everquill Phoenix", 374, Rarity.RARE, mage.cards.e.EverquillPhoenix.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Evolving Wilds", 247, Rarity.COMMON, mage.cards.e.EvolvingWilds.class));
         cards.add(new SetCardInfo("Excavation Mole", 150, Rarity.COMMON, mage.cards.e.ExcavationMole.class));
-        cards.add(new SetCardInfo("Extinction Event", 88, Rarity.RARE, mage.cards.e.ExtinctionEvent.class));
+        cards.add(new SetCardInfo("Extinction Event", 321, Rarity.RARE, mage.cards.e.ExtinctionEvent.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Extinction Event", 88, Rarity.RARE, mage.cards.e.ExtinctionEvent.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Exuberant Wolfbear", 151, Rarity.UNCOMMON, mage.cards.e.ExuberantWolfbear.class));
         cards.add(new SetCardInfo("Facet Reader", 50, Rarity.COMMON, mage.cards.f.FacetReader.class));
         cards.add(new SetCardInfo("Farfinder", 2, Rarity.COMMON, mage.cards.f.Farfinder.class));
         cards.add(new SetCardInfo("Ferocious Tigorilla", 115, Rarity.COMMON, mage.cards.f.FerociousTigorilla.class));
         cards.add(new SetCardInfo("Fertilid", 152, Rarity.COMMON, mage.cards.f.Fertilid.class));
-        cards.add(new SetCardInfo("Fiend Artisan", 220, Rarity.MYTHIC, mage.cards.f.FiendArtisan.class));
+        cards.add(new SetCardInfo("Fiend Artisan", 220, Rarity.MYTHIC, mage.cards.f.FiendArtisan.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Fiend Artisan", 350, Rarity.MYTHIC, mage.cards.f.FiendArtisan.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Fight as One", 12, Rarity.UNCOMMON, mage.cards.f.FightAsOne.class));
         cards.add(new SetCardInfo("Fire Prophecy", 116, Rarity.COMMON, mage.cards.f.FireProphecy.class));
         cards.add(new SetCardInfo("Flame Spill", 117, Rarity.UNCOMMON, mage.cards.f.FlameSpill.class));
-        cards.add(new SetCardInfo("Flourishing Fox", 13, Rarity.UNCOMMON, mage.cards.f.FlourishingFox.class));
+        cards.add(new SetCardInfo("Flourishing Fox", 13, Rarity.UNCOMMON, mage.cards.f.FlourishingFox.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Flourishing Fox", 365, Rarity.UNCOMMON, mage.cards.f.FlourishingFox.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Flycatcher Giraffid", 153, Rarity.COMMON, mage.cards.f.FlycatcherGiraffid.class));
         cards.add(new SetCardInfo("Footfall Crater", 118, Rarity.UNCOMMON, mage.cards.f.FootfallCrater.class));
-        cards.add(new SetCardInfo("Forbidden Friendship", 119, Rarity.COMMON, mage.cards.f.ForbiddenFriendship.class));
+        cards.add(new SetCardInfo("Forbidden Friendship", 119, Rarity.COMMON, mage.cards.f.ForbiddenFriendship.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Forbidden Friendship", 367, Rarity.COMMON, mage.cards.f.ForbiddenFriendship.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Forest", 272, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Forest", 273, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Forest", 274, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Frenzied Raptor", 120, Rarity.COMMON, mage.cards.f.FrenziedRaptor.class));
         cards.add(new SetCardInfo("Frillscare Mentor", 121, Rarity.UNCOMMON, mage.cards.f.FrillscareMentor.class));
-        cards.add(new SetCardInfo("Frondland Felidar", 186, Rarity.RARE, mage.cards.f.FrondlandFelidar.class));
+        cards.add(new SetCardInfo("Frondland Felidar", 186, Rarity.RARE, mage.cards.f.FrondlandFelidar.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Frondland Felidar", 334, Rarity.RARE, mage.cards.f.FrondlandFelidar.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Frost Lynx", 51, Rarity.COMMON, mage.cards.f.FrostLynx.class));
         cards.add(new SetCardInfo("Frostveil Ambush", 52, Rarity.COMMON, mage.cards.f.FrostveilAmbush.class));
         cards.add(new SetCardInfo("Fully Grown", 154, Rarity.COMMON, mage.cards.f.FullyGrown.class));
         cards.add(new SetCardInfo("Garrison Cat", 14, Rarity.COMMON, mage.cards.g.GarrisonCat.class));
-        cards.add(new SetCardInfo("Gemrazer", 155, Rarity.RARE, mage.cards.g.Gemrazer.class));
-        cards.add(new SetCardInfo("General Kudro of Drannith", 187, Rarity.MYTHIC, mage.cards.g.GeneralKudroOfDrannith.class));
+        cards.add(new SetCardInfo("Gemrazer", 155, Rarity.RARE, mage.cards.g.Gemrazer.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Gemrazer", 295, Rarity.RARE, mage.cards.g.Gemrazer.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Gemrazer", 376, Rarity.RARE, mage.cards.g.Gemrazer.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("General Kudro of Drannith", 187, Rarity.MYTHIC, mage.cards.g.GeneralKudroOfDrannith.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("General Kudro of Drannith", 335, Rarity.MYTHIC, mage.cards.g.GeneralKudroOfDrannith.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("General's Enforcer", 188, Rarity.UNCOMMON, mage.cards.g.GeneralsEnforcer.class));
-        cards.add(new SetCardInfo("Genesis Ultimatum", 189, Rarity.RARE, mage.cards.g.GenesisUltimatum.class));
+        cards.add(new SetCardInfo("Genesis Ultimatum", 189, Rarity.RARE, mage.cards.g.GenesisUltimatum.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Genesis Ultimatum", 336, Rarity.RARE, mage.cards.g.GenesisUltimatum.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Glimmerbell", 53, Rarity.COMMON, mage.cards.g.Glimmerbell.class));
         cards.add(new SetCardInfo("Gloom Pangolin", 89, Rarity.COMMON, mage.cards.g.GloomPangolin.class));
-        cards.add(new SetCardInfo("Glowstone Recluse", 156, Rarity.UNCOMMON, mage.cards.g.GlowstoneRecluse.class));
+        cards.add(new SetCardInfo("Glowstone Recluse", 156, Rarity.UNCOMMON, mage.cards.g.GlowstoneRecluse.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Glowstone Recluse", 296, Rarity.UNCOMMON, mage.cards.g.GlowstoneRecluse.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Go for Blood", 122, Rarity.COMMON, mage.cards.g.GoForBlood.class));
         cards.add(new SetCardInfo("Greater Sandwurm", 157, Rarity.COMMON, mage.cards.g.GreaterSandwurm.class));
         cards.add(new SetCardInfo("Grimdancer", 90, Rarity.UNCOMMON, mage.cards.g.Grimdancer.class));
         cards.add(new SetCardInfo("Gust of Wind", 54, Rarity.COMMON, mage.cards.g.GustOfWind.class));
-        cards.add(new SetCardInfo("Gyruda, Doom of Depths", 221, Rarity.RARE, mage.cards.g.GyrudaDoomOfDepths.class));
+        cards.add(new SetCardInfo("Gyruda, Doom of Depths", 221, Rarity.RARE, mage.cards.g.GyrudaDoomOfDepths.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Gyruda, Doom of Depths", 351, Rarity.RARE, mage.cards.g.GyrudaDoomOfDepths.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Gyruda, Doom of Depths", 384, Rarity.RARE, mage.cards.g.GyrudaDoomOfDepths.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Hampering Snare", 55, Rarity.COMMON, mage.cards.h.HamperingSnare.class));
-        cards.add(new SetCardInfo("Heartless Act", 91, Rarity.UNCOMMON, mage.cards.h.HeartlessAct.class));
+        cards.add(new SetCardInfo("Heartless Act", 366, Rarity.UNCOMMON, mage.cards.h.HeartlessAct.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Heartless Act", 91, Rarity.UNCOMMON, mage.cards.h.HeartlessAct.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Heightened Reflexes", 123, Rarity.COMMON, mage.cards.h.HeightenedReflexes.class));
         cards.add(new SetCardInfo("Helica Glider", 15, Rarity.COMMON, mage.cards.h.HelicaGlider.class));
         cards.add(new SetCardInfo("Honey Mammoth", 158, Rarity.COMMON, mage.cards.h.HoneyMammoth.class));
         cards.add(new SetCardInfo("Hornbash Mentor", 159, Rarity.UNCOMMON, mage.cards.h.HornbashMentor.class));
         cards.add(new SetCardInfo("Humble Naturalist", 160, Rarity.COMMON, mage.cards.h.HumbleNaturalist.class));
-        cards.add(new SetCardInfo("Hunted Nightmare", 92, Rarity.RARE, mage.cards.h.HuntedNightmare.class));
+        cards.add(new SetCardInfo("Hunted Nightmare", 322, Rarity.RARE, mage.cards.h.HuntedNightmare.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Hunted Nightmare", 92, Rarity.RARE, mage.cards.h.HuntedNightmare.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Huntmaster Liger", 16, Rarity.UNCOMMON, mage.cards.h.HuntmasterLiger.class));
-        cards.add(new SetCardInfo("Illuna, Apex of Wishes", 190, Rarity.MYTHIC, mage.cards.i.IllunaApexOfWishes.class));
+        cards.add(new SetCardInfo("Huntmaster Liger", 280, Rarity.UNCOMMON, mage.cards.h.HuntmasterLiger.class));
+        cards.add(new SetCardInfo("Huntmaster Liger", 370, Rarity.UNCOMMON, mage.cards.h.HuntmasterLiger.class));
+        cards.add(new SetCardInfo("Illuna, Apex of Wishes", 190, Rarity.MYTHIC, mage.cards.i.IllunaApexOfWishes.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Illuna, Apex of Wishes", 300, Rarity.MYTHIC, mage.cards.i.IllunaApexOfWishes.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Illuna, Apex of Wishes", 379, Rarity.MYTHIC, mage.cards.i.IllunaApexOfWishes.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Imposing Vantasaur", 17, Rarity.COMMON, mage.cards.i.ImposingVantasaur.class));
         cards.add(new SetCardInfo("Indatha Crystal", 235, Rarity.UNCOMMON, mage.cards.i.IndathaCrystal.class));
-        cards.add(new SetCardInfo("Indatha Triome", 248, Rarity.RARE, mage.cards.i.IndathaTriome.class));
+        cards.add(new SetCardInfo("Indatha Triome", 248, Rarity.RARE, mage.cards.i.IndathaTriome.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Indatha Triome", 309, Rarity.RARE, mage.cards.i.IndathaTriome.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Insatiable Hemophage", 290, Rarity.UNCOMMON, mage.cards.i.InsatiableHemophage.class));
         cards.add(new SetCardInfo("Insatiable Hemophage", 93, Rarity.UNCOMMON, mage.cards.i.InsatiableHemophage.class));
-        cards.add(new SetCardInfo("Inspired Ultimatum", 191, Rarity.RARE, mage.cards.i.InspiredUltimatum.class));
+        cards.add(new SetCardInfo("Inspired Ultimatum", 191, Rarity.RARE, mage.cards.i.InspiredUltimatum.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Inspired Ultimatum", 337, Rarity.RARE, mage.cards.i.InspiredUltimatum.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Island", 263, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Island", 264, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Island", 265, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Ivy Elemental", 161, Rarity.UNCOMMON, mage.cards.i.IvyElemental.class));
-        cards.add(new SetCardInfo("Jegantha, the Wellspring", 222, Rarity.RARE, mage.cards.j.JeganthaTheWellspring.class));
+        cards.add(new SetCardInfo("Jegantha, the Wellspring", 222, Rarity.RARE, mage.cards.j.JeganthaTheWellspring.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Jegantha, the Wellspring", 352, Rarity.RARE, mage.cards.j.JeganthaTheWellspring.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Jubilant Skybonder", 223, Rarity.UNCOMMON, mage.cards.j.JubilantSkybonder.class));
         cards.add(new SetCardInfo("Jungle Hollow", 249, Rarity.COMMON, mage.cards.j.JungleHollow.class));
-        cards.add(new SetCardInfo("Kaheera, the Orphanguard", 224, Rarity.RARE, mage.cards.k.KaheeraTheOrphanguard.class));
+        cards.add(new SetCardInfo("Kaheera, the Orphanguard", 224, Rarity.RARE, mage.cards.k.KaheeraTheOrphanguard.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Kaheera, the Orphanguard", 353, Rarity.RARE, mage.cards.k.KaheeraTheOrphanguard.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Keensight Mentor", 18, Rarity.UNCOMMON, mage.cards.k.KeensightMentor.class));
         cards.add(new SetCardInfo("Keep Safe", 56, Rarity.COMMON, mage.cards.k.KeepSafe.class));
-        cards.add(new SetCardInfo("Keruga, the Macrosage", 354, Rarity.RARE, mage.cards.k.KerugaTheMacrosage.class));
+        cards.add(new SetCardInfo("Keruga, the Macrosage", 225, Rarity.RARE, mage.cards.k.KerugaTheMacrosage.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Keruga, the Macrosage", 354, Rarity.RARE, mage.cards.k.KerugaTheMacrosage.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Ketria Crystal", 236, Rarity.UNCOMMON, mage.cards.k.KetriaCrystal.class));
-        cards.add(new SetCardInfo("Ketria Triome", 250, Rarity.RARE, mage.cards.k.KetriaTriome.class));
-        cards.add(new SetCardInfo("Kinnan, Bonder Prodigy", 192, Rarity.MYTHIC, mage.cards.k.KinnanBonderProdigy.class));
-        cards.add(new SetCardInfo("Kogla, the Titan Ape", 162, Rarity.RARE, mage.cards.k.KoglaTheTitanApe.class));
-        cards.add(new SetCardInfo("Labyrinth Raptor", 193, Rarity.RARE, mage.cards.l.LabyrinthRaptor.class));
+        cards.add(new SetCardInfo("Ketria Triome", 250, Rarity.RARE, mage.cards.k.KetriaTriome.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Ketria Triome", 310, Rarity.RARE, mage.cards.k.KetriaTriome.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Kinnan, Bonder Prodigy", 192, Rarity.MYTHIC, mage.cards.k.KinnanBonderProdigy.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Kinnan, Bonder Prodigy", 338, Rarity.MYTHIC, mage.cards.k.KinnanBonderProdigy.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Kogla, the Titan Ape", 162, Rarity.RARE, mage.cards.k.KoglaTheTitanApe.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Kogla, the Titan Ape", 328, Rarity.RARE, mage.cards.k.KoglaTheTitanApe.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Labyrinth Raptor", 193, Rarity.RARE, mage.cards.l.LabyrinthRaptor.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Labyrinth Raptor", 339, Rarity.RARE, mage.cards.l.LabyrinthRaptor.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Lava Serpent", 124, Rarity.COMMON, mage.cards.l.LavaSerpent.class));
-        cards.add(new SetCardInfo("Lavabrink Venturer", 19, Rarity.RARE, mage.cards.l.LavabrinkVenturer.class));
+        cards.add(new SetCardInfo("Lavabrink Venturer", 19, Rarity.RARE, mage.cards.l.LavabrinkVenturer.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Lavabrink Venturer", 315, Rarity.RARE, mage.cards.l.LavabrinkVenturer.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Lead the Stampede", 163, Rarity.UNCOMMON, mage.cards.l.LeadTheStampede.class));
         cards.add(new SetCardInfo("Light of Hope", 20, Rarity.COMMON, mage.cards.l.LightOfHope.class));
         cards.add(new SetCardInfo("Lore Drakkis", 194, Rarity.UNCOMMON, mage.cards.l.LoreDrakkis.class));
-        cards.add(new SetCardInfo("Lukka, Coppercoat Outcast", 125, Rarity.MYTHIC, mage.cards.l.LukkaCoppercoatOutcast.class));
-        cards.add(new SetCardInfo("Luminous Broodmoth", 316, Rarity.MYTHIC, mage.cards.l.LuminousBroodmoth.class));
+        cards.add(new SetCardInfo("Lore Drakkis", 301, Rarity.UNCOMMON, mage.cards.l.LoreDrakkis.class));
+        cards.add(new SetCardInfo("Lukka, Coppercoat Outcast", 125, Rarity.MYTHIC, mage.cards.l.LukkaCoppercoatOutcast.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Lukka, Coppercoat Outcast", 276, Rarity.MYTHIC, mage.cards.l.LukkaCoppercoatOutcast.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Luminous Broodmoth", 21, Rarity.MYTHIC, mage.cards.l.LuminousBroodmoth.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Luminous Broodmoth", 316, Rarity.MYTHIC, mage.cards.l.LuminousBroodmoth.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Luminous Broodmoth", 371, Rarity.MYTHIC, mage.cards.l.LuminousBroodmoth.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Lurking Deadeye", 94, Rarity.COMMON, mage.cards.l.LurkingDeadeye.class));
-        cards.add(new SetCardInfo("Lurrus of the Dream-Den", 226, Rarity.RARE, mage.cards.l.LurrusOfTheDreamDen.class));
-        cards.add(new SetCardInfo("Lutri, the Spellchaser", 227, Rarity.RARE, mage.cards.l.LutriTheSpellchaser.class));
-        cards.add(new SetCardInfo("Majestic Auricorn", 22, Rarity.UNCOMMON, mage.cards.m.MajesticAuricorn.class));
+        cards.add(new SetCardInfo("Lurrus of the Dream-Den", 226, Rarity.RARE, mage.cards.l.LurrusOfTheDreamDen.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Lurrus of the Dream-Den", 355, Rarity.RARE, mage.cards.l.LurrusOfTheDreamDen.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Lutri, the Spellchaser", 227, Rarity.RARE, mage.cards.l.LutriTheSpellchaser.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Lutri, the Spellchaser", 356, Rarity.RARE, mage.cards.l.LutriTheSpellchaser.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Majestic Auricorn", 22, Rarity.UNCOMMON, mage.cards.m.MajesticAuricorn.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Majestic Auricorn", 281, Rarity.UNCOMMON, mage.cards.m.MajesticAuricorn.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Maned Serval", 23, Rarity.COMMON, mage.cards.m.ManedServal.class));
         cards.add(new SetCardInfo("Memory Leak", 95, Rarity.COMMON, mage.cards.m.MemoryLeak.class));
-        cards.add(new SetCardInfo("Migration Path", 164, Rarity.UNCOMMON, mage.cards.m.MigrationPath.class));
-        cards.add(new SetCardInfo("Migratory Greathorn", 165, Rarity.COMMON, mage.cards.m.MigratoryGreathorn.class));
+        cards.add(new SetCardInfo("Migration Path", 164, Rarity.UNCOMMON, mage.cards.m.MigrationPath.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Migration Path", 368, Rarity.UNCOMMON, mage.cards.m.MigrationPath.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Migratory Greathorn", 165, Rarity.COMMON, mage.cards.m.MigratoryGreathorn.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Migratory Greathorn", 297, Rarity.COMMON, mage.cards.m.MigratoryGreathorn.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Momentum Rumbler", 126, Rarity.UNCOMMON, mage.cards.m.MomentumRumbler.class));
         cards.add(new SetCardInfo("Monstrous Step", 166, Rarity.UNCOMMON, mage.cards.m.MonstrousStep.class));
         cards.add(new SetCardInfo("Mosscoat Goriak", 167, Rarity.COMMON, mage.cards.m.MosscoatGoriak.class));
@@ -230,24 +290,37 @@ public final class IkoriaLairOfBehemoths extends ExpansionSet {
         cards.add(new SetCardInfo("Mountain", 270, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Mountain", 271, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Mutual Destruction", 96, Rarity.COMMON, mage.cards.m.MutualDestruction.class));
-        cards.add(new SetCardInfo("Mysterious Egg", 3, Rarity.COMMON, mage.cards.m.MysteriousEgg.class));
+        cards.add(new SetCardInfo("Mysterious Egg", 3, Rarity.COMMON, mage.cards.m.MysteriousEgg.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Mysterious Egg", 385, Rarity.COMMON, mage.cards.m.MysteriousEgg.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Mystic Subdual", 57, Rarity.UNCOMMON, mage.cards.m.MysticSubdual.class));
-        cards.add(new SetCardInfo("Mythos of Brokkos", 168, Rarity.RARE, mage.cards.m.MythosOfBrokkos.class));
-        cards.add(new SetCardInfo("Mythos of Illuna", 58, Rarity.RARE, mage.cards.m.MythosOfIlluna.class));
-        cards.add(new SetCardInfo("Mythos of Nethroi", 97, Rarity.RARE, mage.cards.m.MythosOfNethroi.class));
-        cards.add(new SetCardInfo("Mythos of Snapdax", 24, Rarity.RARE, mage.cards.m.MythosOfSnapdax.class));
-        cards.add(new SetCardInfo("Mythos of Vadrok", 127, Rarity.RARE, mage.cards.m.MythosOfVadrok.class));
-        cards.add(new SetCardInfo("Narset of the Ancient Way", 195, Rarity.MYTHIC, mage.cards.n.NarsetOfTheAncientWay.class));
+        cards.add(new SetCardInfo("Mythos of Brokkos", 168, Rarity.RARE, mage.cards.m.MythosOfBrokkos.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Mythos of Brokkos", 329, Rarity.RARE, mage.cards.m.MythosOfBrokkos.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Mythos of Illuna", 318, Rarity.RARE, mage.cards.m.MythosOfIlluna.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Mythos of Illuna", 58, Rarity.RARE, mage.cards.m.MythosOfIlluna.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Mythos of Nethroi", 323, Rarity.RARE, mage.cards.m.MythosOfNethroi.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Mythos of Nethroi", 97, Rarity.RARE, mage.cards.m.MythosOfNethroi.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Mythos of Snapdax", 24, Rarity.RARE, mage.cards.m.MythosOfSnapdax.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Mythos of Snapdax", 317, Rarity.RARE, mage.cards.m.MythosOfSnapdax.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Mythos of Vadrok", 127, Rarity.RARE, mage.cards.m.MythosOfVadrok.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Mythos of Vadrok", 324, Rarity.RARE, mage.cards.m.MythosOfVadrok.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Narset of the Ancient Way", 195, Rarity.MYTHIC, mage.cards.n.NarsetOfTheAncientWay.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Narset of the Ancient Way", 278, Rarity.MYTHIC, mage.cards.n.NarsetOfTheAncientWay.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Necropanther", 196, Rarity.UNCOMMON, mage.cards.n.Necropanther.class));
-        cards.add(new SetCardInfo("Nethroi, Apex of Death", 197, Rarity.MYTHIC, mage.cards.n.NethroiApexOfDeath.class));
+        cards.add(new SetCardInfo("Necropanther", 302, Rarity.UNCOMMON, mage.cards.n.Necropanther.class));
+        cards.add(new SetCardInfo("Nethroi, Apex of Death", 197, Rarity.MYTHIC, mage.cards.n.NethroiApexOfDeath.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Nethroi, Apex of Death", 303, Rarity.MYTHIC, mage.cards.n.NethroiApexOfDeath.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Nethroi, Apex of Death", 380, Rarity.MYTHIC, mage.cards.n.NethroiApexOfDeath.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Neutralize", 59, Rarity.UNCOMMON, mage.cards.n.Neutralize.class));
         cards.add(new SetCardInfo("Nightsquad Commando", 98, Rarity.COMMON, mage.cards.n.NightsquadCommando.class));
-        cards.add(new SetCardInfo("Obosh, the Preypiercer", 228, Rarity.RARE, mage.cards.o.OboshThePreypiercer.class));
+        cards.add(new SetCardInfo("Obosh, the Preypiercer", 228, Rarity.RARE, mage.cards.o.OboshThePreypiercer.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Obosh, the Preypiercer", 357, Rarity.RARE, mage.cards.o.OboshThePreypiercer.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Of One Mind", 60, Rarity.COMMON, mage.cards.o.OfOneMind.class));
-        cards.add(new SetCardInfo("Offspring's Revenge", 198, Rarity.RARE, mage.cards.o.OffspringsRevenge.class));
+        cards.add(new SetCardInfo("Offspring's Revenge", 198, Rarity.RARE, mage.cards.o.OffspringsRevenge.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Offspring's Revenge", 340, Rarity.RARE, mage.cards.o.OffspringsRevenge.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Ominous Seas", 61, Rarity.UNCOMMON, mage.cards.o.OminousSeas.class));
         cards.add(new SetCardInfo("Pacifism", 25, Rarity.COMMON, mage.cards.p.Pacifism.class));
-        cards.add(new SetCardInfo("Parcelbeast", 199, Rarity.UNCOMMON, mage.cards.p.Parcelbeast.class));
+        cards.add(new SetCardInfo("Parcelbeast", 199, Rarity.UNCOMMON, mage.cards.p.Parcelbeast.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Parcelbeast", 304, Rarity.UNCOMMON, mage.cards.p.Parcelbeast.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Patagia Tiger", 26, Rarity.COMMON, mage.cards.p.PatagiaTiger.class));
         cards.add(new SetCardInfo("Perimeter Sergeant", 27, Rarity.COMMON, mage.cards.p.PerimeterSergeant.class));
         cards.add(new SetCardInfo("Phase Dolphin", 62, Rarity.COMMON, mage.cards.p.PhaseDolphin.class));
@@ -255,51 +328,69 @@ public final class IkoriaLairOfBehemoths extends ExpansionSet {
         cards.add(new SetCardInfo("Plains", 261, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Plains", 262, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Plummet", 169, Rarity.COMMON, mage.cards.p.Plummet.class));
-        cards.add(new SetCardInfo("Pollywog Symbiote", 63, Rarity.UNCOMMON, mage.cards.p.PollywogSymbiote.class));
-        cards.add(new SetCardInfo("Porcuparrot", 128, Rarity.UNCOMMON, mage.cards.p.Porcuparrot.class));
-        cards.add(new SetCardInfo("Pouncing Shoreshark", 64, Rarity.UNCOMMON, mage.cards.p.PouncingShoreshark.class));
+        cards.add(new SetCardInfo("Pollywog Symbiote", 372, Rarity.UNCOMMON, mage.cards.p.PollywogSymbiote.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Pollywog Symbiote", 63, Rarity.UNCOMMON, mage.cards.p.PollywogSymbiote.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Porcuparrot", 128, Rarity.UNCOMMON, mage.cards.p.Porcuparrot.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Porcuparrot", 293, Rarity.UNCOMMON, mage.cards.p.Porcuparrot.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Pouncing Shoreshark", 285, Rarity.UNCOMMON, mage.cards.p.PouncingShoreshark.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Pouncing Shoreshark", 64, Rarity.UNCOMMON, mage.cards.p.PouncingShoreshark.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Prickly Marmoset", 129, Rarity.COMMON, mage.cards.p.PricklyMarmoset.class));
         cards.add(new SetCardInfo("Primal Empathy", 200, Rarity.UNCOMMON, mage.cards.p.PrimalEmpathy.class));
         cards.add(new SetCardInfo("Proud Wildbonder", 229, Rarity.UNCOMMON, mage.cards.p.ProudWildbonder.class));
         cards.add(new SetCardInfo("Pyroceratops", 130, Rarity.COMMON, mage.cards.p.Pyroceratops.class));
-        cards.add(new SetCardInfo("Quartzwood Crasher", 201, Rarity.RARE, mage.cards.q.QuartzwoodCrasher.class));
+        cards.add(new SetCardInfo("Quartzwood Crasher", 201, Rarity.RARE, mage.cards.q.QuartzwoodCrasher.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Quartzwood Crasher", 341, Rarity.RARE, mage.cards.q.QuartzwoodCrasher.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Raking Claws", 131, Rarity.COMMON, mage.cards.r.RakingClaws.class));
         cards.add(new SetCardInfo("Ram Through", 170, Rarity.COMMON, mage.cards.r.RamThrough.class));
         cards.add(new SetCardInfo("Raugrin Crystal", 238, Rarity.UNCOMMON, mage.cards.r.RaugrinCrystal.class));
-        cards.add(new SetCardInfo("Raugrin Triome", 251, Rarity.RARE, mage.cards.r.RaugrinTriome.class));
+        cards.add(new SetCardInfo("Raugrin Triome", 251, Rarity.RARE, mage.cards.r.RaugrinTriome.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Raugrin Triome", 311, Rarity.RARE, mage.cards.r.RaugrinTriome.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Reconnaissance Mission", 65, Rarity.UNCOMMON, mage.cards.r.ReconnaissanceMission.class));
-        cards.add(new SetCardInfo("Regal Leosaur", 202, Rarity.UNCOMMON, mage.cards.r.RegalLeosaur.class));
+        cards.add(new SetCardInfo("Regal Leosaur", 202, Rarity.UNCOMMON, mage.cards.r.RegalLeosaur.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Regal Leosaur", 305, Rarity.UNCOMMON, mage.cards.r.RegalLeosaur.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Reptilian Reflection", 132, Rarity.UNCOMMON, mage.cards.r.ReptilianReflection.class));
-        cards.add(new SetCardInfo("Rielle, the Everwise", 203, Rarity.MYTHIC, mage.cards.r.RielleTheEverwise.class));
+        cards.add(new SetCardInfo("Rielle, the Everwise", 203, Rarity.MYTHIC, mage.cards.r.RielleTheEverwise.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Rielle, the Everwise", 342, Rarity.MYTHIC, mage.cards.r.RielleTheEverwise.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Rooting Moloch", 133, Rarity.UNCOMMON, mage.cards.r.RootingMoloch.class));
         cards.add(new SetCardInfo("Rugged Highlands", 252, Rarity.COMMON, mage.cards.r.RuggedHighlands.class));
-        cards.add(new SetCardInfo("Ruinous Ultimatum", 204, Rarity.RARE, mage.cards.r.RuinousUltimatum.class));
+        cards.add(new SetCardInfo("Ruinous Ultimatum", 204, Rarity.RARE, mage.cards.r.RuinousUltimatum.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Ruinous Ultimatum", 343, Rarity.RARE, mage.cards.r.RuinousUltimatum.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Rumbling Rockslide", 134, Rarity.COMMON, mage.cards.r.RumblingRockslide.class));
         cards.add(new SetCardInfo("Sanctuary Lockdown", 28, Rarity.UNCOMMON, mage.cards.s.SanctuaryLockdown.class));
         cards.add(new SetCardInfo("Sanctuary Smasher", 135, Rarity.UNCOMMON, mage.cards.s.SanctuarySmasher.class));
         cards.add(new SetCardInfo("Savai Crystal", 239, Rarity.UNCOMMON, mage.cards.s.SavaiCrystal.class));
         cards.add(new SetCardInfo("Savai Sabertooth", 29, Rarity.COMMON, mage.cards.s.SavaiSabertooth.class));
         cards.add(new SetCardInfo("Savai Thundermane", 205, Rarity.UNCOMMON, mage.cards.s.SavaiThundermane.class));
-        cards.add(new SetCardInfo("Savai Triome", 253, Rarity.RARE, mage.cards.s.SavaiTriome.class));
+        cards.add(new SetCardInfo("Savai Triome", 253, Rarity.RARE, mage.cards.s.SavaiTriome.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Savai Triome", 312, Rarity.RARE, mage.cards.s.SavaiTriome.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Scoured Barrens", 254, Rarity.COMMON, mage.cards.s.ScouredBarrens.class));
-        cards.add(new SetCardInfo("Sea-Dasher Octopus", 66, Rarity.RARE, mage.cards.s.SeaDasherOctopus.class));
+        cards.add(new SetCardInfo("Sea-Dasher Octopus", 286, Rarity.RARE, mage.cards.s.SeaDasherOctopus.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Sea-Dasher Octopus", 66, Rarity.RARE, mage.cards.s.SeaDasherOctopus.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Serrated Scorpion", 99, Rarity.COMMON, mage.cards.s.SerratedScorpion.class));
-        cards.add(new SetCardInfo("Shark Typhoon", 67, Rarity.RARE, mage.cards.s.SharkTyphoon.class));
+        cards.add(new SetCardInfo("Shark Typhoon", 319, Rarity.RARE, mage.cards.s.SharkTyphoon.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Shark Typhoon", 67, Rarity.RARE, mage.cards.s.SharkTyphoon.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Shredded Sails", 136, Rarity.COMMON, mage.cards.s.ShreddedSails.class));
         cards.add(new SetCardInfo("Skull Prophet", 206, Rarity.UNCOMMON, mage.cards.s.SkullProphet.class));
-        cards.add(new SetCardInfo("Skycat Sovereign", 207, Rarity.RARE, mage.cards.s.SkycatSovereign.class));
+        cards.add(new SetCardInfo("Skycat Sovereign", 207, Rarity.RARE, mage.cards.s.SkycatSovereign.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Skycat Sovereign", 344, Rarity.RARE, mage.cards.s.SkycatSovereign.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Sleeper Dart", 240, Rarity.COMMON, mage.cards.s.SleeperDart.class));
-        cards.add(new SetCardInfo("Slitherwisp", 208, Rarity.RARE, mage.cards.s.Slitherwisp.class));
-        cards.add(new SetCardInfo("Snapdax, Apex of the Hunt", 209, Rarity.MYTHIC, mage.cards.s.SnapdaxApexOfTheHunt.class));
+        cards.add(new SetCardInfo("Slitherwisp", 208, Rarity.RARE, mage.cards.s.Slitherwisp.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Slitherwisp", 345, Rarity.RARE, mage.cards.s.Slitherwisp.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Snapdax, Apex of the Hunt", 209, Rarity.MYTHIC, mage.cards.s.SnapdaxApexOfTheHunt.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Snapdax, Apex of the Hunt", 306, Rarity.MYTHIC, mage.cards.s.SnapdaxApexOfTheHunt.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Snapdax, Apex of the Hunt", 381, Rarity.MYTHIC, mage.cards.s.SnapdaxApexOfTheHunt.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Snare Tactician", 30, Rarity.COMMON, mage.cards.s.SnareTactician.class));
         cards.add(new SetCardInfo("Solid Footing", 31, Rarity.COMMON, mage.cards.s.SolidFooting.class));
-        cards.add(new SetCardInfo("Song of Creation", 210, Rarity.RARE, mage.cards.s.SongOfCreation.class));
+        cards.add(new SetCardInfo("Song of Creation", 210, Rarity.RARE, mage.cards.s.SongOfCreation.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Song of Creation", 346, Rarity.RARE, mage.cards.s.SongOfCreation.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Sonorous Howlbonder", 230, Rarity.UNCOMMON, mage.cards.s.SonorousHowlbonder.class));
         cards.add(new SetCardInfo("Spelleater Wolverine", 137, Rarity.COMMON, mage.cards.s.SpelleaterWolverine.class));
         cards.add(new SetCardInfo("Splendor Mare", 32, Rarity.UNCOMMON, mage.cards.s.SplendorMare.class));
         cards.add(new SetCardInfo("Spontaneous Flight", 33, Rarity.COMMON, mage.cards.s.SpontaneousFlight.class));
         cards.add(new SetCardInfo("Springjaw Trap", 241, Rarity.COMMON, mage.cards.s.SpringjawTrap.class));
-        cards.add(new SetCardInfo("Sprite Dragon", 211, Rarity.UNCOMMON, mage.cards.s.SpriteDragon.class));
+        cards.add(new SetCardInfo("Sprite Dragon", 211, Rarity.UNCOMMON, mage.cards.s.SpriteDragon.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Sprite Dragon", 369, Rarity.UNCOMMON, mage.cards.s.SpriteDragon.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Sprite Dragon", 382, Rarity.UNCOMMON, mage.cards.s.SpriteDragon.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Startling Development", 68, Rarity.COMMON, mage.cards.s.StartlingDevelopment.class));
         cards.add(new SetCardInfo("Stormwild Capridor", 34, Rarity.UNCOMMON, mage.cards.s.StormwildCapridor.class));
         cards.add(new SetCardInfo("Sudden Spinnerets", 171, Rarity.COMMON, mage.cards.s.SuddenSpinnerets.class));
@@ -311,42 +402,61 @@ public final class IkoriaLairOfBehemoths extends ExpansionSet {
         cards.add(new SetCardInfo("Swamp", 268, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Swiftwater Cliffs", 255, Rarity.COMMON, mage.cards.s.SwiftwaterCliffs.class));
         cards.add(new SetCardInfo("Tentative Connection", 138, Rarity.COMMON, mage.cards.t.TentativeConnection.class));
-        cards.add(new SetCardInfo("The Ozolith", 237, Rarity.RARE, mage.cards.t.TheOzolith.class));
+        cards.add(new SetCardInfo("The Ozolith", 237, Rarity.RARE, mage.cards.t.TheOzolith.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("The Ozolith", 362, Rarity.RARE, mage.cards.t.TheOzolith.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Thieving Otter", 69, Rarity.COMMON, mage.cards.t.ThievingOtter.class));
         cards.add(new SetCardInfo("Thornwood Falls", 256, Rarity.COMMON, mage.cards.t.ThornwoodFalls.class));
         cards.add(new SetCardInfo("Thwart the Enemy", 173, Rarity.COMMON, mage.cards.t.ThwartTheEnemy.class));
         cards.add(new SetCardInfo("Titanoth Rex", 174, Rarity.UNCOMMON, mage.cards.t.TitanothRex.class));
-        cards.add(new SetCardInfo("Titans' Nest", 212, Rarity.RARE, mage.cards.t.TitansNest.class));
+        cards.add(new SetCardInfo("Titanoth Rex", 377, Rarity.UNCOMMON, mage.cards.t.TitanothRex.class));
+        cards.add(new SetCardInfo("Titans' Nest", 212, Rarity.RARE, mage.cards.t.TitansNest.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Titans' Nest", 347, Rarity.RARE, mage.cards.t.TitansNest.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Tranquil Cove", 257, Rarity.COMMON, mage.cards.t.TranquilCove.class));
         cards.add(new SetCardInfo("Trumpeting Gnarr", 213, Rarity.UNCOMMON, mage.cards.t.TrumpetingGnarr.class));
-        cards.add(new SetCardInfo("Umori, the Collector", 231, Rarity.RARE, mage.cards.u.UmoriTheCollector.class));
+        cards.add(new SetCardInfo("Trumpeting Gnarr", 307, Rarity.UNCOMMON, mage.cards.t.TrumpetingGnarr.class));
+        cards.add(new SetCardInfo("Umori, the Collector", 231, Rarity.RARE, mage.cards.u.UmoriTheCollector.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Umori, the Collector", 358, Rarity.RARE, mage.cards.u.UmoriTheCollector.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Unbreakable Bond", 101, Rarity.UNCOMMON, mage.cards.u.UnbreakableBond.class));
         cards.add(new SetCardInfo("Unexpected Fangs", 102, Rarity.COMMON, mage.cards.u.UnexpectedFangs.class));
         cards.add(new SetCardInfo("Unlikely Aid", 103, Rarity.COMMON, mage.cards.u.UnlikelyAid.class));
-        cards.add(new SetCardInfo("Unpredictable Cyclone", 139, Rarity.RARE, mage.cards.u.UnpredictableCyclone.class));
-        cards.add(new SetCardInfo("Vadrok, Apex of Thunder", 214, Rarity.MYTHIC, mage.cards.v.VadrokApexOfThunder.class));
+        cards.add(new SetCardInfo("Unpredictable Cyclone", 139, Rarity.RARE, mage.cards.u.UnpredictableCyclone.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Unpredictable Cyclone", 325, Rarity.RARE, mage.cards.u.UnpredictableCyclone.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Vadrok, Apex of Thunder", 214, Rarity.MYTHIC, mage.cards.v.VadrokApexOfThunder.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Vadrok, Apex of Thunder", 308, Rarity.MYTHIC, mage.cards.v.VadrokApexOfThunder.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Vadrok, Apex of Thunder", 383, Rarity.MYTHIC, mage.cards.v.VadrokApexOfThunder.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Valiant Rescuer", 36, Rarity.UNCOMMON, mage.cards.v.ValiantRescuer.class));
-        cards.add(new SetCardInfo("Vivien, Monsters' Advocate", 175, Rarity.MYTHIC, mage.cards.v.VivienMonstersAdvocate.class));
+        cards.add(new SetCardInfo("Vivien, Monsters' Advocate", 175, Rarity.MYTHIC, mage.cards.v.VivienMonstersAdvocate.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Vivien, Monsters' Advocate", 277, Rarity.MYTHIC, mage.cards.v.VivienMonstersAdvocate.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Void Beckoner", 104, Rarity.UNCOMMON, mage.cards.v.VoidBeckoner.class));
-        cards.add(new SetCardInfo("Voracious Greatshark", 70, Rarity.RARE, mage.cards.v.VoraciousGreatshark.class));
+        cards.add(new SetCardInfo("Void Beckoner", 373, Rarity.UNCOMMON, mage.cards.v.VoidBeckoner.class));
+        cards.add(new SetCardInfo("Voracious Greatshark", 320, Rarity.RARE, mage.cards.v.VoraciousGreatshark.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Voracious Greatshark", 70, Rarity.RARE, mage.cards.v.VoraciousGreatshark.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Vulpikeet", 282, Rarity.COMMON, mage.cards.v.Vulpikeet.class));
         cards.add(new SetCardInfo("Vulpikeet", 37, Rarity.COMMON, mage.cards.v.Vulpikeet.class));
         cards.add(new SetCardInfo("Weaponize the Monsters", 140, Rarity.UNCOMMON, mage.cards.w.WeaponizeTheMonsters.class));
-        cards.add(new SetCardInfo("Whirlwind of Thought", 215, Rarity.RARE, mage.cards.w.WhirlwindOfThought.class));
+        cards.add(new SetCardInfo("Whirlwind of Thought", 215, Rarity.RARE, mage.cards.w.WhirlwindOfThought.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Whirlwind of Thought", 348, Rarity.RARE, mage.cards.w.WhirlwindOfThought.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Whisper Squad", 105, Rarity.COMMON, mage.cards.w.WhisperSquad.class));
         cards.add(new SetCardInfo("Will of the All-Hunter", 38, Rarity.UNCOMMON, mage.cards.w.WillOfTheAllHunter.class));
         cards.add(new SetCardInfo("Wilt", 176, Rarity.COMMON, mage.cards.w.Wilt.class));
         cards.add(new SetCardInfo("Wind-Scarred Crag", 258, Rarity.COMMON, mage.cards.w.WindScarredCrag.class));
         cards.add(new SetCardInfo("Wingfold Pteron", 71, Rarity.COMMON, mage.cards.w.WingfoldPteron.class));
         cards.add(new SetCardInfo("Wingspan Mentor", 72, Rarity.UNCOMMON, mage.cards.w.WingspanMentor.class));
-        cards.add(new SetCardInfo("Winota, Joiner of Forces", 216, Rarity.MYTHIC, mage.cards.w.WinotaJoinerOfForces.class));
-        cards.add(new SetCardInfo("Yidaro, Wandering Monster", 141, Rarity.RARE, mage.cards.y.YidaroWanderingMonster.class));
-        cards.add(new SetCardInfo("Yorion, Sky Nomad", 232, Rarity.RARE, mage.cards.y.YorionSkyNomad.class));
+        cards.add(new SetCardInfo("Winota, Joiner of Forces", 216, Rarity.MYTHIC, mage.cards.w.WinotaJoinerOfForces.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Winota, Joiner of Forces", 349, Rarity.MYTHIC, mage.cards.w.WinotaJoinerOfForces.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Yidaro, Wandering Monster", 141, Rarity.RARE, mage.cards.y.YidaroWanderingMonster.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Yidaro, Wandering Monster", 326, Rarity.RARE, mage.cards.y.YidaroWanderingMonster.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Yidaro, Wandering Monster", 375, Rarity.RARE, mage.cards.y.YidaroWanderingMonster.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Yorion, Sky Nomad", 232, Rarity.RARE, mage.cards.y.YorionSkyNomad.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Yorion, Sky Nomad", 359, Rarity.RARE, mage.cards.y.YorionSkyNomad.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Zagoth Crystal", 242, Rarity.UNCOMMON, mage.cards.z.ZagothCrystal.class));
         cards.add(new SetCardInfo("Zagoth Mamba", 106, Rarity.UNCOMMON, mage.cards.z.ZagothMamba.class));
-        cards.add(new SetCardInfo("Zagoth Triome", 259, Rarity.RARE, mage.cards.z.ZagothTriome.class));
+        cards.add(new SetCardInfo("Zagoth Triome", 259, Rarity.RARE, mage.cards.z.ZagothTriome.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Zagoth Triome", 313, Rarity.RARE, mage.cards.z.ZagothTriome.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Zenith Flare", 217, Rarity.UNCOMMON, mage.cards.z.ZenithFlare.class));
-        cards.add(new SetCardInfo("Zilortha, Strength Incarnate", 275, Rarity.MYTHIC, mage.cards.z.ZilorthaStrengthIncarnate.class));
-        cards.add(new SetCardInfo("Zirda, the Dawnwaker", 233, Rarity.RARE, mage.cards.z.ZirdaTheDawnwaker.class));
+        cards.add(new SetCardInfo("Zilortha, Strength Incarnate", 275, Rarity.MYTHIC, mage.cards.z.ZilorthaStrengthIncarnate.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Zirda, the Dawnwaker", 233, Rarity.RARE, mage.cards.z.ZirdaTheDawnwaker.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Zirda, the Dawnwaker", 360, Rarity.RARE, mage.cards.z.ZirdaTheDawnwaker.class, NON_FULL_USE_VARIOUS));
 
         cards.removeIf(setCardInfo -> mutateNames.contains(setCardInfo.getName())); // remove when mutate is implemented
     }
diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt
index dee5f2d791..e9e4d9ceda 100644
--- a/Utils/mtg-cards-data.txt
+++ b/Utils/mtg-cards-data.txt
@@ -37071,6 +37071,7 @@ Imposing Vantasaur|Ikoria: Lair of Behemoths|17|C|{5}{W}|Creature - Dinosaur|3|6
 Keensight Mentor|Ikoria: Lair of Behemoths|18|U|{2}{W}|Creature - Human Cleric|1|4|When Keensight Mentor enters the battlefield, put a vigilance counter on target non-Human creature you control.${1}{W}, {T}: Put a +1/+1 counter on each creature you control with vigilance.|
 Lavabrink Venturer|Ikoria: Lair of Behemoths|19|R|{2}{W}|Creature - Human Soldier|3|3|As Lavabrink Venturer enters the battlefield, choose odd or even.$Lavabrink Venturer has protection from each converted mana cost of the chosen value.|
 Light of Hope|Ikoria: Lair of Behemoths|20|C|{W}|Instant|||Choose one —$• You gain 4 life.$• Destroy target enchantment.$• Put a +1/+1 counter on target creature.|
+Luminous Broodmoth|Ikoria: Lair of Behemoths|21|M|{2}{W}{W}|Creature - Insect|3|4|Flying$Whenever a creature you control without flying dies, return it to the battlefield under its owner's control with a flying counter on it.|
 Majestic Auricorn|Ikoria: Lair of Behemoths|22|U|{4}{W}|Creature - Unicorn|4|4|Mutate {3}{W}$Vigilance$Whenever this creature mutates, you gain 4 life.|
 Maned Serval|Ikoria: Lair of Behemoths|23|C|{1}{W}|Creature - Cat|1|4|Vigilance|
 Mythos of Snapdax|Ikoria: Lair of Behemoths|24|R|{2}{W}{W}|Sorcery|||Each player chooses an artifact, a creature, an enchantment, and a planeswalker from among the nonland permanents they control, then sacrifices the rest. If {B}{R} was spent to cast this spell, you choose the permanents for each player instead.|
@@ -37274,6 +37275,7 @@ Gyruda, Doom of Depths|Ikoria: Lair of Behemoths|221|R|{4}{U/B}{U/B}|Legendary C
 Jegantha, the Wellspring|Ikoria: Lair of Behemoths|222|R|{4}{R/G}|Legendary Creature - Elemental Elk|5|5|Companion — No card in your starting deck has more than one of the same mana symbol in its mana cost.${T}: Add {W}{U}{B}{R}{G}. This mana can't be spent to pay generic mana costs.|
 Jubilant Skybonder|Ikoria: Lair of Behemoths|223|U|{1}{W/U}{W/U}|Creature - Human Wizard|2|2|Flying$Creatures you control with flying have "Spells your opponents cast that target this creature cost {2} more to cast."|
 Kaheera, the Orphanguard|Ikoria: Lair of Behemoths|224|R|{1}{G/W}{G/W}|Legendary Creature - Cat Beast|3|2|Companion — Each creature card in your starting deck is a Cat, Elemental, Nightmare, Dinosaur, or Beast card.$Vigilance$Each other creature you control that's a Cat, Elemental, Nightmare, Dinosaur, or Beast gets +1/+1 and has vigilance.|
+Keruga, the Macrosage|Ikoria: Lair of Behemoths|225|R|{3}{G/U}{G/U}|Legendary Creature - Dinosaur Hippo|5|4|Companion — Your starting deck contains only cards with converted mana cost 3 or greater and land cards.$When Keruga, the Macrosage enters the battlefield, draw a card for each other permanent you control with converted mana cost 3 or greater.|
 Lurrus of the Dream-Den|Ikoria: Lair of Behemoths|226|R|{1}{W/B}{W/B}|Legendary Creature - Cat Nightmare|3|2|Companion — Each permanent card in your starting deck has converted mana cost 2 or less.$Lifelink$During each of your turns, you may cast one permanent spell with converted mana cost 2 or less from your graveyard.|
 Lutri, the Spellchaser|Ikoria: Lair of Behemoths|227|R|{1}{U/R}{U/R}|Legendary Creature - Elemental Otter|3|2|Companion — Each nonland card in your starting deck has a different name.$Flash$When Lutri, the Spellchaser enters the battlefield, if you cast it, copy target instant or sorcery spell you control. You may choose new targets for the copy.|
 Obosh, the Preypiercer|Ikoria: Lair of Behemoths|228|R|{3}{B/R}{B/R}|Legendary Creature - Hellion Horror|3|5|Companion — Your starting deck contains only cards with odd converted mana costs and land cards.$If a source you control with an odd converted mana cost would deal damage to a permanent or player, it deals double that damage to that permanent or player instead.|
@@ -37309,10 +37311,130 @@ Tranquil Cove|Ikoria: Lair of Behemoths|257|C||Land|||Tranquil Cove enters the b
 Wind-Scarred Crag|Ikoria: Lair of Behemoths|258|C||Land|||Wind-Scarred Crag enters the battlefield tapped.$When Wind-Scarred Crag enters the battlefield, you gain 1 life.${T}: Add {R} or {W}.|
 Zagoth Triome|Ikoria: Lair of Behemoths|259|R||Land - Swamp Forest Island|||({T}: Add {B}, {G}, or {U}.)$Zagoth Triome enters the battlefield tapped.$Cycling {3}|
 Plains|Ikoria: Lair of Behemoths|260|C||Basic Land - Plains|||({T}: Add {W}.)|
+Plains|Ikoria: Lair of Behemoths|261|C||Basic Land - Plains|||({T}: Add {W}.)|
+Plains|Ikoria: Lair of Behemoths|262|C||Basic Land - Plains|||({T}: Add {W}.)|
 Island|Ikoria: Lair of Behemoths|263|C||Basic Land - Island|||({T}: Add {U}.)|
+Island|Ikoria: Lair of Behemoths|264|C||Basic Land - Island|||({T}: Add {U}.)|
+Island|Ikoria: Lair of Behemoths|265|C||Basic Land - Island|||({T}: Add {U}.)|
 Swamp|Ikoria: Lair of Behemoths|266|C||Basic Land - Swamp|||({T}: Add {B}.)|
+Swamp|Ikoria: Lair of Behemoths|267|C||Basic Land - Swamp|||({T}: Add {B}.)|
+Swamp|Ikoria: Lair of Behemoths|268|C||Basic Land - Swamp|||({T}: Add {B}.)|
 Mountain|Ikoria: Lair of Behemoths|269|C||Basic Land - Mountain|||({T}: Add {R}.)|
+Mountain|Ikoria: Lair of Behemoths|270|C||Basic Land - Mountain|||({T}: Add {R}.)|
+Mountain|Ikoria: Lair of Behemoths|271|C||Basic Land - Mountain|||({T}: Add {R}.)|
 Forest|Ikoria: Lair of Behemoths|272|C||Basic Land - Forest|||({T}: Add {G}.)|
+Forest|Ikoria: Lair of Behemoths|273|C||Basic Land - Forest|||({T}: Add {G}.)|
+Forest|Ikoria: Lair of Behemoths|274|C||Basic Land - Forest|||({T}: Add {G}.)|
 Zilortha, Strength Incarnate|Ikoria: Lair of Behemoths|275|M|{3}{R}{G}|Legendary Creature - Dinosaur|7|3|Trample$Lethal damage dealt to creatures you control is determined by their power rather than their toughness.|
+Lukka, Coppercoat Outcast|Ikoria: Lair of Behemoths|276|M|{3}{R}{R}|Legendary Planeswalker - Lukka|5|+1: Exile the top three cards of your library. Creature cards exiled this way gain "You may cast this card from exile as long as you control a Lukka planeswalker."$−2: Exile target creature you control, then reveal cards from the top of your library until you reveal a creature card with higher converted mana cost. Put that card onto the battlefield and the rest on the bottom of your library in a random order.$−7: Each creature you control deals damage equal to its power to each opponent.|
+Vivien, Monsters' Advocate|Ikoria: Lair of Behemoths|277|M|{3}{G}{G}|Legendary Planeswalker - Vivien|3|You may look at the top card of your library any time.$You may cast creature spells from the top of your library.$+1: Create a 3/3 green Beast creature token. Put your choice of a vigilance counter, a reach counter, or a trample counter on it.$−2: When you cast your next creature spell this turn, search your library for a creature card with lesser converted mana cost, put it onto the battlefield, then shuffle your library.|
+Narset of the Ancient Way|Ikoria: Lair of Behemoths|278|M|{1}{U}{R}{W}|Legendary Planeswalker - Narset|4|+1: You gain 2 life. Add {U}, {R}, or {W}. Spend this mana only to cast a noncreature spell.$−2: Draw a card, then you may discard a card. When you discard a nonland card this way, Narset of the Ancient Way deals damage equal to that card's converted mana cost to target creature or planeswalker.$−6: You get an emblem with "Whenever you cast a noncreature spell, this emblem deals 2 damage to any target."|
+Cubwarden|Ikoria: Lair of Behemoths|279|R|{3}{W}|Creature - Cat|3|5|Mutate {2}{W}{W}$Lifelink$Whenever this creature mutates, create two 1/1 white Cat creature tokens with lifelink.|
+Huntmaster Liger|Ikoria: Lair of Behemoths|280|U|{3}{W}|Creature - Cat|3|4|Mutate {2}{W}$Whenever this creature mutates, other creatures you control get +X/+X until end of turn, where X is the number of times this creature has mutated.|
+Majestic Auricorn|Ikoria: Lair of Behemoths|281|U|{4}{W}|Creature - Unicorn|4|4|Mutate {3}{W}$Vigilance$Whenever this creature mutates, you gain 4 life.|
+Vulpikeet|Ikoria: Lair of Behemoths|282|C|{3}{W}|Creature - Fox Bird|2|3|Mutate {2}{W}$Flying$Whenever this creature mutates, put a +1/+1 counter on it.|
+Archipelagore|Ikoria: Lair of Behemoths|283|U|{5}{U}{U}|Creature - Leviathan|7|7|Mutate {5}{U}$Whenever this creature mutates, tap up to X target creatures, where X is the number of times this creature has mutated. Those creatures don't untap during their controller's next untap step.|
+Dreamtail Heron|Ikoria: Lair of Behemoths|284|C|{4}{U}|Creature - Elemental Bird|3|4|Mutate {3}{U}$Flying$Whenever this creature mutates, draw a card.|
+Pouncing Shoreshark|Ikoria: Lair of Behemoths|285|U|{4}{U}|Creature - Shark Beast|4|3|Mutate {3}{U}$Flash$Whenever this creature mutates, you may return target creature an opponent controls to its owner's hand.|
+Sea-Dasher Octopus|Ikoria: Lair of Behemoths|286|R|{1}{U}{U}|Creature - Octopus|2|2|Mutate {1}{U}$Flash$Whenever this creature deals combat damage to a player, draw a card.|
+Cavern Whisperer|Ikoria: Lair of Behemoths|287|C|{4}{B}|Creature - Nightmare|4|4|Mutate {3}{B}$Menace$Whenever this creature mutates, each opponent discards a card.|
+Chittering Harvester|Ikoria: Lair of Behemoths|288|U|{5}{B}|Creature - Nightmare|4|6|Mutate {4}{B}$Whenever this creature mutates, each opponent sacrifices a creature.|
+Dirge Bat|Ikoria: Lair of Behemoths|289|R|{2}{B}{B}|Creature - Bat|3|3|Mutate {4}{B}{B}$Flash$Flying$Whenever this creature mutates, destroy target creature or planeswalker an opponent controls.|
+Insatiable Hemophage|Ikoria: Lair of Behemoths|290|U|{3}{B}|Creature - Nightmare|3|3|Mutate {2}{B}$Deathtouch$Whenever this creature mutates, each opponent loses X life and you gain X life, where X is the number of times this creature has mutated.|
+Cloudpiercer|Ikoria: Lair of Behemoths|291|C|{4}{R}|Creature - Dinosaur|5|4|Mutate {3}{R}$Reach$Whenever this creature mutates, you may discard a card. If you do, draw a card.|
+Everquill Phoenix|Ikoria: Lair of Behemoths|292|R|{2}{R}{R}|Creature - Phoenix|4|4|Mutate {3}{R}$Flying$Whenever this creature mutates, create a red artifact token named Feather with "{1}, Sacrifice Feather: Return target Phoenix card from your graveyard to the battlefield tapped."|
+Porcuparrot|Ikoria: Lair of Behemoths|293|U|{3}{R}|Creature - Bird Beast|3|4|Mutate {2}{R}${T}: This creature deals X damage to any target, where X is the number of times this creature has mutated.|
+Auspicious Starrix|Ikoria: Lair of Behemoths|294|U|{4}{G}|Creature - Elk Beast|6|6|Mutate {5}{G}$Whenever this creature mutates, exile cards from the top of your library until you exile X permanent cards, where X is the number of times this creature has mutated. Put those permanent cards onto the battlefield.|
+Gemrazer|Ikoria: Lair of Behemoths|295|R|{3}{G}|Creature - Beast|4|4|Mutate {1}{G}{G}$Reach, trample$Whenever this creature mutates, destroy target artifact or enchantment an opponent controls.|
+Glowstone Recluse|Ikoria: Lair of Behemoths|296|U|{2}{G}|Creature - Spider|2|3|Mutate {3}{G}$Reach$Whenever this creature mutates, put two +1/+1 counters on it.|
+Migratory Greathorn|Ikoria: Lair of Behemoths|297|C|{3}{G}|Creature - Beast|3|4|Mutate {2}{G}$Whenever this creature mutates, search your library for a basic land card, put it onto the battlefield tapped, then shuffle your library.|
+Boneyard Lurker|Ikoria: Lair of Behemoths|298|U|{2}{B}{G}|Creature - Nightmare Beast|4|4|Mutate {2}{B/G}{B/G}$Whenever this creature mutates, return target permanent card from your graveyard to your hand.|
+Brokkos, Apex of Forever|Ikoria: Lair of Behemoths|299|M|{2}{B}{G}{U}|Legendary Creature - Nightmare Beast Elemental|6|6|Mutate {2}{U/B}{G}{G}$Trample$You may cast Brokkos, Apex of Forever from your graveyard using its mutate ability.|
+Illuna, Apex of Wishes|Ikoria: Lair of Behemoths|300|M|{2}{G}{U}{R}|Legendary Creature - Beast Elemental Dinosaur|6|6|Mutate {3}{R/G}{U}{U}$Flying, trample$Whenever this creature mutates, exile cards from the top of your library until you exile a nonland permanent card. Put that card onto the battlefield or into your hand.|
+Lore Drakkis|Ikoria: Lair of Behemoths|301|U|{1}{U}{R}|Creature - Lizard Beast|2|3|Mutate {U/R}{U/R}$Whenever this creature mutates, return target instant or sorcery card from your graveyard to your hand.|
+Necropanther|Ikoria: Lair of Behemoths|302|U|{1}{W}{B}|Creature - Cat Nightmare|3|3|Mutate {2}{W/B}{W/B}$Whenever this creature mutates, return target creature card with converted mana cost 3 or less from your graveyard to the battlefield.|
+Nethroi, Apex of Death|Ikoria: Lair of Behemoths|303|M|{2}{W}{B}{G}|Legendary Creature - Cat Nightmare Beast|5|5|Mutate {4}{G/W}{B}{B}$Deathtouch, lifelink$Whenever this creature mutates, return any number of target creature cards with total power 10 or less from your graveyard to the battlefield.|
+Parcelbeast|Ikoria: Lair of Behemoths|304|U|{2}{G}{U}|Creature - Elemental Beast|2|4|Mutate {G}{U}${1}, {T}: Look at the top card of your library. If it's a land card, you may put it onto the battlefield. If you don't put the card onto the battlefield, put it into your hand.|
+Regal Leosaur|Ikoria: Lair of Behemoths|305|U|{R}{W}|Creature - Dinosaur Cat|2|2|Mutate {1}{R/W}{R/W}$Whenever this creature mutates, other creatures you control get +2/+1 until end of turn.|
+Snapdax, Apex of the Hunt|Ikoria: Lair of Behemoths|306|M|{1}{R}{W}{B}|Legendary Creature - Dinosaur Cat Nightmare|3|5|Mutate {2}{B/R}{W}{W}$Double Strike$Whenever this creature mutates, it deals 4 damage to target creature or planeswalker an opponent controls and you gain 4 life.|
+Trumpeting Gnarr|Ikoria: Lair of Behemoths|307|U|{1}{G}{U}|Creature - Beast|3|3|Mutate {3}{G/U}{G/U}$Whenever this creature mutates, create a 3/3 green Beast creature token.|
+Vadrok, Apex of Thunder|Ikoria: Lair of Behemoths|308|M|{U}{R}{W}|Legendary Creature - Elemental Dinosaur Cat|3|3|Mutate {1}{W/U}{R}{R}$Flying, first strike$Whenever this creature mutates, you may cast target noncreature card with converted mana cost 3 or less from your graveyard without paying its mana cost.|
+Indatha Triome|Ikoria: Lair of Behemoths|309|R||Land - Plains Swamp Forest|||({T}: Add {W}, {B}, or {G}.)$Indatha Triome enters the battlefield tapped.$Cycling {3}|
+Ketria Triome|Ikoria: Lair of Behemoths|310|R||Land - Forest Island Mountain|||({T}: Add {G}, {U}, or {R}.)$Ketria Triome enters the battlefield tapped.$Cycling {3}|
+Raugrin Triome|Ikoria: Lair of Behemoths|311|R||Land - Island Mountain Plains|||({T}: Add {U}, {R}, or {W}.)$Raugrin Triome enters the battlefield tapped.$Cycling {3}|
+Savai Triome|Ikoria: Lair of Behemoths|312|R||Land - Mountain Plains Swamp|||({T}: Add {R}, {W}, or {B}.)$Savai Triome enters the battlefield tapped.$Cycling {3}|
+Zagoth Triome|Ikoria: Lair of Behemoths|313|R||Land - Swamp Forest Island|||({T}: Add {B}, {G}, or {U}.)$Zagoth Triome enters the battlefield tapped.$Cycling {3}|
+Drannith Magistrate|Ikoria: Lair of Behemoths|314|R|{1}{W}|Creature - Human Wizard|1|3|Your opponents can't cast spells from anywhere other than their hands.|
+Lavabrink Venturer|Ikoria: Lair of Behemoths|315|R|{2}{W}|Creature - Human Soldier|3|3|As Lavabrink Venturer enters the battlefield, choose odd or even.$Lavabrink Venturer has protection from each converted mana cost of the chosen value.|
 Luminous Broodmoth|Ikoria: Lair of Behemoths|316|M|{2}{W}{W}|Creature - Insect|3|4|Flying$Whenever a creature you control without flying dies, return it to the battlefield under its owner's control with a flying counter on it.|
+Mythos of Snapdax|Ikoria: Lair of Behemoths|317|R|{2}{W}{W}|Sorcery|||Each player chooses an artifact, a creature, an enchantment, and a planeswalker from among the nonland permanents they control, then sacrifices the rest. If {B}{R} was spent to cast this spell, you choose the permanents for each player instead.|
+Mythos of Illuna|Ikoria: Lair of Behemoths|318|R|{2}{U}{U}|Sorcery|||Create a token that's a copy of target permanent. If {R}{G} was spent to cast this spell, instead create a token that's a copy of that permanent, except the token has "When this permanent enters the battlefield, if it's a creature, it fights up to one target creature you don't control."|
+Shark Typhoon|Ikoria: Lair of Behemoths|319|R|{5}{U}|Enchantment|||Whenever you cast a noncreature spell, create an X/X blue Shark creature token with flying, where X is that spell's converted mana cost.$Cycling {X}{1}{U}$When you cycle Shark Typhoon, create an X/X blue Shark creature token with flying.|
+Voracious Greatshark|Ikoria: Lair of Behemoths|320|R|{3}{U}{U}|Creature - Shark|5|4|Flash$When Voracious Greatshark enters the battlefield, counter target artifact or creature spell.|
+Extinction Event|Ikoria: Lair of Behemoths|321|R|{3}{B}|Sorcery|||Choose odd or even. Exile each creature with converted mana cost of the chosen value.|
+Hunted Nightmare|Ikoria: Lair of Behemoths|322|R|{1}{B}{B}|Creature - Nightmare|4|5|Menace$When Hunted Nightmare enters the battlefield, target opponent puts a deathtouch counter on a creature they control.|
+Mythos of Nethroi|Ikoria: Lair of Behemoths|323|R|{2}{B}|Instant|||Destroy target nonland permanent if it's a creature or if {G}{W} was spent to cast this spell.|
+Mythos of Vadrok|Ikoria: Lair of Behemoths|324|R|{2}{R}{R}|Sorcery|||Mythos of Vadrok deals 5 damage divided as you choose among any number of target creatures and/or planeswalkers. If {W}{U} was spent to cast this spell, until your next turn, those permanents can't attack or block and their activated abilities can't be activated.|
+Unpredictable Cyclone|Ikoria: Lair of Behemoths|325|R|{3}{R}{R}|Enchantment|||If a cycling ability of another nonland card would cause you to draw a card, instead exile cards from the top of your library until you exile a card that shares a card type with the cycled card. You may cast that card without paying its mana cost. Then put the exiled cards that weren't cast this way on the bottom of your library in a random order.$Cycling {2}|
+Yidaro, Wandering Monster|Ikoria: Lair of Behemoths|326|R|{5}{R}{R}|Legendary Creature - Dinosaur Turtle|8|8|Trample, haste$Cycling {1}{R}$When you cycle Yidaro, Wandering Monster, shuffle it into your library from your graveyard. If you've cycled a card named Yidaro, Wandering Monster four or more times this game, put it onto the battlefield from your graveyard instead.|
+Colossification|Ikoria: Lair of Behemoths|327|R|{5}{G}{G}|Enchantment - Aura|||Enchant creature$When Colossification enters the battlefield, tap enchanted creature.$Enchanted creature gets +20/+20.|
+Kogla, the Titan Ape|Ikoria: Lair of Behemoths|328|R|{3}{G}{G}{G}|Legendary Creature - Ape|7|6|When Kogla, the Titan Ape enters the battlefield, it fights up to one target creature you don't control.$Whenever Kogla attacks, destroy target artifact or enchantment defending player controls.${1}{G}: Return target Human you control to its owner's hand. Kogla gains indestructible until end of turn.|
+Mythos of Brokkos|Ikoria: Lair of Behemoths|329|R|{2}{G}{G}|Sorcery|||If {U}{B} was spent to cast Mythos of Brokkos, search your library for a card, put that card into your graveyard, then shuffle your library.$Return up to two permanent cards from your graveyard to your hand.|
+Chevill, Bane of Monsters|Ikoria: Lair of Behemoths|330|M|{B}{G}|Legendary Creature - Human Rogue|1|3|Deathtouch$At the beginning of your upkeep, if your opponents control no permanents with bounty counters on them, put a bounty counter on target creature or planeswalker an opponent controls.$Whenever a permanent an opponent controls with a bounty counter on it dies, you gain 3 life and draw a card.|
+Death's Oasis|Ikoria: Lair of Behemoths|331|R|{W}{B}{G}|Enchantment|||Whenever a nontoken creature you control dies, put the top two cards of your library into your graveyard. Then return a creature card with lesser converted mana cost than the creature that died from your graveyard to your hand.${1}, Sacrifice Death's Oasis: You gain life equal to the greatest converted mana cost among creatures you control.|
+Eerie Ultimatum|Ikoria: Lair of Behemoths|332|R|{W}{W}{B}{B}{B}{G}{G}|Sorcery|||Return any number of permanent cards with different names from your graveyard to the battlefield.|
+Emergent Ultimatum|Ikoria: Lair of Behemoths|333|R|{B}{B}{G}{G}{G}{U}{U}|Sorcery|||Search your library for up to three monocolored cards with different names and exile them. An opponent chooses one of those cards. Shuffle that card into your library. You may cast the other cards without paying their mana costs. Exile Emergent Ultimatum.|
+Frondland Felidar|Ikoria: Lair of Behemoths|334|R|{2}{G}{W}|Creature - Cat Beast|3|5|Vigilance$Creatures you control with vigilance have "{1}, {T}: Tap target creature."|
+General Kudro of Drannith|Ikoria: Lair of Behemoths|335|M|{1}{W}{B}|Legendary Creature - Human Soldier|3|3|Other Humans you control get +1/+1.$Whenever General Kudro of Drannith or another Human enters the battlefield under your control, exile target card from an opponent's graveyard.${2}, Sacrifice two Humans: Destroy target creature with power 4 or greater.|
+Genesis Ultimatum|Ikoria: Lair of Behemoths|336|R|{G}{G}{U}{U}{U}{R}{R}|Sorcery|||Look at the top five cards of your library. Put any number of permanent cards from among them onto the battlefield and the rest into your hand. Exile Genesis Ultimatum.|
+Inspired Ultimatum|Ikoria: Lair of Behemoths|337|R|{U}{U}{R}{R}{R}{W}{W}|Sorcery|||Target player gains 5 life, Inspired Ultimatum deals 5 damage to any target, then you draw five cards.|
+Kinnan, Bonder Prodigy|Ikoria: Lair of Behemoths|338|M|{G}{U}|Legendary Creature - Human Druid|2|2|Whenever you tap a nonland permanent for mana, add one mana of any type that permanent produced.${5}{G}{U}: Look at the top five cards of your library. You may put a non-Human creature card from among them onto the battlefield. Put the rest on the bottom of your library in a random order.|
+Labyrinth Raptor|Ikoria: Lair of Behemoths|339|R|{B}{R}|Creature - Nightmare Dinosaur|2|2|Menace$Whenever a creature you control with menace becomes blocked, defending player sacrifices a creature blocking it.${B}{R}: Creatures you control with menace get +1/+0 until end of turn.|
+Offspring's Revenge|Ikoria: Lair of Behemoths|340|R|{2}{R}{W}{B}|Enchantment|||At the beginning of combat on your turn, exile target red, white, or black creature card from your graveyard. Create a token that's a copy of that card, except it's 1/1. It gains haste until your next turn.|
+Quartzwood Crasher|Ikoria: Lair of Behemoths|341|R|{2}{R}{R}{G}|Creature - Dinosaur Beast|6|6|Trample$Whenever one or more creatures you control with trample deal combat damage to a player, create an X/X green Dinosaur Beast creature token with trample, where X is the amount of damage those creatures dealt to that player.|
+Rielle, the Everwise|Ikoria: Lair of Behemoths|342|M|{1}{U}{R}|Legendary Creature - Human Wizard|0|3|Rielle, the Everwise gets +1/+0 for each instant and sorcery card in your graveyard.$Whenever you discard one or more cards for the first time each turn, draw that many cards.|
+Ruinous Ultimatum|Ikoria: Lair of Behemoths|343|R|{R}{R}{W}{W}{W}{B}{B}|Sorcery|||Destroy all nonland permanents your opponents control.|
+Skycat Sovereign|Ikoria: Lair of Behemoths|344|R|{W}{U}|Creature - Elemental Cat|1|1|Flying$Skycat Sovereign gets +1/+1 for each other creature you control with flying.${2}{W}{U}: Create a 1/1 white Cat Bird creature token with flying.|
+Slitherwisp|Ikoria: Lair of Behemoths|345|R|{U}{B}{B}|Creature - Elemental Nightmare|3|2|Flash$Whenever you cast another spell that has flash, you draw a card and each opponent loses 1 life.|
+Song of Creation|Ikoria: Lair of Behemoths|346|R|{1}{G}{U}{R}|Enchantment|||You may play an additional land on each of your turns.$Whenever you cast a spell, draw two cards.$At the beginning of your end step, discard your hand.|
+Titans' Nest|Ikoria: Lair of Behemoths|347|R|{1}{B}{G}{U}|Enchantment|||At the beginning of your upkeep, look at the top card of your library. You may put that card into your graveyard.$Exile a card from your graveyard: Add {C}. Spend this mana only to cast a colored spell without {X} in its mana cost.|
+Whirlwind of Thought|Ikoria: Lair of Behemoths|348|R|{1}{U}{R}{W}|Enchantment|||Whenever you cast a noncreature spell, draw a card.|
+Winota, Joiner of Forces|Ikoria: Lair of Behemoths|349|M|{2}{R}{W}|Legendary Creature - Human Warrior|4|4|Whenever a non-Human creature you control attacks, look at the top six cards of your library. You may put a Human creature card from among them onto the battlefield tapped and attacking. It gains indestructible until end of turn. Put the rest of the cards on the bottom of your library in a random order.|
+Fiend Artisan|Ikoria: Lair of Behemoths|350|M|{B/G}{B/G}|Creature - Nightmare|1|1|Fiend Artisan gets +1/+1 for each creature card in your graveyard.${X}{B/G}, {T}, Sacrifice another creature: Search your library for a creature card with converted mana cost X or less, put it onto the battlefield, then shuffle your library. Activate this ability only any time you could cast a sorcery.|
+Gyruda, Doom of Depths|Ikoria: Lair of Behemoths|351|R|{4}{U/B}{U/B}|Legendary Creature - Demon Kraken|6|6|Companion — Your starting deck contains only cards with even converted mana costs.$When Gyruda, Doom of Depths enters the battlefield, each player puts the top four cards of the library into their graveyard. Put a creature card with an even converted mana cost from among those cards onto the battlefield under your control.|
+Jegantha, the Wellspring|Ikoria: Lair of Behemoths|352|R|{4}{R/G}|Legendary Creature - Elemental Elk|5|5|Companion — No card in your starting deck has more than one of the same mana symbol in its mana cost.${T}: Add {W}{U}{B}{R}{G}. This mana can't be spent to pay generic mana costs.|
+Kaheera, the Orphanguard|Ikoria: Lair of Behemoths|353|R|{1}{G/W}{G/W}|Legendary Creature - Cat Beast|3|2|Companion — Each creature card in your starting deck is a Cat, Elemental, Nightmare, Dinosaur, or Beast card.$Vigilance$Each other creature you control that's a Cat, Elemental, Nightmare, Dinosaur, or Beast gets +1/+1 and has vigilance.|
 Keruga, the Macrosage|Ikoria: Lair of Behemoths|354|R|{3}{G/U}{G/U}|Legendary Creature - Dinosaur Hippo|5|4|Companion — Your starting deck contains only cards with converted mana cost 3 or greater and land cards.$When Keruga, the Macrosage enters the battlefield, draw a card for each other permanent you control with converted mana cost 3 or greater.|
+Lurrus of the Dream-Den|Ikoria: Lair of Behemoths|355|R|{1}{W/B}{W/B}|Legendary Creature - Cat Nightmare|3|2|Companion — Each permanent card in your starting deck has converted mana cost 2 or less.$Lifelink$During each of your turns, you may cast one permanent spell with converted mana cost 2 or less from your graveyard.|
+Lutri, the Spellchaser|Ikoria: Lair of Behemoths|356|R|{1}{U/R}{U/R}|Legendary Creature - Elemental Otter|3|2|Companion — Each nonland card in your starting deck has a different name.$Flash$When Lutri, the Spellchaser enters the battlefield, if you cast it, copy target instant or sorcery spell you control. You may choose new targets for the copy.|
+Obosh, the Preypiercer|Ikoria: Lair of Behemoths|357|R|{3}{B/R}{B/R}|Legendary Creature - Hellion Horror|3|5|Companion — Your starting deck contains only cards with odd converted mana costs and land cards.$If a source you control with an odd converted mana cost would deal damage to a permanent or player, it deals double that damage to that permanent or player instead.|
+Umori, the Collector|Ikoria: Lair of Behemoths|358|R|{2}{B/G}{B/G}|Legendary Creature - Ooze|4|5|Companion — Each nonland card in your starting deck shares a card type.$As Umori, the Collector enters the battlefield, choose a card type.$Spells you cast of the chosen type cost {1} less to cast.|
+Yorion, Sky Nomad|Ikoria: Lair of Behemoths|359|R|{3}{W/U}{W/U}|Legendary Creature - Bird Serpent|4|5|Companion — Your starting deck contains at least twenty cards more than the minimum deck size.$Flying$When Yorion enters the battlefield, exile any number of other nonland permanents you own and control. Return those cards to the battlefield at the beginning of the next end step.|
+Zirda, the Dawnwaker|Ikoria: Lair of Behemoths|360|R|{1}{R/W}{R/W}|Legendary Creature - Elemental Fox|3|3|Companion — Each permanent card in your starting deck has an activated ability.$Abilities you activate that aren't mana abilities cost {2} less to activate. This effect can't reduce the mana in that cost to less than one mana.${1}, {T}: Target creature can't block this turn.|
+Crystalline Giant|Ikoria: Lair of Behemoths|361|R|{3}|Artifact Creature - Giant|3|3|At the beginning of combat on your turn, choose a kind of counter at random that Crystalline Giant doesn't have on it from among flying, first strike, deathtouch, hexproof, lifelink, menace, reach, trample, vigilance, or +1/+1. Put a counter of that kind on Crystalline Giant.|
+The Ozolith|Ikoria: Lair of Behemoths|362|R|{1}|Legendary Artifact|||Whenever a creature you control leaves the battlefield, if it had counters on it, put those counters on The Ozolith.$At the beginning of combat on your turn, if The Ozolith has counters on it, you may move all counters from The Ozolith onto target creature.|
+Bonders' Enclave|Ikoria: Lair of Behemoths|363|R||Land|||{T}: Add {C}.${3}, {T}: Draw a card. Activate this ability only if you control a creature with power 4 or greater.|
+Colossification|Ikoria: Lair of Behemoths|364|R|{5}{G}{G}|Enchantment - Aura|||Enchant creature$When Colossification enters the battlefield, tap enchanted creature.$Enchanted creature gets +20/+20.|
+Flourishing Fox|Ikoria: Lair of Behemoths|365|U|{W}|Creature - Fox|1|1|Whenever you cycle another card, put a +1/+1 counter on Flourishing Fox.$Cycling {1}|
+Heartless Act|Ikoria: Lair of Behemoths|366|U|{1}{B}|Instant|||Choose one —$• Destroy target creature with no counters on it.$• Remove up to three counters from target creature.|
+Forbidden Friendship|Ikoria: Lair of Behemoths|367|C|{1}{R}|Sorcery|||Create a 1/1 red Dinosaur creature token with haste and a 1/1 white Human Soldier creature token.|
+Migration Path|Ikoria: Lair of Behemoths|368|U|{3}{G}|Sorcery|||Search your library for up to two basic land cards, put them onto the battlefield tapped, then shuffle your library.$Cycling {2}|
+Sprite Dragon|Ikoria: Lair of Behemoths|369|U|{U}{R}|Creature - Faerie Dragon|1|1|Flying, haste$Whenever you cast a noncreature spell, put a +1/+1 counter on Sprite Dragon.|
+Huntmaster Liger|Ikoria: Lair of Behemoths|370|U|{3}{W}|Creature - Cat|3|4|Mutate {2}{W}$Whenever this creature mutates, other creatures you control get +X/+X until end of turn, where X is the number of times this creature has mutated.|
+Luminous Broodmoth|Ikoria: Lair of Behemoths|371|M|{2}{W}{W}|Creature - Insect|3|4|Flying$Whenever a creature you control without flying dies, return it to the battlefield under its owner's control with a flying counter on it.|
+Pollywog Symbiote|Ikoria: Lair of Behemoths|372|U|{1}{U}|Creature - Frog|1|3|Each creature spell you cast costs {1} less to cast if it has mutate.$Whenever you cast a creature spell, if it has mutate, draw a card, then discard a card.|
+Void Beckoner|Ikoria: Lair of Behemoths|373|U|{6}{B}{B}|Creature - Nightmare Horror|8|8|Deathtouch$Cycling {2}{B}$When you cycle Void Beckoner, put a deathtouch counter on target creature you control.|
+Everquill Phoenix|Ikoria: Lair of Behemoths|374|R|{2}{R}{R}|Creature - Phoenix|4|4|Mutate {3}{R}$Flying$Whenever this creature mutates, create a red artifact token named Feather with "{1}, Sacrifice Feather: Return target Phoenix card from your graveyard to the battlefield tapped."|
+Yidaro, Wandering Monster|Ikoria: Lair of Behemoths|375|R|{5}{R}{R}|Legendary Creature - Dinosaur Turtle|8|8|Trample, haste$Cycling {1}{R}$When you cycle Yidaro, Wandering Monster, shuffle it into your library from your graveyard. If you've cycled a card named Yidaro, Wandering Monster four or more times this game, put it onto the battlefield from your graveyard instead.|
+Gemrazer|Ikoria: Lair of Behemoths|376|R|{3}{G}|Creature - Beast|4|4|Mutate {1}{G}{G}$Reach, trample$Whenever this creature mutates, destroy target artifact or enchantment an opponent controls.|
+Titanoth Rex|Ikoria: Lair of Behemoths|377|U|{7}{G}{G}|Creature - Dinosaur Beast|11|11|Trample$Cycling {1}{G}$When you cycle Titanoth Rex, put a trample counter on target creature you control.|
+Brokkos, Apex of Forever|Ikoria: Lair of Behemoths|378|M|{2}{B}{G}{U}|Legendary Creature - Nightmare Beast Elemental|6|6|Mutate {2}{U/B}{G}{G}$Trample$You may cast Brokkos, Apex of Forever from your graveyard using its mutate ability.|
+Illuna, Apex of Wishes|Ikoria: Lair of Behemoths|379|M|{2}{G}{U}{R}|Legendary Creature - Beast Elemental Dinosaur|6|6|Mutate {3}{R/G}{U}{U}$Flying, trample$Whenever this creature mutates, exile cards from the top of your library until you exile a nonland permanent card. Put that card onto the battlefield or into your hand.|
+Nethroi, Apex of Death|Ikoria: Lair of Behemoths|380|M|{2}{W}{B}{G}|Legendary Creature - Cat Nightmare Beast|5|5|Mutate {4}{G/W}{B}{B}$Deathtouch, lifelink$Whenever this creature mutates, return any number of target creature cards with total power 10 or less from your graveyard to the battlefield.|
+Snapdax, Apex of the Hunt|Ikoria: Lair of Behemoths|381|M|{1}{R}{W}{B}|Legendary Creature - Dinosaur Cat Nightmare|3|5|Mutate {2}{B/R}{W}{W}$Double Strike$Whenever this creature mutates, it deals 4 damage to target creature or planeswalker an opponent controls and you gain 4 life.|
+Sprite Dragon|Ikoria: Lair of Behemoths|382|U|{U}{R}|Creature - Faerie Dragon|1|1|Flying, haste$Whenever you cast a noncreature spell, put a +1/+1 counter on Sprite Dragon.|
+Vadrok, Apex of Thunder|Ikoria: Lair of Behemoths|383|M|{U}{R}{W}|Legendary Creature - Elemental Dinosaur Cat|3|3|Mutate {1}{W/U}{R}{R}$Flying, first strike$Whenever this creature mutates, you may cast target noncreature card with converted mana cost 3 or less from your graveyard without paying its mana cost.|
+Gyruda, Doom of Depths|Ikoria: Lair of Behemoths|384|R|{4}{U/B}{U/B}|Legendary Creature - Demon Kraken|6|6|Companion — Your starting deck contains only cards with even converted mana costs.$When Gyruda, Doom of Depths enters the battlefield, each player puts the top four cards of the library into their graveyard. Put a creature card with an even converted mana cost from among those cards onto the battlefield under your control.|
+Mysterious Egg|Ikoria: Lair of Behemoths|385|C|{1}|Creature - Egg|0|2|Whenever this creature mutates, put a +1/+1 counter on it.|
+Dirge Bat|Ikoria: Lair of Behemoths|386|R|{2}{B}{B}|Creature - Bat|3|3|Mutate {4}{B}{B}$Flash$Flying$Whenever this creature mutates, destroy target creature or planeswalker an opponent controls.|
+Crystalline Giant|Ikoria: Lair of Behemoths|387|R|{3}|Artifact Creature - Giant|3|3|At the beginning of combat on your turn, choose a kind of counter at random that Crystalline Giant doesn't have on it from among flying, first strike, deathtouch, hexproof, lifelink, menace, reach, trample, vigilance, or +1/+1. Put a counter of that kind on Crystalline Giant.|

From 42fd5a64107e73814baaed728a1f319b6eb4abe7 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Fri, 15 May 2020 20:47:07 -0400
Subject: [PATCH 037/586] fixed Crosis, the Purger's discard effect

---
 Mage.Sets/src/mage/cards/c/CrosisThePurger.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Mage.Sets/src/mage/cards/c/CrosisThePurger.java b/Mage.Sets/src/mage/cards/c/CrosisThePurger.java
index 3646ea742d..02afdcfbcc 100644
--- a/Mage.Sets/src/mage/cards/c/CrosisThePurger.java
+++ b/Mage.Sets/src/mage/cards/c/CrosisThePurger.java
@@ -85,7 +85,7 @@ class CrosisThePurgerEffect extends OneShotEffect {
         }
         damagedPlayer.revealCards("hand of " + damagedPlayer.getName(), damagedPlayer.getHand(), game);
         Cards cards = new CardsImpl(
-                player.getHand()
+                damagedPlayer.getHand()
                         .getCards(game)
                         .stream()
                         .filter(card -> card.getColor(game).shares(choice.getColor()))

From 713a7ab35dc58966df02f0aa0584719b4e68757b Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Fri, 15 May 2020 20:51:42 -0400
Subject: [PATCH 038/586] fixed a null pointer exception when multiple players
 discard

---
 .../effects/common/discard/DiscardEachPlayerEffect.java      | 3 +--
 Mage/src/main/java/mage/players/PlayerImpl.java              | 5 ++++-
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardEachPlayerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardEachPlayerEffect.java
index 92bf387644..7fee7da644 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardEachPlayerEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardEachPlayerEffect.java
@@ -103,8 +103,7 @@ public class DiscardEachPlayerEffect extends OneShotEffect {
             if (player == null) {
                 continue;
             }
-            Cards cardsPlayer = cardsToDiscard.get(playerId);
-            player.discard(cardsPlayer, source, game);
+            player.discard(cardsToDiscard.get(playerId), source, game);
         }
         return true;
     }
diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java
index f7ff8422b1..305199185a 100644
--- a/Mage/src/main/java/mage/players/PlayerImpl.java
+++ b/Mage/src/main/java/mage/players/PlayerImpl.java
@@ -721,6 +721,9 @@ public abstract class PlayerImpl implements Player, Serializable {
     @Override
     public Cards discard(Cards cards, Ability source, Game game) {
         Cards discardedCards = new CardsImpl();
+        if (cards == null) {
+            return discardedCards;
+        }
         for (Card card : cards.getCards(game)) {
             if (doDiscard(card, source, game, false)) {
                 discardedCards.add(card);
@@ -4005,7 +4008,7 @@ public abstract class PlayerImpl implements Player, Serializable {
                 for (Card card : cards) {
                     if (card instanceof Permanent) {
                         game.getBattlefield().removePermanent(card.getId());
-                        ZoneChangeEvent event = new ZoneChangeEvent((Permanent)card,
+                        ZoneChangeEvent event = new ZoneChangeEvent((Permanent) card,
                                 (source == null ? null : source.getSourceId()),
                                 byOwner ? card.getOwnerId() : getId(), Zone.BATTLEFIELD, Zone.OUTSIDE, appliedEffects);
                         game.fireEvent(event);

From c24851b4a98ab07913e41e620952cb57a63cca56 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Fri, 15 May 2020 21:19:04 -0400
Subject: [PATCH 039/586] fixed a bunch of discard issues (#6532)

---
 Mage.Sets/src/mage/cards/b/BogDown.java       |  4 +-
 .../src/mage/cards/c/CabalTherapist.java      |  2 +-
 .../src/mage/cards/g/GreelMindRaker.java      | 16 ++--
 .../src/mage/cards/i/InsidiousDreams.java     |  2 +-
 Mage.Sets/src/mage/cards/k/KaerveksSpite.java | 18 +++--
 Mage.Sets/src/mage/cards/m/MidnightOil.java   | 77 ++++++-------------
 Mage.Sets/src/mage/cards/n/NogginWhack.java   |  2 +-
 Mage.Sets/src/mage/cards/p/Persecute.java     | 37 ++++-----
 Mage.Sets/src/mage/cards/p/Probe.java         |  2 +-
 Mage.Sets/src/mage/cards/p/PsychicVortex.java |  2 +-
 .../src/mage/cards/s/SickeningDreams.java     |  2 +-
 Mage.Sets/src/mage/cards/t/ThoughtGorger.java | 40 +++++-----
 .../DrawCardSourceControllerEffect.java       |  1 -
 13 files changed, 89 insertions(+), 116 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/b/BogDown.java b/Mage.Sets/src/mage/cards/b/BogDown.java
index b136f2e606..cf00d5babd 100644
--- a/Mage.Sets/src/mage/cards/b/BogDown.java
+++ b/Mage.Sets/src/mage/cards/b/BogDown.java
@@ -8,7 +8,7 @@ import mage.abilities.keyword.KickerAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.filter.common.FilterControlledLandPermanent;
+import mage.filter.StaticFilters;
 import mage.target.TargetPlayer;
 import mage.target.common.TargetControlledPermanent;
 
@@ -24,7 +24,7 @@ public final class BogDown extends CardImpl {
 
         // Kicker-Sacrifice two lands.
         this.addAbility(new KickerAbility(new SacrificeTargetCost(new TargetControlledPermanent(2, 2,
-                new FilterControlledLandPermanent("two lands"), true))));
+                StaticFilters.FILTER_CONTROLLED_LAND_SHORT_TEXT, true))));
 
         // Target player discards two cards. If Bog Down was kicked, that player discards three cards instead.
         this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new DiscardTargetEffect(3),
diff --git a/Mage.Sets/src/mage/cards/c/CabalTherapist.java b/Mage.Sets/src/mage/cards/c/CabalTherapist.java
index 56ccea0d38..53d044bd86 100644
--- a/Mage.Sets/src/mage/cards/c/CabalTherapist.java
+++ b/Mage.Sets/src/mage/cards/c/CabalTherapist.java
@@ -43,7 +43,7 @@ public final class CabalTherapist extends CardImpl {
         ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility(
                 new ChooseACardNameEffect(ChooseACardNameEffect.TypeOfName.NON_LAND_NAME),
                 false, "choose a nonland card name, then target player " +
-                "reveals their hand and discards all cards with that name."
+                "reveals their hand and discards all cards with that name"
         );
         ability.addEffect(new CabalTherapistDiscardEffect());
         ability.addTarget(new TargetPlayer());
diff --git a/Mage.Sets/src/mage/cards/g/GreelMindRaker.java b/Mage.Sets/src/mage/cards/g/GreelMindRaker.java
index bc79a60e01..d615d2a683 100644
--- a/Mage.Sets/src/mage/cards/g/GreelMindRaker.java
+++ b/Mage.Sets/src/mage/cards/g/GreelMindRaker.java
@@ -1,7 +1,5 @@
-
 package mage.cards.g;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleActivatedAbility;
@@ -15,17 +13,19 @@ import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.SubType;
 import mage.constants.SuperType;
-import mage.constants.Zone;
 import mage.filter.FilterCard;
 import mage.target.TargetPlayer;
 import mage.target.common.TargetCardInHand;
 
+import java.util.UUID;
+
 /**
- *
  * @author Styxo
  */
 public final class GreelMindRaker extends CardImpl {
 
+    private static final FilterCard filter = new FilterCard("two cards");
+
     public GreelMindRaker(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}");
 
@@ -36,14 +36,16 @@ public final class GreelMindRaker extends CardImpl {
         this.toughness = new MageInt(3);
 
         // {X}{B}, {tap}, Discard two cards: Target player discards X cards at random.
-        Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DiscardTargetEffect(ManacostVariableValue.instance, true), new ManaCostsImpl("{X}{B}"));
+        Ability ability = new SimpleActivatedAbility(new DiscardTargetEffect(
+                ManacostVariableValue.instance, true
+        ), new ManaCostsImpl("{X}{B}"));
         ability.addCost(new TapSourceCost());
-        ability.addCost(new DiscardTargetCost(new TargetCardInHand(2, new FilterCard())));
+        ability.addCost(new DiscardTargetCost(new TargetCardInHand(2, filter)));
         ability.addTarget(new TargetPlayer());
         this.addAbility(ability);
     }
 
-    public GreelMindRaker(final GreelMindRaker card) {
+    private GreelMindRaker(final GreelMindRaker card) {
         super(card);
     }
 
diff --git a/Mage.Sets/src/mage/cards/i/InsidiousDreams.java b/Mage.Sets/src/mage/cards/i/InsidiousDreams.java
index fecfde77a6..bdd0e66400 100644
--- a/Mage.Sets/src/mage/cards/i/InsidiousDreams.java
+++ b/Mage.Sets/src/mage/cards/i/InsidiousDreams.java
@@ -113,7 +113,7 @@ class InsidiousDreamsAdditionalCost extends VariableCostImpl {
 
     InsidiousDreamsAdditionalCost() {
         super("cards to discard");
-        this.text = "as an additional cost to cast this spell, discard X cards";
+        this.text = "discard X cards";
     }
 
     InsidiousDreamsAdditionalCost(final InsidiousDreamsAdditionalCost cost) {
diff --git a/Mage.Sets/src/mage/cards/k/KaerveksSpite.java b/Mage.Sets/src/mage/cards/k/KaerveksSpite.java
index f38414c79f..fe2406d23e 100644
--- a/Mage.Sets/src/mage/cards/k/KaerveksSpite.java
+++ b/Mage.Sets/src/mage/cards/k/KaerveksSpite.java
@@ -1,30 +1,34 @@
 package mage.cards.k;
 
-import java.util.UUID;
 import mage.abilities.costs.common.DiscardHandCost;
 import mage.abilities.costs.common.SacrificeAllCost;
-import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.LoseLifeTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
+import mage.filter.FilterPermanent;
 import mage.filter.common.FilterControlledPermanent;
+import mage.target.TargetPlayer;
+
+import java.util.UUID;
 
 public final class KaerveksSpite extends CardImpl {
 
+    private static final FilterPermanent filter = new FilterControlledPermanent("permanents you control");
+
     public KaerveksSpite(UUID ownerId, CardSetInfo cardSetInfo) {
         super(ownerId, cardSetInfo, new CardType[]{CardType.INSTANT}, "{B}{B}{B}");
 
         // As an additional cost to cast Kaervek's Spite, sacrifice all permanents you control and discard your hand.
-        this.getSpellAbility().addCost(new SacrificeAllCost(new FilterControlledPermanent("permanents you control")));
+        this.getSpellAbility().addCost(new SacrificeAllCost(filter));
         this.getSpellAbility().addCost(new DiscardHandCost());
 
-        // Target player loses 5 life.
-        Effect effect = new LoseLifeTargetEffect(5);
-        this.getSpellAbility().addEffect(effect);
+        // Target player loses 5 life.=
+        this.getSpellAbility().addEffect(new LoseLifeTargetEffect(5));
+        this.getSpellAbility().addTarget(new TargetPlayer());
     }
 
-    public KaerveksSpite(final KaerveksSpite other) {
+    private KaerveksSpite(final KaerveksSpite other) {
         super(other);
     }
 
diff --git a/Mage.Sets/src/mage/cards/m/MidnightOil.java b/Mage.Sets/src/mage/cards/m/MidnightOil.java
index 954d340896..97280100ae 100644
--- a/Mage.Sets/src/mage/cards/m/MidnightOil.java
+++ b/Mage.Sets/src/mage/cards/m/MidnightOil.java
@@ -1,14 +1,11 @@
-
 package mage.cards.m;
 
-import java.util.UUID;
 import mage.abilities.Ability;
-import mage.abilities.TriggeredAbilityImpl;
 import mage.abilities.common.BeginningOfDrawTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.dynamicvalue.common.CountersSourceCount;
-import mage.abilities.effects.Effect;
+import mage.abilities.effects.common.DiscardCardControllerTriggeredAbility;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.abilities.effects.common.LoseLifeSourceControllerEffect;
 import mage.abilities.effects.common.continuous.MaximumHandSizeControllerEffect;
@@ -20,42 +17,48 @@ import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.Duration;
 import mage.constants.TargetController;
-import mage.constants.Zone;
-import mage.counters.Counter;
 import mage.counters.CounterType;
-import mage.game.Game;
-import mage.game.events.GameEvent;
-import mage.game.events.GameEvent.EventType;
+
+import java.util.UUID;
 
 /**
- *
  * @author LevelX2
  */
 public final class MidnightOil extends CardImpl {
 
     public MidnightOil(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{B}{B}");
+        super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}{B}");
 
         // Midnight Oil enters the battlefield with seven hour counters on it.
-        this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(new Counter(CounterType.HOUR.createInstance(7))),
-                "with seven hour counters on it"));
+        this.addAbility(new EntersBattlefieldAbility(
+                new AddCountersSourceEffect(
+                        CounterType.HOUR.createInstance(7)
+                ), "with seven hour counters on it"
+        ));
 
         // At the beginning of your draw step, draw an additional card and remove two hour counters from Midnight Oil.
-        Ability ability = new BeginningOfDrawTriggeredAbility(new DrawCardSourceControllerEffect(1), TargetController.YOU, false);
-        Effect effect = new RemoveCounterSourceEffect(CounterType.HOUR.createInstance(2));
-        effect.setText("and remove two hour counters from {this}");
-        ability.addEffect(effect);
+        Ability ability = new BeginningOfDrawTriggeredAbility(
+                new DrawCardSourceControllerEffect(1),
+                TargetController.YOU, false
+        );
+        ability.addEffect(new RemoveCounterSourceEffect(
+                CounterType.HOUR.createInstance(2)
+        ).concatBy("and"));
         this.addAbility(ability);
 
         // Your maximum hand size is equal to the number of hour counters on Midnight Oil.
-        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new MaximumHandSizeControllerEffect(new CountersSourceCount(CounterType.HOUR), Duration.WhileOnBattlefield, HandSizeModification.SET, TargetController.YOU)));
+        this.addAbility(new SimpleStaticAbility(new MaximumHandSizeControllerEffect(
+                new CountersSourceCount(CounterType.HOUR), Duration.WhileOnBattlefield,
+                HandSizeModification.SET, TargetController.YOU
+        ).setText("your maximum hand size is equal to the number of hour counters on {this}")));
 
         // Whenever you discard a card, you lose 1 life.
-        this.addAbility(new MidnightOilTriggeredAbility(new LoseLifeSourceControllerEffect(1)));
-
+        this.addAbility(new DiscardCardControllerTriggeredAbility(
+                new LoseLifeSourceControllerEffect(1), false
+        ));
     }
 
-    public MidnightOil(final MidnightOil card) {
+    private MidnightOil(final MidnightOil card) {
         super(card);
     }
 
@@ -64,35 +67,3 @@ public final class MidnightOil extends CardImpl {
         return new MidnightOil(this);
     }
 }
-
-class MidnightOilTriggeredAbility extends TriggeredAbilityImpl {
-
-    MidnightOilTriggeredAbility(Effect effect) {
-        super(Zone.BATTLEFIELD, effect, false);
-    }
-
-    MidnightOilTriggeredAbility(final MidnightOilTriggeredAbility ability) {
-        super(ability);
-    }
-
-    @Override
-    public MidnightOilTriggeredAbility copy() {
-        return new MidnightOilTriggeredAbility(this);
-    }
-
-    @Override
-    public boolean checkEventType(GameEvent event, Game game) {
-        return event.getType() == EventType.DISCARDED_CARD;
-    }
-
-    @Override
-    public boolean checkTrigger(GameEvent event, Game game) {
-        return isControlledBy(event.getPlayerId());
-
-    }
-
-    @Override
-    public String getRule() {
-        return "Whenever you discard a card, " + super.getRule();
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/n/NogginWhack.java b/Mage.Sets/src/mage/cards/n/NogginWhack.java
index 3eabce33e6..f7627b5526 100644
--- a/Mage.Sets/src/mage/cards/n/NogginWhack.java
+++ b/Mage.Sets/src/mage/cards/n/NogginWhack.java
@@ -93,7 +93,7 @@ class NogginWhackEffect extends OneShotEffect {
         if (!revealedCards.isEmpty()) {
             targetPlayer.revealCards(source, revealedCards, game);
             controller.chooseTarget(Outcome.Exile, revealedCards, targetInHand, source, game);
-            targetPlayer.discard(new CardsImpl(target.getTargets()), source, game);
+            targetPlayer.discard(new CardsImpl(targetInHand.getTargets()), source, game);
         }
         return true;
     }
diff --git a/Mage.Sets/src/mage/cards/p/Persecute.java b/Mage.Sets/src/mage/cards/p/Persecute.java
index 606121cf23..b5cad0fddb 100644
--- a/Mage.Sets/src/mage/cards/p/Persecute.java
+++ b/Mage.Sets/src/mage/cards/p/Persecute.java
@@ -1,24 +1,23 @@
-
 package mage.cards.p;
 
-import java.util.Set;
-import java.util.UUID;
 import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.cards.Cards;
+import mage.cards.CardsImpl;
 import mage.choices.ChoiceColor;
 import mage.constants.CardType;
 import mage.constants.Outcome;
+import mage.filter.FilterCard;
+import mage.filter.predicate.mageobject.ColorPredicate;
 import mage.game.Game;
 import mage.players.Player;
 import mage.target.TargetPlayer;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class Persecute extends CardImpl {
@@ -29,7 +28,6 @@ public final class Persecute extends CardImpl {
         // Choose a color. Target player reveals their hand and discards all cards of that color.
         this.getSpellAbility().addEffect(new PersecuteEffect());
         this.getSpellAbility().addTarget(new TargetPlayer());
-
     }
 
     public Persecute(final Persecute card) {
@@ -44,12 +42,12 @@ public final class Persecute extends CardImpl {
 
 class PersecuteEffect extends OneShotEffect {
 
-    public PersecuteEffect() {
+    PersecuteEffect() {
         super(Outcome.Discard);
         this.staticText = "Choose a color. Target player reveals their hand and discards all cards of that color";
     }
 
-    public PersecuteEffect(final PersecuteEffect effect) {
+    private PersecuteEffect(final PersecuteEffect effect) {
         super(effect);
     }
 
@@ -64,17 +62,16 @@ class PersecuteEffect extends OneShotEffect {
         MageObject sourceObject = game.getObject(source.getSourceId());
         Player targetPlayer = game.getPlayer(getTargetPointer().getFirst(game, source));
         ChoiceColor choice = new ChoiceColor();
-        if (controller != null && sourceObject != null && targetPlayer != null && controller.choose(outcome, choice, game)) {
-            Cards hand = targetPlayer.getHand();
-            targetPlayer.revealCards(sourceObject.getIdName(), hand, game);
-            Set<Card> cards = hand.getCards(game);
-            for (Card card : cards) {
-                if (card != null && card.getColor(game).shares(choice.getColor())) {
-                    targetPlayer.discard(card, source, game);
-                }
-            }
-            return true;
+        if (controller == null
+                || sourceObject == null
+                || targetPlayer == null
+                || !controller.choose(outcome, choice, game)) {
+            return false;
         }
-        return false;
+        FilterCard filterCard = new FilterCard();
+        filterCard.add(new ColorPredicate(choice.getColor()));
+        targetPlayer.revealCards(source, targetPlayer.getHand(), game);
+        targetPlayer.discard(new CardsImpl(targetPlayer.getHand().getCards(filterCard, game)), source, game);
+        return true;
     }
 }
diff --git a/Mage.Sets/src/mage/cards/p/Probe.java b/Mage.Sets/src/mage/cards/p/Probe.java
index 6c98d48e93..4a80a78d85 100644
--- a/Mage.Sets/src/mage/cards/p/Probe.java
+++ b/Mage.Sets/src/mage/cards/p/Probe.java
@@ -32,7 +32,7 @@ public final class Probe extends CardImpl {
         this.getSpellAbility().addEffect(new ConditionalOneShotEffect(
                 new DiscardTargetEffect(2),
                 KickedCondition.instance,
-                "<br><br>if this spell was kicked, target player discards two cards"));
+                "<br><br>If this spell was kicked, target player discards two cards"));
         this.getSpellAbility().setTargetAdjuster(ProbeAdjuster.instance);
     }
 
diff --git a/Mage.Sets/src/mage/cards/p/PsychicVortex.java b/Mage.Sets/src/mage/cards/p/PsychicVortex.java
index 91789ec2cc..87b86d7e5b 100644
--- a/Mage.Sets/src/mage/cards/p/PsychicVortex.java
+++ b/Mage.Sets/src/mage/cards/p/PsychicVortex.java
@@ -55,7 +55,7 @@ public final class PsychicVortex extends CardImpl {
 class PsychicVortexCost extends CostImpl {
     
     PsychicVortexCost() {
-        this.text = "Draw a card.";
+        this.text = "Draw a card";
     }
 
     @Override
diff --git a/Mage.Sets/src/mage/cards/s/SickeningDreams.java b/Mage.Sets/src/mage/cards/s/SickeningDreams.java
index 3a74c9e7ee..ac2d709f11 100644
--- a/Mage.Sets/src/mage/cards/s/SickeningDreams.java
+++ b/Mage.Sets/src/mage/cards/s/SickeningDreams.java
@@ -47,7 +47,7 @@ class SickeningDreamsAdditionalCost extends VariableCostImpl {
 
     SickeningDreamsAdditionalCost() {
         super("cards to discard");
-        this.text = "as an additional cost to cast this spell, discard X cards";
+        this.text = "discard X cards";
     }
 
     SickeningDreamsAdditionalCost(final SickeningDreamsAdditionalCost cost) {
diff --git a/Mage.Sets/src/mage/cards/t/ThoughtGorger.java b/Mage.Sets/src/mage/cards/t/ThoughtGorger.java
index 117beb14e2..f20363f2bb 100644
--- a/Mage.Sets/src/mage/cards/t/ThoughtGorger.java
+++ b/Mage.Sets/src/mage/cards/t/ThoughtGorger.java
@@ -1,7 +1,5 @@
-
 package mage.cards.t;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
@@ -11,22 +9,23 @@ import mage.abilities.keyword.TrampleAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.SubType;
 import mage.constants.Outcome;
+import mage.constants.SubType;
 import mage.constants.Zone;
 import mage.counters.CounterType;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
 import mage.players.Player;
 
+import java.util.UUID;
+
 /**
- *
  * @author jeffwadsworth
  */
 public final class ThoughtGorger extends CardImpl {
 
     public ThoughtGorger(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{B}{B}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{B}");
         this.subtype.add(SubType.HORROR);
 
         this.power = new MageInt(2);
@@ -35,15 +34,13 @@ public final class ThoughtGorger extends CardImpl {
         this.addAbility(TrampleAbility.getInstance());
 
         // When Thought Gorger enters the battlefield, put a +1/+1 counter on it for each card in your hand. If you do, discard your hand.
-        Ability ability1 = new EntersBattlefieldTriggeredAbility(new ThoughtGorgerEffectEnters());
-        this.addAbility(ability1);
+        this.addAbility(new EntersBattlefieldTriggeredAbility(new ThoughtGorgerEffectEnters()));
 
         // When Thought Gorger leaves the battlefield, draw a card for each +1/+1 counter on it.
-        Ability ability2 = new LeavesBattlefieldTriggeredAbility(new ThoughtGorgerEffectLeaves(), false);
-        this.addAbility(ability2);
+        this.addAbility(new LeavesBattlefieldTriggeredAbility(new ThoughtGorgerEffectLeaves(), false));
     }
 
-    public ThoughtGorger(final ThoughtGorger card) {
+    private ThoughtGorger(final ThoughtGorger card) {
         super(card);
     }
 
@@ -56,12 +53,12 @@ public final class ThoughtGorger extends CardImpl {
 
 class ThoughtGorgerEffectEnters extends OneShotEffect {
 
-    public ThoughtGorgerEffectEnters() {
+    ThoughtGorgerEffectEnters() {
         super(Outcome.Benefit);
         this.staticText = "put a +1/+1 counter on it for each card in your hand. If you do, discard your hand.";
     }
 
-    public ThoughtGorgerEffectEnters(final ThoughtGorgerEffectEnters effect) {
+    private ThoughtGorgerEffectEnters(final ThoughtGorgerEffectEnters effect) {
         super(effect);
     }
 
@@ -74,24 +71,27 @@ class ThoughtGorgerEffectEnters extends OneShotEffect {
     public boolean apply(Game game, Ability source) {
         Player player = game.getPlayer(source.getControllerId());
         Permanent thoughtGorger = game.getPermanent(source.getSourceId());
-        if (player != null && !player.getHand().isEmpty() && thoughtGorger != null ) {
-            int cardsInHand = player.getHand().size();
-            thoughtGorger.addCounters(CounterType.P1P1.createInstance(cardsInHand), source, game);
-            player.discard(cardsInHand, false, source, game);
-            return true;
+        if (player == null
+                || player.getHand().isEmpty()
+                || thoughtGorger == null
+                || !thoughtGorger.addCounters(
+                CounterType.P1P1.createInstance(player.getHand().size()), source, game
+        )) {
+            return false;
         }
-        return false;
+        player.discard(player.getHand(), source, game);
+        return true;
     }
 }
 
 class ThoughtGorgerEffectLeaves extends OneShotEffect {
 
-    public ThoughtGorgerEffectLeaves() {
+    ThoughtGorgerEffectLeaves() {
         super(Outcome.Neutral);
         this.staticText = "draw a card for each +1/+1 counter on it.";
     }
 
-    public ThoughtGorgerEffectLeaves(final ThoughtGorgerEffectLeaves effect) {
+    private ThoughtGorgerEffectLeaves(final ThoughtGorgerEffectLeaves effect) {
         super(effect);
     }
 
diff --git a/Mage/src/main/java/mage/abilities/effects/common/DrawCardSourceControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DrawCardSourceControllerEffect.java
index 978826b327..7dd857656f 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/DrawCardSourceControllerEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/DrawCardSourceControllerEffect.java
@@ -41,7 +41,6 @@ public class DrawCardSourceControllerEffect extends OneShotEffect {
         super(effect);
         this.amount = effect.amount.copy();
         this.whoDrawCard = effect.whoDrawCard;
-        setText();
     }
 
     @Override

From 72d9c736c971644c4b03de17d00975015236f986 Mon Sep 17 00:00:00 2001
From: johnm <johnm@WINDOWS-QR5QIIL.lan>
Date: Sat, 16 May 2020 20:49:14 +0100
Subject: [PATCH 040/586] Fix so Nacatl's attack trigger works correctly when a
 creature has copied Nacatl

---
 .../src/mage/cards/n/NacatlWarPride.java      | 27 ++++++++-----------
 1 file changed, 11 insertions(+), 16 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/n/NacatlWarPride.java b/Mage.Sets/src/mage/cards/n/NacatlWarPride.java
index 0bf4c1f9a2..06affdcc5f 100644
--- a/Mage.Sets/src/mage/cards/n/NacatlWarPride.java
+++ b/Mage.Sets/src/mage/cards/n/NacatlWarPride.java
@@ -23,9 +23,10 @@ import mage.constants.Zone;
 import mage.filter.common.FilterControlledCreaturePermanent;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
-import mage.game.permanent.token.EmptyToken;
 import mage.target.targetpointer.FixedTargets;
-import mage.util.CardUtil;
+import mage.abilities.effects.common.CreateTokenCopyTargetEffect;
+import mage.players.Player;
+import mage.target.targetpointer.FixedTarget;
 
 /**
  *
@@ -92,20 +93,14 @@ class NacatlWarPrideEffect extends OneShotEffect {
             return false;
         }
 
-        List<Permanent> copies = new ArrayList<>();
-        for (int i = 0; i < count; i++) {
-            EmptyToken token = new EmptyToken();
-            CardUtil.copyTo(token).from(origNactalWarPride);
-            token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId(), true, true);
-
-            for (UUID tokenId : token.getLastAddedTokenIds()) { // by cards like Doubling Season multiple tokens can be added to the battlefield
-                Permanent tokenPermanent = game.getPermanent(tokenId);
-                if (tokenPermanent != null) {
-                    copies.add(tokenPermanent);
-                }
-            }
-        }
-
+        List<Permanent> copies = new ArrayList<>();    
+        
+        Player controller = game.getPlayer(source.getControllerId());
+        CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(controller.getId(), null, false, count, true, true);
+        effect.setTargetPointer(new FixedTarget(origNactalWarPride, game));
+        effect.apply(game, source);
+        copies.addAll(effect.getAddedPermanent());
+        
         if (!copies.isEmpty()) {
             FixedTargets fixedTargets = new FixedTargets(copies, game);
             ExileTargetEffect exileEffect = new ExileTargetEffect();

From 3c42207488e4ba422aa6b69ce314d9dfc236c623 Mon Sep 17 00:00:00 2001
From: johnm <johnm@WINDOWS-QR5QIIL.lan>
Date: Sat, 16 May 2020 20:52:38 +0100
Subject: [PATCH 041/586] Fix so Nacatl's attack trigger works correctly when a
 creature has copied Nacatl

---
 Mage.Sets/src/mage/cards/n/NacatlWarPride.java | 1 -
 1 file changed, 1 deletion(-)

diff --git a/Mage.Sets/src/mage/cards/n/NacatlWarPride.java b/Mage.Sets/src/mage/cards/n/NacatlWarPride.java
index 06affdcc5f..3bc52a35f6 100644
--- a/Mage.Sets/src/mage/cards/n/NacatlWarPride.java
+++ b/Mage.Sets/src/mage/cards/n/NacatlWarPride.java
@@ -94,7 +94,6 @@ class NacatlWarPrideEffect extends OneShotEffect {
         }
 
         List<Permanent> copies = new ArrayList<>();    
-        
         Player controller = game.getPlayer(source.getControllerId());
         CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(controller.getId(), null, false, count, true, true);
         effect.setTargetPointer(new FixedTarget(origNactalWarPride, game));

From 937b7f72ee3736b15f6cb84f1a9dab7d3763c9f0 Mon Sep 17 00:00:00 2001
From: johnm <johnm@WINDOWS-QR5QIIL.lan>
Date: Sun, 17 May 2020 14:35:41 +0100
Subject: [PATCH 042/586] Default range of influence is now all players rather
 than one.

---
 .../src/main/java/mage/client/dialog/NewTableDialog.java        | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java
index ea34892d98..a905c84926 100644
--- a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java
+++ b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java
@@ -890,7 +890,7 @@ public class NewTableDialog extends MageDialog {
         this.spnFreeMulligans.setValue(Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_NUMBER_OF_FREE_MULLIGANS + versionStr, "0")));
         this.cbMulligan.setSelectedItem(MulliganType.valueByName(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_MULLIGAN_TYPE + versionStr, MulliganType.GAME_DEFAULT.toString())));
 
-        int range = Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_RANGE + versionStr, "1"));
+        int range = Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_RANGE + versionStr, "0"));
         for (RangeOfInfluence roi : RangeOfInfluence.values()) {
             if (roi.getRange() == range) {
                 this.cbRange.setSelectedItem(roi);

From 0c2e08f54ee9cd3552e09be69ed55d9a336842c1 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Mon, 18 May 2020 06:46:39 +0400
Subject: [PATCH 043/586] * UI: choose spell to cast dialog - added card name
 for spell abilities (split, adventure, additional spell, etc, see #6549);

---
 .../java/mage/view/AbilityPickerView.java     | 22 ++++-
 .../test/serverside/AbilityPickerTest.java    | 87 +++++++++++++++++++
 2 files changed, 106 insertions(+), 3 deletions(-)
 create mode 100644 Mage.Tests/src/test/java/org/mage/test/serverside/AbilityPickerTest.java

diff --git a/Mage.Common/src/main/java/mage/view/AbilityPickerView.java b/Mage.Common/src/main/java/mage/view/AbilityPickerView.java
index 8ea265aee0..7cb356d20b 100644
--- a/Mage.Common/src/main/java/mage/view/AbilityPickerView.java
+++ b/Mage.Common/src/main/java/mage/view/AbilityPickerView.java
@@ -1,6 +1,7 @@
 package mage.view;
 
 import mage.abilities.Ability;
+import mage.abilities.SpellAbility;
 
 import java.io.Serializable;
 import java.util.LinkedHashMap;
@@ -28,15 +29,30 @@ public class AbilityPickerView implements Serializable {
             if (objectName == null) {
                 rule = ability.getRule(true);
             } else {
-                rule = ability.getRule(objectName);
-                if (rule.isEmpty()) {
-                    rule = ability.toString();
+                // spell abilities must start with "Cast name" (split cards have different names for each spell part)
+                if (ability instanceof SpellAbility) {
+                    SpellAbility spell = (SpellAbility) ability;
+                    rule = getAbilityRules(spell, spell.getCardName());
+                    if (!rule.startsWith("Cast ")) {
+                        rule = spell.toString() + ": " + rule; // spell.toString() must return this.name (example: Cast Armed)
+                    }
+                } else {
+                    rule = getAbilityRules(ability, objectName);
                 }
             }
             choices.put(ability.getId(), num + ". " + rule);
         }
     }
 
+    private String getAbilityRules(Ability ability, String objectName) {
+        String rule = ability.getRule(objectName);
+        if (rule.isEmpty()) {
+            rule = ability.toString();
+        }
+        rule = Character.toUpperCase(rule.charAt(0)) + rule.substring(1);
+        return rule;
+    }
+
     public AbilityPickerView(Map<UUID, String> modes, String message) {
         this.choices = modes;
         this.message = message;
diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/AbilityPickerTest.java b/Mage.Tests/src/test/java/org/mage/test/serverside/AbilityPickerTest.java
new file mode 100644
index 0000000000..a064f280e4
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/serverside/AbilityPickerTest.java
@@ -0,0 +1,87 @@
+package org.mage.test.serverside;
+
+import mage.abilities.Abilities;
+import mage.abilities.Ability;
+import mage.cards.repository.CardInfo;
+import mage.cards.repository.CardRepository;
+import mage.game.permanent.PermanentCard;
+import mage.game.permanent.PermanentImpl;
+import mage.view.AbilityPickerView;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestPlayerBase;
+
+/**
+ * @author JayDi85
+ */
+public class AbilityPickerTest extends CardTestPlayerBase {
+
+    @Test
+    public void test_PickerChoices_FusedSpells() {
+        // must be 3 spells for choices
+        Abilities<Ability> abilities = getAbilitiesFromCard("Armed // Dangerous");
+        Assert.assertEquals(3, abilities.size());
+
+        AbilityPickerView view = new AbilityPickerView("test name", abilities, "test message");
+        Assert.assertEquals(3, view.getChoices().size());
+        view.getChoices().values().forEach(c -> {
+            Assert.assertTrue("Must start with Cast text, but found: " + c, c.contains("Cast "));
+        });
+    }
+
+    @Test
+    public void test_PickerChoices_AdventureSpells() {
+        // must be 2 spells for choices and 1 static ability
+        Abilities<Ability> abilities = getAbilitiesFromCard("Foulmire Knight");
+        Assert.assertEquals(3, abilities.size());
+
+        AbilityPickerView view = new AbilityPickerView("test name", abilities, "test message");
+        Assert.assertEquals(3, view.getChoices().size());
+        view.getChoices().values().forEach(c -> {
+            if (c.contains("Deathtouch")) {
+                return;
+            }
+            Assert.assertTrue("Must start with Cast text, but found: " + c, c.contains("Cast "));
+        });
+    }
+
+    @Test
+    public void test_PickerChoices_ActivatedAbilities() {
+        // must be 1 cast + 3 abilities
+        Abilities<Ability> abilities = getAbilitiesFromCard("Dimir Cluestone");
+        Assert.assertEquals(4, abilities.size());
+
+        AbilityPickerView view = new AbilityPickerView("test name", abilities, "test message");
+        Assert.assertEquals(4, view.getChoices().size());
+        int castCount = 0;
+        int abilsCount = 0;
+        for (String c : view.getChoices().values()) {
+            if (c.contains("Cast ")) {
+                castCount++;
+            } else {
+                abilsCount++;
+            }
+        }
+        Assert.assertEquals(1, castCount);
+        Assert.assertEquals(3, abilsCount);
+    }
+
+    @Test
+    public void test_PickerChoices_AdditionalSpells() {
+        // must be 2 cast
+        Abilities<Ability> abilities = getAbilitiesFromCard("Cling to Dust");
+        Assert.assertEquals(2, abilities.size());
+
+        AbilityPickerView view = new AbilityPickerView("test name", abilities, "test message");
+        Assert.assertEquals(2, view.getChoices().size());
+        view.getChoices().values().forEach(c -> {
+            Assert.assertTrue("Must start with Cast text, but found: " + c, c.contains("Cast "));
+        });
+    }
+
+    private Abilities<Ability> getAbilitiesFromCard(String cardName) {
+        CardInfo info = CardRepository.instance.findCard(cardName);
+        PermanentImpl permanent = new PermanentCard(info.getCard(), playerA.getId(), currentGame);
+        return permanent.getAbilities(currentGame);
+    }
+}

From 0be7d6579b8ec60ee018073a5b43768a9dc8b7ee Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Mon, 18 May 2020 08:24:17 +0400
Subject: [PATCH 044/586] * UI: split cards - fixed wrong card names in rules
 text and hints (#6549);

---
 .../mage/client/util/gui/GuiDisplayUtil.java  | 26 ++++++++++++++-----
 .../org/mage/card/arcane/CardRenderer.java    | 19 +++++++++++++-
 .../mage/card/arcane/TextboxRuleParser.java   |  7 +++--
 3 files changed, 41 insertions(+), 11 deletions(-)

diff --git a/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java b/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java
index 1469aff0e4..eea8d87ac3 100644
--- a/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java
+++ b/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java
@@ -14,9 +14,8 @@ import org.mage.card.arcane.UI;
 
 import javax.swing.*;
 import java.awt.*;
-import java.util.ArrayList;
 import java.util.List;
-import java.util.Locale;
+import java.util.*;
 
 import static mage.client.dialog.PreferencesDialog.KEY_MAGE_PANEL_LAST_SIZE;
 
@@ -344,6 +343,9 @@ public final class GuiDisplayUtil {
         }
         buffer.append("</td></tr></table>");
 
+        // split card rules shows up by parts, so no needs to duplicate it later (only dynamic abilities must be shown)
+        Set<String> duplicatedRules = new HashSet<>();
+
         StringBuilder rule = new StringBuilder("<br/>");
         if (card.isSplitCard()) {
             rule.append("<table cellspacing=0 cellpadding=0 border=0 width='100%'>");
@@ -356,7 +358,9 @@ public final class GuiDisplayUtil {
             rule.append("</td></tr></table>");
             for (String ruling : card.getLeftSplitRules()) {
                 if (ruling != null && !ruling.replace(".", "").trim().isEmpty()) {
-                    rule.append("<p style='margin: 2px'>").append(ruling).append("</p>");
+                    // split names must be replaced
+                    duplicatedRules.add(ruling);
+                    rule.append("<p style='margin: 2px'>").append(replaceNamesInRule(ruling, card.getLeftSplitName())).append("</p>");
                 }
             }
             rule.append("<table cellspacing=0 cellpadding=0 border=0 width='100%'>");
@@ -369,13 +373,18 @@ public final class GuiDisplayUtil {
             rule.append("</td></tr></table>");
             for (String ruling : card.getRightSplitRules()) {
                 if (ruling != null && !ruling.replace(".", "").trim().isEmpty()) {
-                    rule.append("<p style='margin: 2px'>").append(ruling).append("</p>");
+                    // split names must be replaced
+                    duplicatedRules.add(ruling);
+                    rule.append("<p style='margin: 2px'>").append(replaceNamesInRule(ruling, card.getRightSplitName())).append("</p>");
                 }
             }
         }
         if (!textLines.getLines().isEmpty()) {
             for (String textLine : textLines.getLines()) {
                 if (textLine != null && !textLine.replace(".", "").trim().isEmpty()) {
+                    if (duplicatedRules.contains(textLine)) {
+                        continue;
+                    }
                     rule.append("<p style='margin: 2px'>").append(textLine).append("</p>");
                 }
             }
@@ -383,8 +392,7 @@ public final class GuiDisplayUtil {
 
         String legal = rule.toString();
         if (!legal.isEmpty()) {
-            legal = legal.replaceAll("\\{this\\}", card.getName().isEmpty() ? "this" : card.getName());
-            legal = legal.replaceAll("\\{source\\}", card.getName().isEmpty() ? "this" : card.getName());
+            legal = replaceNamesInRule(legal, card.getDisplayName()); // must show real display name (e.g. split part, not original card)
             buffer.append(ManaSymbols.replaceSymbolsWithHTML(legal, ManaSymbols.Type.TOOLTIP));
         }
 
@@ -397,6 +405,12 @@ public final class GuiDisplayUtil {
         return buffer;
     }
 
+    private static String replaceNamesInRule(String rule, String cardName) {
+        String res = rule.replaceAll("\\{this\\}", cardName.isEmpty() ? "this" : cardName);
+        res = res.replaceAll("\\{source\\}", cardName.isEmpty() ? "this" : cardName);
+        return res;
+    }
+
     private static String getResourcePath(String image) {
         return GuiDisplayUtil.class.getClassLoader().getResource(image).toString();
     }
diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardRenderer.java
index 048d4d42db..bdb099bac9 100644
--- a/Mage.Client/src/main/java/org/mage/card/arcane/CardRenderer.java
+++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardRenderer.java
@@ -140,13 +140,30 @@ public abstract class CardRenderer {
                 break;
             }
 
+            // workaround to use real split card names
+            String realCardName = cardView.getDisplayName();
+            if (cardView.isSplitCard()) {
+                for (String partRule : cardView.getLeftSplitRules()) {
+                    if (partRule.equals(rule)) {
+                        realCardName = cardView.getLeftSplitName();
+                        break;
+                    }
+                }
+                for (String partRule : cardView.getRightSplitRules()) {
+                    if (partRule.equals(rule)) {
+                        realCardName = cardView.getRightSplitName();
+                        break;
+                    }
+                }
+            }
+
             // Kill reminder text
             if (PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_RENDERING_REMINDER_TEXT, "false").equals("false")) {
                 rule = CardRendererUtils.killReminderText(rule).trim();
             }
 
             if (!rule.isEmpty()) {
-                TextboxRule tbRule = TextboxRuleParser.parse(cardView, rule);
+                TextboxRule tbRule = TextboxRuleParser.parse(cardView, rule, realCardName);
                 if (tbRule.type == TextboxRuleType.SIMPLE_KEYWORD) {
                     keywords.add(tbRule);
                 } else if (tbRule.text.isEmpty()) {
diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/TextboxRuleParser.java b/Mage.Client/src/main/java/org/mage/card/arcane/TextboxRuleParser.java
index a9ba60ed4f..27fc01cd62 100644
--- a/Mage.Client/src/main/java/org/mage/card/arcane/TextboxRuleParser.java
+++ b/Mage.Client/src/main/java/org/mage/card/arcane/TextboxRuleParser.java
@@ -30,7 +30,7 @@ public final class TextboxRuleParser {
     // if the ability is a loyalty ability, and returning an TextboxRule
     // representing that information, which can be used to render the rule in
     // the textbox of a card.
-    public static TextboxRule parse(CardView source, String rule) {
+    public static TextboxRule parse(CardView source, String rule, String cardNameToUse) {
         // List of regions to apply
         List<TextboxRule.AttributeRegion> regions = new ArrayList<>();
 
@@ -104,10 +104,9 @@ public final class TextboxRuleParser {
                         String contents = rule.substring(index + 1, closeIndex);
                         if (contents.equals("this") || contents.equals("source")) {
                             // Replace {this} with the card's name
-                            String cardName = source.getName();
-                            build.append(cardName);
+                            build.append(cardNameToUse);
                             index += contents.length() + 2;
-                            outputIndex += cardName.length();
+                            outputIndex += cardNameToUse.length();
                         } else {
                             Image symbol = ManaSymbols.getSizedManaSymbol(contents.replace("/", ""), 10);
                             if (symbol != null) {

From 5b36fd390a3c618cc0b7148f505b743c1479244b Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Mon, 18 May 2020 12:26:08 +0400
Subject: [PATCH 045/586] Fixed test

---
 Mage.Sets/src/mage/sets/IkoriaLairOfBehemoths.java | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Mage.Sets/src/mage/sets/IkoriaLairOfBehemoths.java b/Mage.Sets/src/mage/sets/IkoriaLairOfBehemoths.java
index dcc3d8dcd9..0ec42b4dc2 100644
--- a/Mage.Sets/src/mage/sets/IkoriaLairOfBehemoths.java
+++ b/Mage.Sets/src/mage/sets/IkoriaLairOfBehemoths.java
@@ -407,8 +407,8 @@ public final class IkoriaLairOfBehemoths extends ExpansionSet {
         cards.add(new SetCardInfo("Thieving Otter", 69, Rarity.COMMON, mage.cards.t.ThievingOtter.class));
         cards.add(new SetCardInfo("Thornwood Falls", 256, Rarity.COMMON, mage.cards.t.ThornwoodFalls.class));
         cards.add(new SetCardInfo("Thwart the Enemy", 173, Rarity.COMMON, mage.cards.t.ThwartTheEnemy.class));
-        cards.add(new SetCardInfo("Titanoth Rex", 174, Rarity.UNCOMMON, mage.cards.t.TitanothRex.class));
-        cards.add(new SetCardInfo("Titanoth Rex", 377, Rarity.UNCOMMON, mage.cards.t.TitanothRex.class));
+        cards.add(new SetCardInfo("Titanoth Rex", 174, Rarity.UNCOMMON, mage.cards.t.TitanothRex.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Titanoth Rex", 377, Rarity.UNCOMMON, mage.cards.t.TitanothRex.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Titans' Nest", 212, Rarity.RARE, mage.cards.t.TitansNest.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Titans' Nest", 347, Rarity.RARE, mage.cards.t.TitansNest.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Tranquil Cove", 257, Rarity.COMMON, mage.cards.t.TranquilCove.class));
@@ -427,8 +427,8 @@ public final class IkoriaLairOfBehemoths extends ExpansionSet {
         cards.add(new SetCardInfo("Valiant Rescuer", 36, Rarity.UNCOMMON, mage.cards.v.ValiantRescuer.class));
         cards.add(new SetCardInfo("Vivien, Monsters' Advocate", 175, Rarity.MYTHIC, mage.cards.v.VivienMonstersAdvocate.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Vivien, Monsters' Advocate", 277, Rarity.MYTHIC, mage.cards.v.VivienMonstersAdvocate.class, NON_FULL_USE_VARIOUS));
-        cards.add(new SetCardInfo("Void Beckoner", 104, Rarity.UNCOMMON, mage.cards.v.VoidBeckoner.class));
-        cards.add(new SetCardInfo("Void Beckoner", 373, Rarity.UNCOMMON, mage.cards.v.VoidBeckoner.class));
+        cards.add(new SetCardInfo("Void Beckoner", 104, Rarity.UNCOMMON, mage.cards.v.VoidBeckoner.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Void Beckoner", 373, Rarity.UNCOMMON, mage.cards.v.VoidBeckoner.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Voracious Greatshark", 320, Rarity.RARE, mage.cards.v.VoraciousGreatshark.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Voracious Greatshark", 70, Rarity.RARE, mage.cards.v.VoraciousGreatshark.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Vulpikeet", 282, Rarity.COMMON, mage.cards.v.Vulpikeet.class));

From 944ef840363c0d3abf8b4948c3898372dd13f95d Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 18 May 2020 10:39:00 -0400
Subject: [PATCH 046/586] updated ban lists

---
 .../Mage.Deck.Constructed/src/mage/deck/Brawl.java             | 2 ++
 .../Mage.Deck.Constructed/src/mage/deck/Legacy.java            | 3 ++-
 .../Mage.Deck.Constructed/src/mage/deck/Vintage.java           | 1 +
 3 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Brawl.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Brawl.java
index e656b53d87..2fd9250d66 100644
--- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Brawl.java
+++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Brawl.java
@@ -25,9 +25,11 @@ public class Brawl extends Constructed {
         setCodes.addAll(Standard.makeLegalSets());
 
         banned.add("Golos, Tireless Pilgrim");
+        banned.add("Drannith Magistrate");
         banned.add("Lutri, the Spellchaser");
         banned.add("Oko, Thief of Crowns");
         banned.add("Sorcerous Spyglass");
+        banned.add("Winota, Joiner of Forces");
     }
 
     public Brawl(String name) {
diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Legacy.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Legacy.java
index 47abc0531b..d297bdfb8e 100644
--- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Legacy.java
+++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Legacy.java
@@ -49,6 +49,7 @@ public class Legacy extends Constructed {
         banned.add("Iterative Analysis");
         banned.add("Jeweled Bird");
         banned.add("Library of Alexandria");
+        banned.add("Lurrus of the Dream-Den");
         banned.add("Mana Crypt");
         banned.add("Mana Drain");
         banned.add("Mana Vault");
@@ -94,6 +95,6 @@ public class Legacy extends Constructed {
         banned.add("Wrenn and Six");
         banned.add("Yawgmoth's Bargain");
         banned.add("Yawgmoth's Will");
-
+        banned.add("Zirda, the Dawnwaker");
     }
 }
diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Vintage.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Vintage.java
index a01a05ced3..a6cacdf855 100644
--- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Vintage.java
+++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Vintage.java
@@ -30,6 +30,7 @@ public class Vintage extends Constructed {
         banned.add("Immediate Action");
         banned.add("Iterative Analysis");
         banned.add("Jeweled Bird");
+        banned.add("Lurrus of the Dream-Den");
         banned.add("Muzzio's Preparations");
         banned.add("Power Play");
         banned.add("Rebirth");

From 6b5108770cf3a4de6d97a31cb542a28b5cc91211 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Tue, 19 May 2020 00:27:42 +0400
Subject: [PATCH 047/586] * UI: fixed wrong ability text capitalization in mode
 choose dialog;

---
 Mage.Common/src/main/java/mage/view/AbilityPickerView.java    | 4 +++-
 .../Mage.Player.Human/src/mage/player/human/HumanPlayer.java  | 3 +++
 2 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/Mage.Common/src/main/java/mage/view/AbilityPickerView.java b/Mage.Common/src/main/java/mage/view/AbilityPickerView.java
index 7cb356d20b..d4ae6a0c23 100644
--- a/Mage.Common/src/main/java/mage/view/AbilityPickerView.java
+++ b/Mage.Common/src/main/java/mage/view/AbilityPickerView.java
@@ -49,7 +49,9 @@ public class AbilityPickerView implements Serializable {
         if (rule.isEmpty()) {
             rule = ability.toString();
         }
-        rule = Character.toUpperCase(rule.charAt(0)) + rule.substring(1);
+        if (!rule.isEmpty()) {
+            rule = Character.toUpperCase(rule.charAt(0)) + rule.substring(1);
+        }
         return rule;
     }
 
diff --git a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java
index 10632fbf1d..edd52b2c80 100644
--- a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java
+++ b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java
@@ -2096,6 +2096,9 @@ public class HumanPlayer extends PlayerImpl {
                             modeText = "(selected " + timesSelected + "x) " + modeText;
                         }
                     }
+                    if (!modeText.isEmpty()) {
+                        modeText = Character.toUpperCase(modeText.charAt(0)) + modeText.substring(1);
+                    }
                     modeMap.put(mode.getId(), modeIndex + ". " + modeText);
                 }
             }

From 63dbf5f40b2b83daa53215beb0c0c0280c43416b Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Tue, 19 May 2020 01:30:46 +0400
Subject: [PATCH 048/586] * Split cards improves:  * Fixed that fused spells
 can't be played from non hand zone;  * Fixed double choose spell dialog
 (#5506, #6549);

---
 .../java/mage/player/ai/ComputerPlayer.java   | 21 ---------
 .../src/mage/player/human/HumanPlayer.java    | 45 -------------------
 .../mage/test/player/TestComputerPlayer.java  | 33 --------------
 .../mage/test/player/TestComputerPlayer7.java | 33 --------------
 .../player/TestComputerPlayerMonteCarlo.java  | 33 --------------
 .../java/org/mage/test/player/TestPlayer.java |  6 ---
 .../java/org/mage/test/stub/PlayerStub.java   |  5 ---
 Mage/src/main/java/mage/players/Player.java   |  2 -
 .../main/java/mage/players/PlayerImpl.java    | 13 +-----
 9 files changed, 1 insertion(+), 190 deletions(-)

diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java
index d1c17c2466..4708905b36 100644
--- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java
+++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java
@@ -1895,27 +1895,6 @@ public class ComputerPlayer extends PlayerImpl implements Player {
         return 0;
     }
 
-    @Override
-    public SpellAbility chooseSpellAbilityForCast(SpellAbility ability, Game game, boolean noMana) {
-        switch (ability.getSpellAbilityType()) {
-            case SPLIT:
-            case SPLIT_FUSED:
-            case SPLIT_AFTERMATH:
-                MageObject object = game.getObject(ability.getSourceId());
-                if (object != null) {
-                    LinkedHashMap<UUID, ActivatedAbility> useableAbilities = getSpellAbilities(playerId, object, game.getState().getZone(object.getId()), game);
-                    if (useableAbilities != null && !useableAbilities.isEmpty()) {
-                        // game.fireGetChoiceEvent(playerId, name, object, new ArrayList<>(useableAbilities.values()));
-                        // TODO: Improve this
-                        return (SpellAbility) useableAbilities.values().iterator().next();
-                    }
-                }
-                return null;
-            default:
-                return ability;
-        }
-    }
-
     @Override
     public Mode chooseMode(Modes modes, Ability source, Game game) {
         log.debug("chooseMode");
diff --git a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java
index edd52b2c80..18ca9e30a4 100644
--- a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java
+++ b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java
@@ -1973,51 +1973,6 @@ public class HumanPlayer extends PlayerImpl {
         return true;
     }
 
-    @Override
-    public SpellAbility chooseSpellAbilityForCast(SpellAbility ability, Game game, boolean noMana) {
-        if (gameInCheckPlayableState(game)) {
-            return null;
-        }
-
-        // TODO: add canRespond cycle?
-        if (!canRespond()) {
-            return null;
-        }
-
-        switch (ability.getSpellAbilityType()) {
-            case SPLIT:
-            case SPLIT_FUSED:
-            case SPLIT_AFTERMATH:
-                MageObject object = game.getObject(ability.getSourceId());
-                if (object != null) {
-                    String message = "Choose ability to cast" + (noMana ? " for FREE" : "") + "<br>" + object.getLogName();
-                    LinkedHashMap<UUID, ActivatedAbility> useableAbilities = getSpellAbilities(playerId, object, game.getState().getZone(object.getId()), game);
-                    if (useableAbilities != null
-                            && useableAbilities.size() == 1) {
-                        return (SpellAbility) useableAbilities.values().iterator().next();
-                    } else if (useableAbilities != null
-                            && !useableAbilities.isEmpty()) {
-
-                        updateGameStatePriority("chooseSpellAbilityForCast", game);
-                        prepareForResponse(game);
-                        if (!isExecutingMacro()) {
-                            game.fireGetChoiceEvent(playerId, message, object, new ArrayList<>(useableAbilities.values()));
-                        }
-                        waitForResponse(game);
-
-                        if (response.getUUID() != null) {
-                            if (useableAbilities.containsKey(response.getUUID())) {
-                                return (SpellAbility) useableAbilities.get(response.getUUID());
-                            }
-                        }
-                    }
-                }
-                return null;
-            default:
-                return ability;
-        }
-    }
-
     @Override
     public SpellAbility chooseAbilityForCast(Card card, Game game, boolean nonMana) {
         if (gameInCheckPlayableState(game)) {
diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestComputerPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestComputerPlayer.java
index b0ec4c7b39..244e572636 100644
--- a/Mage.Tests/src/test/java/org/mage/test/player/TestComputerPlayer.java
+++ b/Mage.Tests/src/test/java/org/mage/test/player/TestComputerPlayer.java
@@ -29,39 +29,6 @@ public class TestComputerPlayer extends ComputerPlayer {
         this.testPlayerLink = testPlayerLink;
     }
 
-    @Override
-    public SpellAbility chooseSpellAbilityForCast(SpellAbility ability, Game game, boolean noMana) {
-        // copy-paste for TestComputerXXX
-
-        // workaround to cast fused cards in tests by it's NAMES (Wear, Tear, Wear // Tear)
-        // reason: TestPlayer uses outer computerPlayer to cast, not TestPlayer
-        switch (ability.getSpellAbilityType()) {
-            case SPLIT:
-            case SPLIT_FUSED:
-            case SPLIT_AFTERMATH:
-                if (!this.testPlayerLink.getChoices().isEmpty()) {
-                    MageObject object = game.getObject(ability.getSourceId());
-                    if (object != null) {
-                        LinkedHashMap<UUID, ActivatedAbility> useableAbilities = getSpellAbilities(playerId, object, game.getState().getZone(object.getId()), game);
-
-                        // left, right or fused cast
-                        for (String choose : this.testPlayerLink.getChoices()) {
-                            for (ActivatedAbility activatedAbility : useableAbilities.values()) {
-                                if (activatedAbility instanceof SpellAbility) {
-                                    if (((SpellAbility) activatedAbility).getCardName().equals(choose)) {
-                                        return (SpellAbility) activatedAbility;
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-        }
-
-        // default implementation by AI
-        return super.chooseSpellAbilityForCast(ability, game, noMana);
-    }
-
     @Override
     public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game) {
         // copy-paste for TestComputerXXX
diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestComputerPlayer7.java b/Mage.Tests/src/test/java/org/mage/test/player/TestComputerPlayer7.java
index f8d9933eb4..d09df3087f 100644
--- a/Mage.Tests/src/test/java/org/mage/test/player/TestComputerPlayer7.java
+++ b/Mage.Tests/src/test/java/org/mage/test/player/TestComputerPlayer7.java
@@ -29,39 +29,6 @@ public class TestComputerPlayer7 extends ComputerPlayer7 {
         this.testPlayerLink = testPlayerLink;
     }
 
-    @Override
-    public SpellAbility chooseSpellAbilityForCast(SpellAbility ability, Game game, boolean noMana) {
-        // copy-paste for TestComputerXXX
-
-        // workaround to cast fused cards in tests by it's NAMES (Wear, Tear, Wear // Tear)
-        // reason: TestPlayer uses outer computerPlayer to cast, not TestPlayer
-        switch (ability.getSpellAbilityType()) {
-            case SPLIT:
-            case SPLIT_FUSED:
-            case SPLIT_AFTERMATH:
-                if (!this.testPlayerLink.getChoices().isEmpty()) {
-                    MageObject object = game.getObject(ability.getSourceId());
-                    if (object != null) {
-                        LinkedHashMap<UUID, ActivatedAbility> useableAbilities = getSpellAbilities(playerId, object, game.getState().getZone(object.getId()), game);
-
-                        // left, right or fused cast
-                        for (String choose : this.testPlayerLink.getChoices()) {
-                            for (ActivatedAbility activatedAbility : useableAbilities.values()) {
-                                if (activatedAbility instanceof SpellAbility) {
-                                    if (((SpellAbility) activatedAbility).getCardName().equals(choose)) {
-                                        return (SpellAbility) activatedAbility;
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-        }
-
-        // default implementation by AI
-        return super.chooseSpellAbilityForCast(ability, game, noMana);
-    }
-
     @Override
     public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game) {
         // copy-paste for TestComputerXXX
diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestComputerPlayerMonteCarlo.java b/Mage.Tests/src/test/java/org/mage/test/player/TestComputerPlayerMonteCarlo.java
index aad9d88650..3dd236fcd9 100644
--- a/Mage.Tests/src/test/java/org/mage/test/player/TestComputerPlayerMonteCarlo.java
+++ b/Mage.Tests/src/test/java/org/mage/test/player/TestComputerPlayerMonteCarlo.java
@@ -29,39 +29,6 @@ public class TestComputerPlayerMonteCarlo extends ComputerPlayerMCTS {
         this.testPlayerLink = testPlayerLink;
     }
 
-    @Override
-    public SpellAbility chooseSpellAbilityForCast(SpellAbility ability, Game game, boolean noMana) {
-        // copy-paste for TestComputerXXX
-
-        // workaround to cast fused cards in tests by it's NAMES (Wear, Tear, Wear // Tear)
-        // reason: TestPlayer uses outer computerPlayer to cast, not TestPlayer
-        switch (ability.getSpellAbilityType()) {
-            case SPLIT:
-            case SPLIT_FUSED:
-            case SPLIT_AFTERMATH:
-                if (!this.testPlayerLink.getChoices().isEmpty()) {
-                    MageObject object = game.getObject(ability.getSourceId());
-                    if (object != null) {
-                        LinkedHashMap<UUID, ActivatedAbility> useableAbilities = getSpellAbilities(playerId, object, game.getState().getZone(object.getId()), game);
-
-                        // left, right or fused cast
-                        for (String choose : this.testPlayerLink.getChoices()) {
-                            for (ActivatedAbility activatedAbility : useableAbilities.values()) {
-                                if (activatedAbility instanceof SpellAbility) {
-                                    if (((SpellAbility) activatedAbility).getCardName().equals(choose)) {
-                                        return (SpellAbility) activatedAbility;
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-        }
-
-        // default implementation by AI
-        return super.chooseSpellAbilityForCast(ability, game, noMana);
-    }
-
     @Override
     public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game) {
         // copy-paste for TestComputerXXX
diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
index 34f9dbdc1a..4dd31575be 100644
--- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
+++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
@@ -3456,12 +3456,6 @@ public class TestPlayer implements Player {
         computerPlayer.cleanUpOnMatchEnd();
     }
 
-    @Override
-    public SpellAbility chooseSpellAbilityForCast(SpellAbility ability, Game game, boolean noMana) {
-        Assert.fail("That's method must calls only from computerPlayer->cast(), see TestComputerPlayerXXX");
-        return computerPlayer.chooseSpellAbilityForCast(ability, game, noMana);
-    }
-
     @Override
     public void skip() {
         computerPlayer.skip();
diff --git a/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java b/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java
index ed5e1adbbc..a104c25d48 100644
--- a/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java
+++ b/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java
@@ -543,11 +543,6 @@ public class PlayerStub implements Player {
         return false;
     }
 
-    @Override
-    public SpellAbility chooseSpellAbilityForCast(SpellAbility ability, Game game, boolean noMana) {
-        return null;
-    }
-
     @Override
     public boolean putInHand(Card card, Game game) {
         return false;
diff --git a/Mage/src/main/java/mage/players/Player.java b/Mage/src/main/java/mage/players/Player.java
index 0e7c610903..51f7536137 100644
--- a/Mage/src/main/java/mage/players/Player.java
+++ b/Mage/src/main/java/mage/players/Player.java
@@ -317,8 +317,6 @@ public interface Player extends MageItem, Copyable<Player> {
 
     boolean cast(SpellAbility ability, Game game, boolean noMana, MageObjectReference reference);
 
-    SpellAbility chooseSpellAbilityForCast(SpellAbility ability, Game game, boolean noMana);
-
     SpellAbility chooseAbilityForCast(Card card, Game game, boolean noMana);
 
     boolean putInHand(Card card, Game game);
diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java
index 305199185a..a4a8667daf 100644
--- a/Mage/src/main/java/mage/players/PlayerImpl.java
+++ b/Mage/src/main/java/mage/players/PlayerImpl.java
@@ -1120,13 +1120,7 @@ public abstract class PlayerImpl implements Player, Serializable {
         SpellAbility ability = originalAbility.copy();
         ability.setControllerId(getId());
         ability.setSourceObjectZoneChangeCounter(game.getState().getZoneChangeCounter(ability.getSourceId()));
-        if (ability.getSpellAbilityType() != SpellAbilityType.BASE) {
-            ability = chooseSpellAbilityForCast(ability, game, noMana);
-            if (ability == null) {
-                // No ability could be cast (selected), probably because of no valid targets (happens often if a card can be cast by an effect).
-                return false;
-            }
-        }
+
         //20091005 - 601.2a
         if (ability.getSourceId() == null) {
             logger.error("Ability without sourceId turn " + game.getTurnNum() + ". Ability: " + ability.getRule());
@@ -1189,11 +1183,6 @@ public abstract class PlayerImpl implements Player, Serializable {
         return false;
     }
 
-    @Override
-    public SpellAbility chooseSpellAbilityForCast(SpellAbility ability, Game game, boolean noMana) {
-        return ability;
-    }
-
     @Override
     public boolean playLand(Card card, Game game, boolean ignoreTiming) {
         // Check for alternate casting possibilities: e.g. land with Morph

From abda99e203fcabdae107200b6103b668b822cadd Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Sun, 24 May 2020 09:21:49 +0400
Subject: [PATCH 049/586] Fixed that fused cards allows to cast from graveyard
 (see prev commit 63dbf5f40b2b83daa53215beb0c0c0280c43416b);

---
 Mage.Sets/src/mage/cards/m/ManaCache.java     |   7 +-
 .../src/mage/cards/w/WellOfKnowledge.java     |  13 +--
 .../cards/single/KessDissidentMageTest.java   | 104 ++++++++++++++++++
 .../java/mage/abilities/ActivatedAbility.java |  16 ++-
 .../java/mage/abilities/SpellAbility.java     |  13 ++-
 .../mage/abilities/common/PassAbility.java    |   7 +-
 .../mage/abilities/keyword/EmergeAbility.java |   2 +-
 .../abilities/keyword/FlashbackAbility.java   |  17 +--
 .../abilities/keyword/SpectacleAbility.java   |   2 +-
 .../mage/abilities/keyword/SurgeAbility.java  |   2 +-
 10 files changed, 143 insertions(+), 40 deletions(-)
 create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/single/KessDissidentMageTest.java

diff --git a/Mage.Sets/src/mage/cards/m/ManaCache.java b/Mage.Sets/src/mage/cards/m/ManaCache.java
index 54cb19a0ba..be93b51c67 100644
--- a/Mage.Sets/src/mage/cards/m/ManaCache.java
+++ b/Mage.Sets/src/mage/cards/m/ManaCache.java
@@ -1,7 +1,5 @@
-
 package mage.cards.m;
 
-import java.util.UUID;
 import mage.Mana;
 import mage.abilities.Ability;
 import mage.abilities.TriggeredAbility;
@@ -27,8 +25,9 @@ import mage.game.events.GameEvent;
 import mage.game.permanent.Permanent;
 import mage.players.Player;
 
+import java.util.UUID;
+
 /**
- *
  * @author L_J
  */
 public final class ManaCache extends CardImpl {
@@ -105,7 +104,7 @@ class ManaCacheManaAbility extends ActivatedManaAbilityImpl {
         if (player != null && playerId.equals(game.getActivePlayerId()) && game.getStep().getType().isBefore(PhaseStep.END_TURN)) {
             if (costs.canPay(this, sourceId, playerId, game)) {
                 this.setControllerId(playerId);
-                return ActivationStatus.getTrue();
+                return ActivationStatus.getTrue(this, game);
             }
         }
         return ActivationStatus.getFalse();
diff --git a/Mage.Sets/src/mage/cards/w/WellOfKnowledge.java b/Mage.Sets/src/mage/cards/w/WellOfKnowledge.java
index 2986d5b86d..a054e2ced6 100644
--- a/Mage.Sets/src/mage/cards/w/WellOfKnowledge.java
+++ b/Mage.Sets/src/mage/cards/w/WellOfKnowledge.java
@@ -1,7 +1,5 @@
-
 package mage.cards.w;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.ActivatedAbilityImpl;
 import mage.abilities.condition.common.IsStepCondition;
@@ -10,16 +8,13 @@ import mage.abilities.effects.Effects;
 import mage.abilities.effects.OneShotEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.CardType;
-import mage.constants.EffectType;
-import mage.constants.Outcome;
-import mage.constants.PhaseStep;
-import mage.constants.Zone;
+import mage.constants.*;
 import mage.game.Game;
 import mage.players.Player;
 
+import java.util.UUID;
+
 /**
- *
  * @author Styxo
  */
 public final class WellOfKnowledge extends CardImpl {
@@ -68,7 +63,7 @@ class WellOfKnowledgeConditionalActivatedAbility extends ActivatedAbilityImpl {
                 && costs.canPay(this, sourceId, playerId, game)
                 && game.isActivePlayer(playerId)) {
             this.activatorId = playerId;
-            return ActivationStatus.getTrue();
+            return ActivationStatus.getTrue(this, game);
         }
         return ActivationStatus.getFalse();
 
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/KessDissidentMageTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/KessDissidentMageTest.java
new file mode 100644
index 0000000000..fce7afd9db
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/KessDissidentMageTest.java
@@ -0,0 +1,104 @@
+package org.mage.test.cards.single;
+
+import mage.constants.PhaseStep;
+import mage.constants.Zone;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestPlayerBase;
+
+/**
+ * @author JayDi85
+ */
+
+public class KessDissidentMageTest extends CardTestPlayerBase {
+
+    // Kess, Dissident Mage
+    // During each of your turns, you may cast an instant or sorcery card from your graveyard.
+    // If a card cast this way would be put into your graveyard this turn, exile it instead.
+
+    @Test
+    public void test_Simple() {
+        addCard(Zone.BATTLEFIELD, playerA, "Kess, Dissident Mage", 1);
+        //
+        addCard(Zone.GRAVEYARD, playerA, "Lightning Bolt", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
+
+        checkPlayableAbility("must play simple", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Lightning Bolt", true);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB);
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertExileCount(playerA, "Lightning Bolt", 1);
+        assertLife(playerB, 20 - 3);
+    }
+
+    @Test
+    public void test_Split_OnePart() {
+        addCard(Zone.BATTLEFIELD, playerA, "Kess, Dissident Mage", 1);
+        //
+        // Create a 3/3 green Centaur creature token.
+        // You gain 2 life for each creature you control.
+        addCard(Zone.GRAVEYARD, playerA, "Alive // Well", 1); // {3}{G} // {W}
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 4);
+
+        checkPlayableAbility("must play part", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Alive", true);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Alive");
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerA, "Centaur", 1);
+        assertLife(playerA, 20);
+        assertExileCount(playerA, "Alive // Well", 1);
+    }
+
+    @Test
+    public void test_Split_Check() {
+        // testing check command only for fused cards
+
+        // Create a 3/3 green Centaur creature token.
+        // You gain 2 life for each creature you control.
+        addCard(Zone.HAND, playerA, "Alive // Well", 1); // {3}{G} // {W}
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 4);
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 1);
+
+        // tap green first for Alive spell
+        activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {G}", 4);
+        checkPlayableAbility("must play fused", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast fused Alive // Well", true);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "fused Alive // Well");
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerA, "Centaur", 1);
+        assertLife(playerA, 20 + 2);
+        assertGraveyardCount(playerA, "Alive // Well", 1);
+    }
+
+    @Test
+    public void test_Split_CantPlay() {
+        addCard(Zone.BATTLEFIELD, playerA, "Kess, Dissident Mage", 1);
+        //
+        // Create a 3/3 green Centaur creature token.
+        // You gain 2 life for each creature you control.
+        addCard(Zone.GRAVEYARD, playerA, "Alive // Well", 1); // {3}{G} // {W}
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 4);
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 1);
+
+        // tap green first for Alive spell
+        activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {G}", 4);
+        checkPlayableAbility("can't play fused from graveyard", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast fused Alive // Well", false);
+        //castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "fused Alive // Well");
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+    }
+}
diff --git a/Mage/src/main/java/mage/abilities/ActivatedAbility.java b/Mage/src/main/java/mage/abilities/ActivatedAbility.java
index 4601964969..95e97d2bfd 100644
--- a/Mage/src/main/java/mage/abilities/ActivatedAbility.java
+++ b/Mage/src/main/java/mage/abilities/ActivatedAbility.java
@@ -1,18 +1,19 @@
 package mage.abilities;
 
-import java.util.UUID;
+import mage.MageObject;
 import mage.MageObjectReference;
 import mage.abilities.mana.ManaOptions;
 import mage.constants.TargetController;
 import mage.game.Game;
 
+import java.util.UUID;
+
 /**
- *
  * @author BetaSteward_at_googlemail.com
  */
 public interface ActivatedAbility extends Ability {
 
-    final public class ActivationStatus {
+    final class ActivationStatus {
 
         private final boolean canActivate;
         private final MageObjectReference permittingObject;
@@ -34,8 +35,13 @@ public interface ActivatedAbility extends Ability {
             return new ActivationStatus(false, null);
         }
 
-        public static ActivationStatus getTrue() {
-            return new ActivationStatus(true, null);
+        /**
+         * @param permittingObjectAbility card or permanent that allows to activate current ability
+         */
+        public static ActivationStatus getTrue(Ability permittingObjectAbility, Game game) {
+            MageObject object = permittingObjectAbility == null ? null : permittingObjectAbility.getSourceObject(game);
+            MageObjectReference ref = object == null ? null : new MageObjectReference(object, game);
+            return new ActivationStatus(true, ref);
         }
     }
 
diff --git a/Mage/src/main/java/mage/abilities/SpellAbility.java b/Mage/src/main/java/mage/abilities/SpellAbility.java
index 9bd506f34a..0df442d16c 100644
--- a/Mage/src/main/java/mage/abilities/SpellAbility.java
+++ b/Mage/src/main/java/mage/abilities/SpellAbility.java
@@ -1,7 +1,5 @@
 package mage.abilities;
 
-import java.util.Optional;
-import java.util.UUID;
 import mage.MageObject;
 import mage.MageObjectReference;
 import mage.abilities.costs.Cost;
@@ -17,6 +15,9 @@ import mage.game.events.GameEvent;
 import mage.game.stack.Spell;
 import mage.players.Player;
 
+import java.util.Optional;
+import java.util.UUID;
+
 /**
  * @author BetaSteward_at_googlemail.com
  */
@@ -108,8 +109,12 @@ public class SpellAbility extends ActivatedAbilityImpl {
                 if (getSpellAbilityType() == SpellAbilityType.SPLIT_FUSED) {
                     SplitCard splitCard = (SplitCard) game.getCard(getSourceId());
                     if (splitCard != null) {
-                        return new ActivationStatus(splitCard.getLeftHalfCard().getSpellAbility().canChooseTarget(game)
-                                && splitCard.getRightHalfCard().getSpellAbility().canChooseTarget(game), null);
+                        // fused can be called from hand only, so not permitting object allows or other zones checks
+                        // see https://www.mtgsalvation.com/forums/magic-fundamentals/magic-rulings/magic-rulings-archives/251926-snapcaster-mage-and-fuse
+                        if (game.getState().getZone(splitCard.getId()) == Zone.HAND) {
+                            return new ActivationStatus(splitCard.getLeftHalfCard().getSpellAbility().canChooseTarget(game)
+                                    && splitCard.getRightHalfCard().getSpellAbility().canChooseTarget(game), null);
+                        }
                     }
                     return ActivationStatus.getFalse();
 
diff --git a/Mage/src/main/java/mage/abilities/common/PassAbility.java b/Mage/src/main/java/mage/abilities/common/PassAbility.java
index 08ebb1608a..e49cc4ca4a 100644
--- a/Mage/src/main/java/mage/abilities/common/PassAbility.java
+++ b/Mage/src/main/java/mage/abilities/common/PassAbility.java
@@ -1,14 +1,13 @@
-
 package mage.abilities.common;
 
-import java.util.UUID;
 import mage.abilities.ActivatedAbilityImpl;
 import mage.abilities.effects.common.PassEffect;
 import mage.constants.Zone;
 import mage.game.Game;
 
+import java.util.UUID;
+
 /**
- *
  * @author BetaSteward_at_googlemail.com
  */
 public class PassAbility extends ActivatedAbilityImpl {
@@ -29,7 +28,7 @@ public class PassAbility extends ActivatedAbilityImpl {
 
     @Override
     public ActivationStatus canActivate(UUID playerId, Game game) {
-        return ActivationStatus.getTrue();
+        return ActivationStatus.getTrue(this, game);
     }
 
     @Override
diff --git a/Mage/src/main/java/mage/abilities/keyword/EmergeAbility.java b/Mage/src/main/java/mage/abilities/keyword/EmergeAbility.java
index c11ec9da55..75ef4762ba 100644
--- a/Mage/src/main/java/mage/abilities/keyword/EmergeAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/EmergeAbility.java
@@ -57,7 +57,7 @@ public class EmergeAbility extends SpellAbility {
                         new FilterControlledCreaturePermanent(), this.getControllerId(), this.getSourceId(), game)) {
                     ManaCost costToPay = CardUtil.reduceCost(emergeCost.copy(), creature.getConvertedManaCost());
                     if (costToPay.canPay(this, this.getSourceId(), this.getControllerId(), game)) {
-                        return ActivationStatus.getTrue();
+                        return ActivationStatus.getTrue(this, game);
                     }
                 }
             }
diff --git a/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java b/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java
index 145257ae4b..980716538d 100644
--- a/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java
@@ -1,6 +1,5 @@
 package mage.abilities.keyword;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.SpellAbility;
 import mage.abilities.costs.Cost;
@@ -9,21 +8,18 @@ import mage.abilities.effects.ContinuousEffect;
 import mage.abilities.effects.ReplacementEffectImpl;
 import mage.cards.Card;
 import mage.cards.SplitCard;
-import mage.constants.Duration;
-import mage.constants.Outcome;
-import mage.constants.SpellAbilityCastMode;
-import mage.constants.SpellAbilityType;
-import mage.constants.TimingRule;
-import mage.constants.Zone;
+import mage.constants.*;
 import mage.game.Game;
 import mage.game.events.GameEvent;
 import mage.game.events.ZoneChangeEvent;
 import mage.players.Player;
 import mage.target.targetpointer.FixedTarget;
 
+import java.util.UUID;
+
 /**
  * 702.32. Flashback
- *
+ * <p>
  * 702.32a. Flashback appears on some instants and sorceries. It represents two
  * static abilities: one that functions while the card is in a player‘s
  * graveyard and the other that functions while the card is on the stack.
@@ -69,6 +65,7 @@ public class FlashbackAbility extends SpellAbility {
                     return ActivationStatus.getFalse();
                 }
                 // Flashback can never cast a split card by Fuse, because Fuse only works from hand
+                // https://tappedout.net/mtg-questions/snapcaster-mage-and-flashback-on-a-fuse-card-one-or-both-halves-legal-targets/
                 if (card.isSplitCard()) {
                     if (((SplitCard) card).getLeftHalfCard().getName().equals(abilityName)) {
                         return ((SplitCard) card).getLeftHalfCard().getSpellAbility().canActivate(playerId, game);
@@ -218,9 +215,7 @@ class FlashbackReplacementEffect extends ReplacementEffectImpl {
                 && ((ZoneChangeEvent) event).getToZone() != Zone.EXILED) {
 
             int zcc = game.getState().getZoneChangeCounter(source.getSourceId());
-            if (((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1 == zcc) {
-                return true;
-            }
+            return ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1 == zcc;
 
         }
         return false;
diff --git a/Mage/src/main/java/mage/abilities/keyword/SpectacleAbility.java b/Mage/src/main/java/mage/abilities/keyword/SpectacleAbility.java
index 0bf383e545..9cb4a763a7 100644
--- a/Mage/src/main/java/mage/abilities/keyword/SpectacleAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/SpectacleAbility.java
@@ -48,7 +48,7 @@ public class SpectacleAbility extends SpellAbility {
     public ActivationStatus canActivate(UUID playerId, Game game) {
         if (OpponentsLostLifeCount.instance.calculate(game, playerId) > 0
                 && super.canActivate(playerId, game).canActivate()) {
-            return ActivationStatus.getTrue();
+            return ActivationStatus.getTrue(this, game);
         }
         return ActivationStatus.getFalse();
     }
diff --git a/Mage/src/main/java/mage/abilities/keyword/SurgeAbility.java b/Mage/src/main/java/mage/abilities/keyword/SurgeAbility.java
index 207bf737be..0833adf2df 100644
--- a/Mage/src/main/java/mage/abilities/keyword/SurgeAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/SurgeAbility.java
@@ -54,7 +54,7 @@ public class SurgeAbility extends SpellAbility {
                     if (!player.hasOpponent(playerToCheckId, game)) {
                         if (watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(playerToCheckId) > 0
                                 && super.canActivate(playerId, game).canActivate()) {
-                            return ActivationStatus.getTrue();
+                            return ActivationStatus.getTrue(this, game);
                         }
                     }
                 }

From 9df5c9ba687fec92d43584af29eefb77fef87b9a Mon Sep 17 00:00:00 2001
From: johnm <johnm@WINDOWS-QR5QIIL.lan>
Date: Sun, 24 May 2020 13:52:48 +0100
Subject: [PATCH 050/586] Default unique name for AI.

---
 .../src/main/java/mage/client/table/NewPlayerPanel.java     | 6 ++++++
 .../src/main/java/mage/client/table/TablePlayerPanel.java   | 4 +++-
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/Mage.Client/src/main/java/mage/client/table/NewPlayerPanel.java b/Mage.Client/src/main/java/mage/client/table/NewPlayerPanel.java
index 6645b4ab16..c41c5873f7 100644
--- a/Mage.Client/src/main/java/mage/client/table/NewPlayerPanel.java
+++ b/Mage.Client/src/main/java/mage/client/table/NewPlayerPanel.java
@@ -46,6 +46,12 @@ public class NewPlayerPanel extends javax.swing.JPanel {
         this.txtPlayerName.setEnabled(false);
     }
 
+    public void setPlayerName(String playerName, boolean editable, boolean enabled) {
+        this.txtPlayerName.setText(playerName);
+        this.txtPlayerName.setEditable(editable);
+        this.txtPlayerName.setEnabled(enabled);
+    }
+    
     protected void playerLoadDeck() {
         String lastFolder = MageFrame.getPreferences().get("lastDeckFolder", "");
         if (!lastFolder.isEmpty()) {
diff --git a/Mage.Client/src/main/java/mage/client/table/TablePlayerPanel.java b/Mage.Client/src/main/java/mage/client/table/TablePlayerPanel.java
index ae68ccddd6..a736c414ff 100644
--- a/Mage.Client/src/main/java/mage/client/table/TablePlayerPanel.java
+++ b/Mage.Client/src/main/java/mage/client/table/TablePlayerPanel.java
@@ -126,7 +126,9 @@ public class TablePlayerPanel extends javax.swing.JPanel {
     private void cbPlayerTypeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbPlayerTypeActionPerformed
         if (getPlayerType() != PlayerType.HUMAN) {
             this.newPlayerPanel.setVisible(true);
-        } else {
+            this.newPlayerPanel.setPlayerName("Computer " + this.lblPlayerNum.getText().charAt(this.lblPlayerNum.getText().length() - 1),true,true);
+        } 
+        else {
             this.newPlayerPanel.setVisible(false);
         }
         this.revalidate();

From 5743e4361efb425fc36360853a4059cac7bfe241 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Mon, 25 May 2020 16:51:23 +0200
Subject: [PATCH 051/586] * Some minor fixes/code cleanups to IKO cards.

---
 Mage.Sets/src/mage/cards/c/ChargeOfTheForeverBeast.java    | 4 ++--
 Mage.Sets/src/mage/cards/c/CrystallineGiant.java           | 4 ++--
 Mage.Sets/src/mage/cards/c/CrystallineResonance.java       | 2 +-
 Mage.Sets/src/mage/cards/d/DarkBargain.java                | 2 +-
 Mage.Sets/src/mage/cards/l/LavabrinkVenturer.java          | 1 -
 Mage.Sets/src/mage/cards/m/MythosOfSnapdax.java            | 7 +++----
 Mage.Sets/src/mage/cards/t/TitansNest.java                 | 5 ++++-
 .../mage/abilities/costs/common/ExileFromGraveCost.java    | 4 +++-
 Mage/src/main/java/mage/filter/StaticFilters.java          | 6 ++++++
 9 files changed, 22 insertions(+), 13 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/c/ChargeOfTheForeverBeast.java b/Mage.Sets/src/mage/cards/c/ChargeOfTheForeverBeast.java
index 2ef93a847f..6e878e63de 100644
--- a/Mage.Sets/src/mage/cards/c/ChargeOfTheForeverBeast.java
+++ b/Mage.Sets/src/mage/cards/c/ChargeOfTheForeverBeast.java
@@ -26,7 +26,7 @@ public final class ChargeOfTheForeverBeast extends CardImpl {
 
         // As an additional cost to cast this spell, reveal a creature card from your hand.
         this.getSpellAbility().addCost(new RevealTargetFromHandCost(
-                new TargetCardInHand(StaticFilters.FILTER_CARD_CREATURE_A)
+                new TargetCardInHand(StaticFilters.FILTER_CARD_CREATURE_YOUR_HAND)
         ));
 
         // Charge of the Forever-Beast deals damage to target creature or planeswalker equal to the revealed card's power.
@@ -64,7 +64,7 @@ class ChargeOfTheForeverBeastEffect extends OneShotEffect {
     public boolean apply(Game game, Ability source) {
         Permanent permanent = game.getPermanent(source.getFirstTarget());
         RevealTargetFromHandCost cost = (RevealTargetFromHandCost) source.getCosts().get(0);
-        if (permanent == null && cost == null) {
+        if (permanent == null || cost == null) {
             return false;
         }
         Card card = cost.getRevealedCards().get(0);
diff --git a/Mage.Sets/src/mage/cards/c/CrystallineGiant.java b/Mage.Sets/src/mage/cards/c/CrystallineGiant.java
index 47bd468750..369d9b25c9 100644
--- a/Mage.Sets/src/mage/cards/c/CrystallineGiant.java
+++ b/Mage.Sets/src/mage/cards/c/CrystallineGiant.java
@@ -92,8 +92,8 @@ class CrystallineGiantEffect extends OneShotEffect {
         List<CounterType> counterTypes = new ArrayList();
         counterTypes.addAll(counterTypeSet);
         counterTypes.removeIf(counters::containsKey);
-        if (counterTypes.size() == 0) {
-            return false;
+        if (counterTypes.isEmpty()) {
+            return true;
         }
         return permanent.addCounters(counterTypes.get(
                 RandomUtil.nextInt(counterTypes.size())
diff --git a/Mage.Sets/src/mage/cards/c/CrystallineResonance.java b/Mage.Sets/src/mage/cards/c/CrystallineResonance.java
index b6e7012192..a9f78614e6 100644
--- a/Mage.Sets/src/mage/cards/c/CrystallineResonance.java
+++ b/Mage.Sets/src/mage/cards/c/CrystallineResonance.java
@@ -33,7 +33,7 @@ public final class CrystallineResonance extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}");
 
         // Whenever you cycle a card, you may have Crystalline Resonance become a copy of another target permanent until your next turn, except it has this ability.
-        this.addAbility(this.createAbility());
+        this.addAbility(CrystallineResonance.createAbility());
     }
 
     private CrystallineResonance(final CrystallineResonance card) {
diff --git a/Mage.Sets/src/mage/cards/d/DarkBargain.java b/Mage.Sets/src/mage/cards/d/DarkBargain.java
index 769842cc37..f6fee752e7 100644
--- a/Mage.Sets/src/mage/cards/d/DarkBargain.java
+++ b/Mage.Sets/src/mage/cards/d/DarkBargain.java
@@ -44,7 +44,7 @@ class DarkBargainEffect extends OneShotEffect {
 
     public DarkBargainEffect() {
         super(Outcome.Benefit);
-        this.staticText = "Look at the top three cards of your library. Put two of them into your hand and the rest into your graveyard";
+        this.staticText = "Look at the top three cards of your library. Put two of them into your hand and the other into your graveyard";
     }
 
     public DarkBargainEffect(final DarkBargainEffect effect) {
diff --git a/Mage.Sets/src/mage/cards/l/LavabrinkVenturer.java b/Mage.Sets/src/mage/cards/l/LavabrinkVenturer.java
index 6e30084538..ef18a42db3 100644
--- a/Mage.Sets/src/mage/cards/l/LavabrinkVenturer.java
+++ b/Mage.Sets/src/mage/cards/l/LavabrinkVenturer.java
@@ -79,7 +79,6 @@ class LavabrinkVenturerEffect extends GainAbilitySourceEffect {
         if (choosenMode == null) {
             return false;
         }
-        Ability ability;
         switch (choosenMode) {
             case "Odd":
                 this.ability = new ProtectionAbility(oddFilter);
diff --git a/Mage.Sets/src/mage/cards/m/MythosOfSnapdax.java b/Mage.Sets/src/mage/cards/m/MythosOfSnapdax.java
index 797e9b0d96..d8dd2294d6 100644
--- a/Mage.Sets/src/mage/cards/m/MythosOfSnapdax.java
+++ b/Mage.Sets/src/mage/cards/m/MythosOfSnapdax.java
@@ -40,6 +40,7 @@ public class MythosOfSnapdax extends CardImpl {
         super(card);
     }
 
+    @Override
     public MythosOfSnapdax copy() {
         return new MythosOfSnapdax(this);
     }
@@ -113,10 +114,8 @@ class MythosOfSnapdaxEffect extends OneShotEffect {
             }
         }
 
-        for (Iterator<Permanent> iterator = game.getBattlefield().getActivePermanents(
-                StaticFilters.FILTER_PERMANENT_NON_LAND, source.getControllerId(), game
-        ).iterator(); iterator.hasNext(); ) {
-            Permanent permanent = iterator.next();
+        for (Permanent permanent : game.getBattlefield().getActivePermanents(
+                StaticFilters.FILTER_PERMANENT_NON_LAND, source.getControllerId(), game)) {
             if (permanent == null || toKeep.contains(permanent.getId())) {
                 continue;
             }
diff --git a/Mage.Sets/src/mage/cards/t/TitansNest.java b/Mage.Sets/src/mage/cards/t/TitansNest.java
index 4de69fb430..f805e124d3 100644
--- a/Mage.Sets/src/mage/cards/t/TitansNest.java
+++ b/Mage.Sets/src/mage/cards/t/TitansNest.java
@@ -89,7 +89,10 @@ class TitansNestEffect extends OneShotEffect {
 class TitansNestManaAbility extends ActivatedManaAbilityImpl {
 
     TitansNestManaAbility() {
-        super(Zone.BATTLEFIELD, (BasicManaEffect) new BasicManaEffect(new TitansNestConditionalMana()).setText("Add {C}. Spend this mana only to cast a colored spell without {X} in its mana cost."), new ExileFromGraveCost(new TargetCardInYourGraveyard()));
+        super(Zone.BATTLEFIELD, (BasicManaEffect) new BasicManaEffect(
+                new TitansNestConditionalMana())
+                .setText("Add {C}. Spend this mana only to cast a colored spell without {X} in its mana cost."), 
+                new ExileFromGraveCost(new TargetCardInYourGraveyard()));
         this.netMana.add(Mana.ColorlessMana(1));
     }
 
diff --git a/Mage/src/main/java/mage/abilities/costs/common/ExileFromGraveCost.java b/Mage/src/main/java/mage/abilities/costs/common/ExileFromGraveCost.java
index dd70b3866b..e83cb0045a 100644
--- a/Mage/src/main/java/mage/abilities/costs/common/ExileFromGraveCost.java
+++ b/Mage/src/main/java/mage/abilities/costs/common/ExileFromGraveCost.java
@@ -36,7 +36,9 @@ public class ExileFromGraveCost extends CostImpl {
                     + CardUtil.numberToText(target.getMaxNumberOfTargets()))
                     + ' ' + target.getTargetName();
         } else {
-            this.text = "Exile " + target.getTargetName();
+            this.text = "Exile " 
+                    + (target.getTargetName().startsWith("card ") ? "a ":"") 
+                    + target.getTargetName();
         }
         if (!this.text.endsWith(" from your graveyard")) {
             this.text = this.text + " from your graveyard";
diff --git a/Mage/src/main/java/mage/filter/StaticFilters.java b/Mage/src/main/java/mage/filter/StaticFilters.java
index 5c6c021cf4..2e10475094 100644
--- a/Mage/src/main/java/mage/filter/StaticFilters.java
+++ b/Mage/src/main/java/mage/filter/StaticFilters.java
@@ -89,6 +89,12 @@ public final class StaticFilters {
         FILTER_CARD_CREATURE_A.setLockedFilter(true);
     }
 
+    public static final FilterCreatureCard FILTER_CARD_CREATURE_YOUR_HAND = new FilterCreatureCard("a creature card from your hand");
+
+    static {
+        FILTER_CARD_CREATURE_YOUR_HAND.setLockedFilter(true);
+    }
+
     public static final FilterCreatureCard FILTER_CARD_CREATURE_YOUR_GRAVEYARD = new FilterCreatureCard("creature card from your graveyard");
 
     static {

From 365754b6f746eeddf93f2d53730c01026d48c242 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Mon, 25 May 2020 17:17:17 +0200
Subject: [PATCH 052/586] * Fixed token creation of Shark Typhoon (fixes
 #6572).

---
 Mage.Sets/src/mage/cards/s/SharkTyphoon.java | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/s/SharkTyphoon.java b/Mage.Sets/src/mage/cards/s/SharkTyphoon.java
index 5134684285..8753458c49 100644
--- a/Mage.Sets/src/mage/cards/s/SharkTyphoon.java
+++ b/Mage.Sets/src/mage/cards/s/SharkTyphoon.java
@@ -31,7 +31,7 @@ public final class SharkTyphoon extends CardImpl {
 
         // Whenever you cast a noncreature spell, create an X/X blue Shark creature token with flying, where X is that spell's converted mana cost.
         this.addAbility(new SpellCastControllerTriggeredAbility(
-                new SharkTyphoonCastEffect(), StaticFilters.FILTER_SPELL_A_NON_CREATURE, false
+                new SharkTyphoonCastEffect(), StaticFilters.FILTER_SPELL_A_NON_CREATURE, false, true
         ));
 
         // Cycling {X}{1}{U}
@@ -69,7 +69,7 @@ class SharkTyphoonCastEffect extends OneShotEffect {
 
     @Override
     public boolean apply(Game game, Ability source) {
-        Spell spell = game.getSpell(targetPointer.getFirst(game, source));
+        Spell spell = game.getSpell(getTargetPointer().getFirst(game, source));
         int xValue = 0;
         if (spell != null) {
             xValue = spell.getConvertedManaCost();

From adcd9d84c66f2c7b2fe51f56eb0c70db0ec5a97b Mon Sep 17 00:00:00 2001
From: johnm <johnm@WINDOWS-QR5QIIL.lan>
Date: Mon, 25 May 2020 16:49:12 +0100
Subject: [PATCH 053/586] Single setPlayerName method.

---
 .../src/main/java/mage/client/table/NewPlayerPanel.java     | 6 ------
 .../src/main/java/mage/client/table/TablePlayerPanel.java   | 2 +-
 2 files changed, 1 insertion(+), 7 deletions(-)

diff --git a/Mage.Client/src/main/java/mage/client/table/NewPlayerPanel.java b/Mage.Client/src/main/java/mage/client/table/NewPlayerPanel.java
index c41c5873f7..5394c5640a 100644
--- a/Mage.Client/src/main/java/mage/client/table/NewPlayerPanel.java
+++ b/Mage.Client/src/main/java/mage/client/table/NewPlayerPanel.java
@@ -45,12 +45,6 @@ public class NewPlayerPanel extends javax.swing.JPanel {
         this.txtPlayerName.setEditable(false);
         this.txtPlayerName.setEnabled(false);
     }
-
-    public void setPlayerName(String playerName, boolean editable, boolean enabled) {
-        this.txtPlayerName.setText(playerName);
-        this.txtPlayerName.setEditable(editable);
-        this.txtPlayerName.setEnabled(enabled);
-    }
     
     protected void playerLoadDeck() {
         String lastFolder = MageFrame.getPreferences().get("lastDeckFolder", "");
diff --git a/Mage.Client/src/main/java/mage/client/table/TablePlayerPanel.java b/Mage.Client/src/main/java/mage/client/table/TablePlayerPanel.java
index a736c414ff..2ced51179f 100644
--- a/Mage.Client/src/main/java/mage/client/table/TablePlayerPanel.java
+++ b/Mage.Client/src/main/java/mage/client/table/TablePlayerPanel.java
@@ -126,7 +126,7 @@ public class TablePlayerPanel extends javax.swing.JPanel {
     private void cbPlayerTypeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbPlayerTypeActionPerformed
         if (getPlayerType() != PlayerType.HUMAN) {
             this.newPlayerPanel.setVisible(true);
-            this.newPlayerPanel.setPlayerName("Computer " + this.lblPlayerNum.getText().charAt(this.lblPlayerNum.getText().length() - 1),true,true);
+            this.newPlayerPanel.setPlayerName("Computer " + this.lblPlayerNum.getText().charAt(this.lblPlayerNum.getText().length() - 1));
         } 
         else {
             this.newPlayerPanel.setVisible(false);

From d2b8928e606964dec2f8d4cd37bd322ddeec67cb Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Mon, 25 May 2020 18:09:28 +0200
Subject: [PATCH 054/586] * Fixed a bug that the win method for a player did
 not handle the range correctly and did erroneously end the game while still
 multiple players alive (related to #6553).

---
 .../mage/cards/a/ApproachOfTheSecondSun.java  |  6 +-
 .../mage/test/multiplayer/PlayerWinsTest.java | 64 +++++++++++++++++++
 .../main/java/mage/players/PlayerImpl.java    | 14 ++--
 3 files changed, 76 insertions(+), 8 deletions(-)
 create mode 100644 Mage.Tests/src/test/java/org/mage/test/multiplayer/PlayerWinsTest.java

diff --git a/Mage.Sets/src/mage/cards/a/ApproachOfTheSecondSun.java b/Mage.Sets/src/mage/cards/a/ApproachOfTheSecondSun.java
index ea4a3c9548..f47ae0b081 100644
--- a/Mage.Sets/src/mage/cards/a/ApproachOfTheSecondSun.java
+++ b/Mage.Sets/src/mage/cards/a/ApproachOfTheSecondSun.java
@@ -27,6 +27,8 @@ public final class ApproachOfTheSecondSun extends CardImpl {
     public ApproachOfTheSecondSun(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{6}{W}");
 
+        // If this spell was cast from your hand and you've cast another spell named Approach of the Second Sun this game,
+        // you win the game. Otherwise, put Approach of the Second Sun into its owner's library seventh from the top and you gain 7 life.        
         getSpellAbility().addEffect(new ApproachOfTheSecondSunEffect());
         getSpellAbility().addWatcher(new ApproachOfTheSecondSunWatcher());
     }
@@ -46,7 +48,7 @@ class ApproachOfTheSecondSunEffect extends OneShotEffect {
     public ApproachOfTheSecondSunEffect() {
         super(Outcome.Win);
         this.staticText
-                = "If this spell was cast from your hand and you've cast another spell named Approach of the Second Sun this game, you win the game. "
+                = "If this spell was cast from your hand and you've cast another spell named {this} this game, you win the game. "
                 + "Otherwise, put {this} into its owner's library seventh from the top and you gain 7 life.";
     }
 
@@ -93,7 +95,7 @@ class ApproachOfTheSecondSunEffect extends OneShotEffect {
 
 class ApproachOfTheSecondSunWatcher extends Watcher {
 
-    private Map<UUID, Integer> approachesCast = new HashMap<>();
+    private final Map<UUID, Integer> approachesCast = new HashMap<>();
 
     public ApproachOfTheSecondSunWatcher() {
         super(WatcherScope.GAME);
diff --git a/Mage.Tests/src/test/java/org/mage/test/multiplayer/PlayerWinsTest.java b/Mage.Tests/src/test/java/org/mage/test/multiplayer/PlayerWinsTest.java
new file mode 100644
index 0000000000..e2bea870ea
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/multiplayer/PlayerWinsTest.java
@@ -0,0 +1,64 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.mage.test.multiplayer;
+
+import java.io.FileNotFoundException;
+import mage.constants.MultiplayerAttackOption;
+import mage.constants.PhaseStep;
+import mage.constants.RangeOfInfluence;
+import mage.constants.Zone;
+import mage.game.FreeForAll;
+import mage.game.Game;
+import mage.game.GameException;
+import mage.game.mulligan.MulliganType;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestMultiPlayerBase;
+
+
+/**
+ * @author LevelX2
+ */
+public class PlayerWinsTest extends CardTestMultiPlayerBase {
+
+    @Override
+    protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
+        Game game = new FreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ONE, MulliganType.GAME_DEFAULT.getMulligan(0), 40);
+        // Player order: A -> D -> C -> B
+        playerA = createPlayer(game, playerA, "PlayerA");
+        playerB = createPlayer(game, playerB, "PlayerB");
+        playerC = createPlayer(game, playerC, "PlayerC");
+        playerD = createPlayer(game, playerD, "PlayerD");
+        return game;
+    }
+
+    /**
+     * Tests multiplayer effects Player order: A -> D -> C -> B
+     */
+    @Test
+    public void ApproachOfTheSecondSunTest() {
+
+        // If this spell was cast from your hand and you've cast another spell named Approach of the Second Sun this game,
+        // you win the game. Otherwise, put Approach of the Second Sun into its owner's library seventh from the top and you gain 7 life. 
+        addCard(Zone.HAND, playerA, "Approach of the Second Sun", 2); // Sorcery {6}{W}
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 7);
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Approach of the Second Sun");
+        castSpell(5, PhaseStep.PRECOMBAT_MAIN, playerA, "Approach of the Second Sun");
+
+        setStopAt(5, PhaseStep.POSTCOMBAT_MAIN);
+        execute();
+
+        assertLife(playerA, 47);
+        assertLife(playerC, 40);
+        Assert.assertTrue("Player D has lost the game", !playerD.isInGame());
+        Assert.assertTrue("Player B has lost the game", !playerB.isInGame());
+        Assert.assertTrue("Player C is in the game", playerC.isInGame());
+        Assert.assertTrue("Player A is in the game", playerA.isInGame());
+
+    }
+
+}
diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java
index a4a8667daf..1b745b1056 100644
--- a/Mage/src/main/java/mage/players/PlayerImpl.java
+++ b/Mage/src/main/java/mage/players/PlayerImpl.java
@@ -2526,12 +2526,14 @@ public abstract class PlayerImpl implements Player, Serializable {
                         opponent.lostForced(game);
                     }
                 }
-                // if no more opponents alive, you win and the game ends
+                // if no more opponents alive (independant from range), you win and the game ends
                 int opponentsAlive = 0;
-                for (UUID opponentId : game.getOpponents(playerId)) {
-                    Player opponent = game.getPlayer(opponentId);
-                    if (opponent != null && !opponent.hasLost()) {
-                        opponentsAlive++;
+                for (UUID playerIdToCheck : game.getPlayerList()) {
+                    if (game.isOpponent(this, playerIdToCheck)) { // Check without range
+                        Player opponent = game.getPlayer(playerIdToCheck);
+                        if (opponent != null && !opponent.hasLost()) {
+                            opponentsAlive++;
+                        }
                     }
                 }
                 if (opponentsAlive == 0 && !hasWon()) {
@@ -2541,7 +2543,7 @@ public abstract class PlayerImpl implements Player, Serializable {
                     game.end();
                 }
             } else {
-                logger.debug("player won -> but already lost before: " + this.getName());
+                logger.debug("player won -> but already lost before or other players still alive: " + this.getName());
             }
         }
     }

From 37f7389c175300c7775142ca7e8aac6916aa2478 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Tue, 26 May 2020 11:30:47 +0400
Subject: [PATCH 055/586] * Images: fixed Star Wars download from grabbag
 images source;

---
 .../card/dl/sources/GrabbagImageSource.java       | 15 ++-------------
 1 file changed, 2 insertions(+), 13 deletions(-)

diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GrabbagImageSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GrabbagImageSource.java
index 7cd59d0a21..9d6cebf890 100644
--- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GrabbagImageSource.java
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GrabbagImageSource.java
@@ -18,14 +18,13 @@ public enum GrabbagImageSource implements CardImageSource {
 
     private static final Set<String> supportedSets = new LinkedHashSet<String>() {
         {
-            add("PTC");
             add("SWS");
         }
     };
 
     @Override
     public String getSourceName() {
-        return "";
+        return "Grabbag";
     }
 
     @Override
@@ -76,16 +75,6 @@ public enum GrabbagImageSource implements CardImageSource {
             return;
         }
         singleLinks = new HashMap<>();
-        singleLinks.put("PTC/Arbiter of the Ideal", "MTG/BNG/en/promo/ArbiterOfTheIdeal.jpg");
-        singleLinks.put("PTC/Courser of Kruphix", "MTG/BNG/en/promo/CourserOfKruphix.jpg");
-        singleLinks.put("PTC/Eater of Hope", "MTG/BNG/en/promo/EaterOfHope.jpg");
-        singleLinks.put("PTC/Fated Return", "MTG/BNG/en/promo/FatedReturn.jpg");
-        singleLinks.put("PTC/Forgestoker Dragon", "MTG/BNG/en/promo/ForgestokerDragon.jpg");
-        singleLinks.put("PTC/Nessian Wilds Ravager", "MTG/BNG/en/promo/NessianWildsRavager.jpg");
-        singleLinks.put("PTC/Pain Seer", "MTG/BNG/en/promo/PainSeer.jpg");
-        singleLinks.put("PTC/Silent Sentinel", "MTG/BNG/en/promo/SilentSentinel.jpg");
-        singleLinks.put("PTC/Tromokratis", "MTG/BNG/en/promo/Tromokratis.jpg");
-
         singleLinks.put("SWS/AAT-1", "CqmDY8V.jpg");
         singleLinks.put("SWS/Acklay of the Arena", "ESVRm6F.jpg");
         singleLinks.put("SWS/Acquire Target", "FOskB4q.jpg");
@@ -478,7 +467,7 @@ public enum GrabbagImageSource implements CardImageSource {
         if (card.getSet().equals("MTG")) {
             return "http://static.starcitygames.com/sales/cardscans/";
         } else if (card.getSet().equals("SWS")) {
-            return "http://i.imgur.com/";
+            return "https://i.imgur.com/";
         } else {
             return "http://magiccards.info/scans/en/";
         }

From 3aefbfb360d1a75365f2ac729481ac4296d51bfb Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Tue, 26 May 2020 16:38:01 +0200
Subject: [PATCH 056/586] * Fixed problems with win / lose restrictions in
 relation to empty draw condition (fixes #1681 #6553).

---
 .../mage/cards/j/JaceWielderOfMysteries.java  |   4 +-
 .../src/mage/cards/l/LaboratoryManiac.java    |   4 +-
 .../src/mage/cards/t/TreasureKeeper.java      |   3 +-
 .../mage/test/multiplayer/PlayerWinsTest.java | 100 +++++++++++++++++-
 .../java/org/mage/test/player/TestPlayer.java |   5 -
 .../java/org/mage/test/stub/PlayerStub.java   |   5 -
 .../LookLibraryMayPutToBottomEffect.java      |   2 +-
 .../java/mage/actions/MageDrawAction.java     |   6 --
 Mage/src/main/java/mage/game/GameImpl.java    |   2 +-
 .../main/java/mage/game/events/GameEvent.java |   1 -
 Mage/src/main/java/mage/players/Library.java  |   2 +
 Mage/src/main/java/mage/players/Player.java   |   2 -
 .../main/java/mage/players/PlayerImpl.java    |   5 -
 13 files changed, 107 insertions(+), 34 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/j/JaceWielderOfMysteries.java b/Mage.Sets/src/mage/cards/j/JaceWielderOfMysteries.java
index 2332417ac9..ddfcc1a16e 100644
--- a/Mage.Sets/src/mage/cards/j/JaceWielderOfMysteries.java
+++ b/Mage.Sets/src/mage/cards/j/JaceWielderOfMysteries.java
@@ -90,14 +90,14 @@ class JaceWielderOfMysteriesContinuousEffect extends ReplacementEffectImpl {
 
     @Override
     public boolean checksEventType(GameEvent event, Game game) {
-        return event.getType() == GameEvent.EventType.EMPTY_DRAW;
+        return event.getType() == GameEvent.EventType.DRAW_CARD;
     }
 
     @Override
     public boolean applies(GameEvent event, Ability source, Game game) {
         if (event.getPlayerId().equals(source.getControllerId())) {
             Player player = game.getPlayer(event.getPlayerId());
-            if (player != null && !player.hasLost() && player.isEmptyDraw()) {
+            if (player != null && !player.hasLost() && !player.getLibrary().hasCards()) {
                 return true;
             }
         }
diff --git a/Mage.Sets/src/mage/cards/l/LaboratoryManiac.java b/Mage.Sets/src/mage/cards/l/LaboratoryManiac.java
index c01a0e7681..7c372edebe 100644
--- a/Mage.Sets/src/mage/cards/l/LaboratoryManiac.java
+++ b/Mage.Sets/src/mage/cards/l/LaboratoryManiac.java
@@ -79,14 +79,14 @@ class LaboratoryManiacEffect extends ReplacementEffectImpl {
 
     @Override
     public boolean checksEventType(GameEvent event, Game game) {
-        return event.getType() == EventType.EMPTY_DRAW;
+        return event.getType() == EventType.DRAW_CARD;
     }
 
     @Override
     public boolean applies(GameEvent event, Ability source, Game game) {
         if (event.getPlayerId().equals(source.getControllerId())) {
             Player player = game.getPlayer(event.getPlayerId());
-            if (player != null && !player.hasLost() && player.isEmptyDraw()) {
+            if (player != null && !player.hasLost() && !player.getLibrary().hasCards()) {
                 return true;
             }
         }
diff --git a/Mage.Sets/src/mage/cards/t/TreasureKeeper.java b/Mage.Sets/src/mage/cards/t/TreasureKeeper.java
index 1bd30575c3..a8752827b0 100644
--- a/Mage.Sets/src/mage/cards/t/TreasureKeeper.java
+++ b/Mage.Sets/src/mage/cards/t/TreasureKeeper.java
@@ -63,8 +63,7 @@ class TreasureKeeperEffect extends OneShotEffect {
     public boolean apply(Game game, Ability source) {
         Boolean cardWasCast = false;
         Player controller = game.getPlayer(source.getControllerId());
-        if (controller != null
-                && !controller.getLibrary().isEmptyDraw()) {
+        if (controller != null && controller.getLibrary().hasCards()) {
             CardsImpl toReveal = new CardsImpl();
             Card nonLandCard = null;
             for (Card card : controller.getLibrary().getCards(game)) {
diff --git a/Mage.Tests/src/test/java/org/mage/test/multiplayer/PlayerWinsTest.java b/Mage.Tests/src/test/java/org/mage/test/multiplayer/PlayerWinsTest.java
index e2bea870ea..1a31ecac58 100644
--- a/Mage.Tests/src/test/java/org/mage/test/multiplayer/PlayerWinsTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/multiplayer/PlayerWinsTest.java
@@ -38,6 +38,10 @@ public class PlayerWinsTest extends CardTestMultiPlayerBase {
     /**
      * Tests multiplayer effects Player order: A -> D -> C -> B
      */
+    
+    /**
+     * Test that players out of range do not lose the game if effect from Approach of the Seconnd Sun takes effect.     
+     */
     @Test
     public void ApproachOfTheSecondSunTest() {
 
@@ -54,11 +58,103 @@ public class PlayerWinsTest extends CardTestMultiPlayerBase {
 
         assertLife(playerA, 47);
         assertLife(playerC, 40);
-        Assert.assertTrue("Player D has lost the game", !playerD.isInGame());
-        Assert.assertTrue("Player B has lost the game", !playerB.isInGame());
+        Assert.assertTrue("Player D still alive but should have lost the game", !playerD.isInGame());
+        Assert.assertTrue("Player B still alive but should have lost the game", !playerB.isInGame());
         Assert.assertTrue("Player C is in the game", playerC.isInGame());
         Assert.assertTrue("Player A is in the game", playerA.isInGame());
 
     }
+    
+    /**
+     * Test that players out of range do not lose the game if effect from Laboratory Maniac takes effect.     
+     */
+    @Test
+    public void LaboratoryManiacWinsTest() {       
+        // If you would draw a card while your library has no cards in it, you win the game instead.        
+        addCard(Zone.HAND, playerA, "Laboratory Maniac", 1); // Creature {2}{U}
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 3);
+        
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Laboratory Maniac");        
+        removeAllCardsFromLibrary(playerA);
+        addCard(Zone.LIBRARY, playerA, "Mountain");
+        
+        setStopAt(5, PhaseStep.POSTCOMBAT_MAIN);
+        execute();
 
+        assertPermanentCount(playerA, "Laboratory Maniac", 1);
+        assertHandCount(playerA, "Mountain", 1);
+        assertLibraryCount(playerA, 0);
+        Assert.assertTrue("Player D still alive but should have lost the game", !playerD.isInGame());
+        Assert.assertTrue("Player B still alive but should have lost the game", !playerB.isInGame());
+        Assert.assertTrue("Player C should be in the game but has lost", playerC.isInGame());
+        Assert.assertTrue("Player A should be in the game but has lost", playerA.isInGame());
+    }
+    
+    /**
+     * Test that player can't win while Platinium Angel ist in range.     
+     */
+    @Test
+    public void LaboratoryManiacAndPlatinumAngelInRangeTest() {       
+        // If you would draw a card while your library has no cards in it, you win the game instead.        
+        addCard(Zone.HAND, playerA, "Laboratory Maniac", 1); // Creature {2}{U}
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 3);
+        removeAllCardsFromLibrary(playerA);
+        
+        // You can't lose the game and your opponents can't win the game.
+        addCard(Zone.HAND, playerD, "Platinum Angel", 1); // Creature {2}{U}
+        addCard(Zone.BATTLEFIELD, playerD, "Island", 7);
+        
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Laboratory Maniac");        
+        addCard(Zone.LIBRARY, playerA, "Mountain");
+        
+        castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerD, "Platinum Angel");        
+
+        setStopAt(9, PhaseStep.POSTCOMBAT_MAIN);
+        execute();
+
+        assertPermanentCount(playerA, "Laboratory Maniac", 1);
+        assertHandCount(playerA, "Mountain", 1);
+        assertLibraryCount(playerA, 0);
+        
+        assertPermanentCount(playerD, "Platinum Angel", 1);
+        
+        Assert.assertTrue("Player D should be in the game but has lost", playerD.isInGame());
+        Assert.assertTrue("Player B should be in the game but has lost", playerB.isInGame());
+        Assert.assertTrue("Player C should be in the game but has lost", playerC.isInGame());
+        Assert.assertTrue("Player A should be in the game but has lost", playerA.isInGame());
+    }
+    
+        /**
+     * Test that player can't win while Platinium Angel ist in range.     
+     */
+    @Test
+    public void LaboratoryManiacAndPlatinumAngelFirstOutOfRangeTest() {       
+        // If you would draw a card while your library has no cards in it, you win the game instead.        
+        addCard(Zone.HAND, playerA, "Laboratory Maniac", 1); // Creature {2}{U}
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 3);
+        removeAllCardsFromLibrary(playerA);
+        
+        // You can't lose the game and your opponents can't win the game.
+        addCard(Zone.HAND, playerC, "Platinum Angel", 1); // Creature {2}{U}
+        addCard(Zone.BATTLEFIELD, playerC, "Island", 7);
+        
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Laboratory Maniac");        
+        addCard(Zone.LIBRARY, playerA, "Mountain");
+        
+        castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerC, "Platinum Angel");        
+
+        setStopAt(9, PhaseStep.POSTCOMBAT_MAIN);
+        execute();
+
+        assertPermanentCount(playerA, "Laboratory Maniac", 1);
+        assertHandCount(playerA, "Mountain", 1);
+        assertLibraryCount(playerA, 0);
+        
+        assertPermanentCount(playerC, "Platinum Angel", 1);
+ 
+        Assert.assertTrue("Player D still alive but should have lost the gamet", !playerD.isInGame());
+        Assert.assertTrue("Player B still alive but should have lost the game", !playerB.isInGame());
+        Assert.assertTrue("Player C should be in the game but has lost", playerC.isInGame());
+        Assert.assertTrue("Player A should be in the game but has lost", playerA.isInGame());
+    }
 }
diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
index 4dd31575be..f5bf8f8b2a 100644
--- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
+++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
@@ -2952,11 +2952,6 @@ public class TestPlayer implements Player {
         computerPlayer.pass(game);
     }
 
-    @Override
-    public boolean isEmptyDraw() {
-        return computerPlayer.isEmptyDraw();
-    }
-
     @Override
     public void resetPassed() {
         computerPlayer.resetPassed();
diff --git a/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java b/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java
index a104c25d48..d4b5f701cb 100644
--- a/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java
+++ b/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java
@@ -288,11 +288,6 @@ public class PlayerStub implements Player {
         return false;
     }
 
-    @Override
-    public boolean isEmptyDraw() {
-        return false;
-    }
-
     @Override
     public void pass(Game game) {
 
diff --git a/Mage/src/main/java/mage/abilities/effects/common/LookLibraryMayPutToBottomEffect.java b/Mage/src/main/java/mage/abilities/effects/common/LookLibraryMayPutToBottomEffect.java
index f2d8b038fd..4718bfdcb8 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/LookLibraryMayPutToBottomEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/LookLibraryMayPutToBottomEffect.java
@@ -39,7 +39,7 @@ public class LookLibraryMayPutToBottomEffect extends OneShotEffect {
         if (sourceObject == null || controller == null) {
             return false;
         }
-        if (!controller.getLibrary().isEmptyDraw()) {
+        if (controller.getLibrary().hasCards()) {
             Card card = controller.getLibrary().getFromTop(game);
             if (card == null) {
                 return false;
diff --git a/Mage/src/main/java/mage/actions/MageDrawAction.java b/Mage/src/main/java/mage/actions/MageDrawAction.java
index bf1fe4e922..f40d409b40 100644
--- a/Mage/src/main/java/mage/actions/MageDrawAction.java
+++ b/Mage/src/main/java/mage/actions/MageDrawAction.java
@@ -61,12 +61,6 @@ public class MageDrawAction extends MageAction {
             if (!player.isTopCardRevealed() && numDrawn > 0) {
                 game.fireInformEvent(player.getLogName() + " draws " + CardUtil.numberToText(numDrawn, "a") + " card" + (numDrawn > 1 ? "s" : ""));
             }
-            if (player.isEmptyDraw()) {
-                event = GameEvent.getEvent(GameEvent.EventType.EMPTY_DRAW, player.getId(), player.getId());
-                if (!game.replaceEvent(event)) {
-                    game.doAction(new MageLoseGameAction(player, MageLoseGameAction.DRAW_REASON), sourceId);
-                }
-            }
 
             setScore(player, score);
             game.setStateCheckRequired();
diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java
index 018559d14f..d0930ad3f0 100644
--- a/Mage/src/main/java/mage/game/GameImpl.java
+++ b/Mage/src/main/java/mage/game/GameImpl.java
@@ -1848,7 +1848,7 @@ public abstract class GameImpl implements Game, Serializable {
         for (Player player : state.getPlayers().values()) {
             if (!player.hasLost()
                     && ((player.getLife() <= 0 && player.canLoseByZeroOrLessLife())
-                    || player.isEmptyDraw()
+                    || player.getLibrary().isEmptyDraw()
                     || player.getCounters().getCount(CounterType.POISON) >= 10)) {
                 player.lost(this);
             }
diff --git a/Mage/src/main/java/mage/game/events/GameEvent.java b/Mage/src/main/java/mage/game/events/GameEvent.java
index 7c467e3b40..d95756bc0d 100644
--- a/Mage/src/main/java/mage/game/events/GameEvent.java
+++ b/Mage/src/main/java/mage/game/events/GameEvent.java
@@ -68,7 +68,6 @@ public class GameEvent implements Serializable {
          */
         ZONE_CHANGE,
         ZONE_CHANGE_GROUP,
-        EMPTY_DRAW,
         DRAW_CARDS, // applies to an instruction to draw more than one card before any replacement effects apply to individual cards drawn
         DRAW_CARD, DREW_CARD,
         EXPLORED,
diff --git a/Mage/src/main/java/mage/players/Library.java b/Mage/src/main/java/mage/players/Library.java
index 82323073e8..5ff04b98e9 100644
--- a/Mage/src/main/java/mage/players/Library.java
+++ b/Mage/src/main/java/mage/players/Library.java
@@ -239,6 +239,8 @@ public class Library implements Serializable {
 
     /**
      * Tests only -- find card position in library
+     * @param cardId
+     * @return 
      */
     public int getCardPosition(UUID cardId) {
         UUID[] list = library.toArray(new UUID[0]);
diff --git a/Mage/src/main/java/mage/players/Player.java b/Mage/src/main/java/mage/players/Player.java
index 51f7536137..8df2d477a3 100644
--- a/Mage/src/main/java/mage/players/Player.java
+++ b/Mage/src/main/java/mage/players/Player.java
@@ -152,8 +152,6 @@ public interface Player extends MageItem, Copyable<Player> {
 
     boolean isPassed();
 
-    boolean isEmptyDraw();
-
     void pass(Game game);
 
     void resetPassed();
diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java
index 1b745b1056..f79bc6d165 100644
--- a/Mage/src/main/java/mage/players/PlayerImpl.java
+++ b/Mage/src/main/java/mage/players/PlayerImpl.java
@@ -2338,11 +2338,6 @@ public abstract class PlayerImpl implements Player, Serializable {
         resetStoredBookmark(game);
     }
 
-    @Override
-    public boolean isEmptyDraw() {
-        return library.isEmptyDraw();
-    }
-
     @Override
     public void resetPassed() {
         this.passed = this.loses || this.hasLeft();

From 5e63c44c5d19672723b73c0751ac7540ddee0776 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Tue, 26 May 2020 19:29:46 +0200
Subject: [PATCH 057/586] * Abondon Hope - Fixed rule text (#6532).

---
 Mage.Sets/src/mage/cards/a/AbandonHope.java                  | 5 +++--
 .../common/discard/DiscardCardYouChooseTargetEffect.java     | 4 ++--
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/a/AbandonHope.java b/Mage.Sets/src/mage/cards/a/AbandonHope.java
index fab2833581..3c3e6e538c 100644
--- a/Mage.Sets/src/mage/cards/a/AbandonHope.java
+++ b/Mage.Sets/src/mage/cards/a/AbandonHope.java
@@ -35,8 +35,9 @@ public final class AbandonHope extends CardImpl {
         this.addAbility(ability);
 
         // Look at target opponent's hand and choose X cards from it. That player discards those cards.
-        ManacostVariableValue manaX = ManacostVariableValue.instance;
-        this.getSpellAbility().addEffect(new DiscardCardYouChooseTargetEffect(manaX, TargetController.ANY));
+        this.getSpellAbility().addEffect(
+                new DiscardCardYouChooseTargetEffect(ManacostVariableValue.instance, TargetController.ANY)
+                .setText("Look at target opponent's hand and choose X cards from it. That player discards those cards"));
         this.getSpellAbility().addTarget(new TargetOpponent());
         this.getSpellAbility().setCostAdjuster(AbandonHopeAdjuster.instance);
     }
diff --git a/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardCardYouChooseTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardCardYouChooseTargetEffect.java
index 403fde557a..4ba0596c82 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardCardYouChooseTargetEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/discard/DiscardCardYouChooseTargetEffect.java
@@ -33,7 +33,7 @@ public class DiscardCardYouChooseTargetEffect extends OneShotEffect {
     private static final FilterCard filterOneCard = new FilterCard("one card");
 
     public DiscardCardYouChooseTargetEffect() {
-        this(new FilterCard("a card"));
+        this(StaticFilters.FILTER_CARD_A);
     }
 
     public DiscardCardYouChooseTargetEffect(TargetController targetController) {
@@ -170,7 +170,7 @@ public class DiscardCardYouChooseTargetEffect extends OneShotEffect {
         } else {
             if (numberCardsToReveal instanceof StaticValue) {
                 sb.append(" reveals ");
-                sb.append(CardUtil.numberToText(((StaticValue) numberCardsToReveal).getValue()) + " cards");
+                sb.append(CardUtil.numberToText(((StaticValue) numberCardsToReveal).getValue())).append(" cards");
                 sb.append(" from their hand");
             } else {
                 sb.append(" reveals a number of cards from their hand equal to ");

From a18951c27aab8b5b29f7a9fdf5860bbaba6f1bda Mon Sep 17 00:00:00 2001
From: John Hitchings <hitch17@gmail.com>
Date: Tue, 26 May 2020 14:23:22 -0700
Subject: [PATCH 058/586] Add Vintage Cube 2020. See
 https://magic.wizards.com/en/articles/archive/vintage-cube-cardlist

---
 .../cubes/VintageCubeApril2020.java           | 552 ++++++++++++++++++
 Mage.Server/config/config.xml                 |   1 +
 Mage.Server/release/config/config.xml         |   1 +
 3 files changed, 554 insertions(+)
 create mode 100644 Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/VintageCubeApril2020.java

diff --git a/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/VintageCubeApril2020.java b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/VintageCubeApril2020.java
new file mode 100644
index 0000000000..762f392292
--- /dev/null
+++ b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/VintageCubeApril2020.java
@@ -0,0 +1,552 @@
+package mage.tournament.cubes;
+
+import mage.game.draft.DraftCube;
+
+public class VintageCubeApril2020 extends DraftCube {
+
+    public VintageCubeApril2020() {
+        super("MTGO Vintage Cube April 2020");
+
+        cubeCards.add(new CardIdentity("Kytheon, Hero of Akros", ""));
+        cubeCards.add(new CardIdentity("Mother of Runes", ""));
+        cubeCards.add(new CardIdentity("Student of Warfare", ""));
+        cubeCards.add(new CardIdentity("Adanto Vanguard", ""));
+        cubeCards.add(new CardIdentity("Containment Priest", ""));
+        cubeCards.add(new CardIdentity("Leonin Relic-Warder", ""));
+        cubeCards.add(new CardIdentity("Porcelain Legionnaire", ""));
+        cubeCards.add(new CardIdentity("Selfless Spirit", ""));
+        cubeCards.add(new CardIdentity("Soulfire Grand Master", ""));
+        cubeCards.add(new CardIdentity("Stoneforge Mystic", ""));
+        cubeCards.add(new CardIdentity("Thalia, Guardian of Thraben", ""));
+        cubeCards.add(new CardIdentity("Tithe Taker", ""));
+        cubeCards.add(new CardIdentity("Wall of Omens", ""));
+        cubeCards.add(new CardIdentity("Blade Splicer", ""));
+        cubeCards.add(new CardIdentity("Brightling", ""));
+        cubeCards.add(new CardIdentity("Brimaz, King of Oreskos", ""));
+        cubeCards.add(new CardIdentity("Fairgrounds Warden", ""));
+        cubeCards.add(new CardIdentity("Flickerwisp", ""));
+        cubeCards.add(new CardIdentity("Monastery Mentor", ""));
+        cubeCards.add(new CardIdentity("Recruiter of the Guard", ""));
+        cubeCards.add(new CardIdentity("Silverblade Paladin", ""));
+        cubeCards.add(new CardIdentity("Emeria Angel", ""));
+        cubeCards.add(new CardIdentity("Hero of Bladehold", ""));
+        cubeCards.add(new CardIdentity("Linvala, Keeper of Silence", ""));
+        cubeCards.add(new CardIdentity("Restoration Angel", ""));
+        cubeCards.add(new CardIdentity("Angel of Invention", ""));
+        cubeCards.add(new CardIdentity("Elspeth Conquers Death", ""));
+        cubeCards.add(new CardIdentity("Archangel Avacyn", ""));
+        cubeCards.add(new CardIdentity("Baneslayer Angel", ""));
+        cubeCards.add(new CardIdentity("Lyra Dawnbringer", ""));
+        cubeCards.add(new CardIdentity("Reveillark", ""));
+        cubeCards.add(new CardIdentity("Sun Titan", ""));
+        cubeCards.add(new CardIdentity("Angel of Serenity", ""));
+        cubeCards.add(new CardIdentity("Elesh Norn, Grand Cenobite", ""));
+        cubeCards.add(new CardIdentity("Iona, Shield of Emeria", ""));
+        cubeCards.add(new CardIdentity("Gideon Blackblade", ""));
+        cubeCards.add(new CardIdentity("Elspeth, Sun's Nemesis", ""));
+        cubeCards.add(new CardIdentity("Gideon, Ally of Zendikar", ""));
+        cubeCards.add(new CardIdentity("Gideon Jura", ""));
+        cubeCards.add(new CardIdentity("Elspeth, Sun's Champion", ""));
+        cubeCards.add(new CardIdentity("Condemn", ""));
+        cubeCards.add(new CardIdentity("Enlightened Tutor", ""));
+        cubeCards.add(new CardIdentity("Mana Tithe", ""));
+        cubeCards.add(new CardIdentity("Path to Exile", ""));
+        cubeCards.add(new CardIdentity("Swords to Plowshares", ""));
+        cubeCards.add(new CardIdentity("Disenchant", ""));
+        cubeCards.add(new CardIdentity("Unexpectedly Absent", ""));
+        cubeCards.add(new CardIdentity("Balance", ""));
+        cubeCards.add(new CardIdentity("Council's Judgment", ""));
+        cubeCards.add(new CardIdentity("Spectral Procession", ""));
+        cubeCards.add(new CardIdentity("Armageddon", ""));
+        cubeCards.add(new CardIdentity("Day of Judgment", ""));
+        cubeCards.add(new CardIdentity("Ravages of War", ""));
+        cubeCards.add(new CardIdentity("Wrath of God", ""));
+        cubeCards.add(new CardIdentity("Terminus", ""));
+        cubeCards.add(new CardIdentity("Land Tax", ""));
+        cubeCards.add(new CardIdentity("Legion's Landing", ""));
+        cubeCards.add(new CardIdentity("Honor of the Pure", ""));
+        cubeCards.add(new CardIdentity("Banishing Light", ""));
+        cubeCards.add(new CardIdentity("Oblivion Ring", ""));
+        cubeCards.add(new CardIdentity("Faith's Fetters", ""));
+        cubeCards.add(new CardIdentity("Moat", ""));
+        cubeCards.add(new CardIdentity("Parallax Wave", ""));
+        cubeCards.add(new CardIdentity("Spear of Heliod", ""));
+        cubeCards.add(new CardIdentity("Karakas", ""));
+        cubeCards.add(new CardIdentity("Thassa's Oracle", ""));
+        cubeCards.add(new CardIdentity("Baral, Chief of Compliance", ""));
+        cubeCards.add(new CardIdentity("Jace, Vryn's Prodigy", ""));
+        cubeCards.add(new CardIdentity("Looter il-Kor", ""));
+        cubeCards.add(new CardIdentity("Phantasmal Image", ""));
+        cubeCards.add(new CardIdentity("Snapcaster Mage", ""));
+        cubeCards.add(new CardIdentity("Thing in the Ice", ""));
+        cubeCards.add(new CardIdentity("Arcane Artisan", ""));
+        cubeCards.add(new CardIdentity("Deceiver Exarch", ""));
+        cubeCards.add(new CardIdentity("Pestermite", ""));
+        cubeCards.add(new CardIdentity("Spellseeker", ""));
+        cubeCards.add(new CardIdentity("Trinket Mage", ""));
+        cubeCards.add(new CardIdentity("Vendilion Clique", ""));
+        cubeCards.add(new CardIdentity("Glen Elendra Archmage", ""));
+        cubeCards.add(new CardIdentity("Phyrexian Metamorph", ""));
+        cubeCards.add(new CardIdentity("Sower of Temptation", ""));
+        cubeCards.add(new CardIdentity("Venser, Shaper Savant", ""));
+        cubeCards.add(new CardIdentity("Mulldrifter", ""));
+        cubeCards.add(new CardIdentity("Riftwing Cloudskate", ""));
+        cubeCards.add(new CardIdentity("Consecrated Sphinx", ""));
+        cubeCards.add(new CardIdentity("Frost Titan", ""));
+        cubeCards.add(new CardIdentity("Torrential Gearhulk", ""));
+        cubeCards.add(new CardIdentity("Palinchron", ""));
+        cubeCards.add(new CardIdentity("Inkwell Leviathan", ""));
+        cubeCards.add(new CardIdentity("Jace Beleren", ""));
+        cubeCards.add(new CardIdentity("Jace, the Mind Sculptor", ""));
+        cubeCards.add(new CardIdentity("Tezzeret the Seeker", ""));
+        cubeCards.add(new CardIdentity("Ancestral Recall", ""));
+        cubeCards.add(new CardIdentity("Brainstorm", ""));
+        cubeCards.add(new CardIdentity("High Tide", ""));
+        cubeCards.add(new CardIdentity("Mystical Tutor", ""));
+        cubeCards.add(new CardIdentity("Spell Pierce", ""));
+        cubeCards.add(new CardIdentity("Brain Freeze", ""));
+        cubeCards.add(new CardIdentity("Counterspell", ""));
+        cubeCards.add(new CardIdentity("Daze", ""));
+        cubeCards.add(new CardIdentity("Impulse", ""));
+        cubeCards.add(new CardIdentity("Mana Drain", ""));
+        cubeCards.add(new CardIdentity("Mana Leak", ""));
+        cubeCards.add(new CardIdentity("Miscalculation", ""));
+        cubeCards.add(new CardIdentity("Remand", ""));
+        cubeCards.add(new CardIdentity("Frantic Search", ""));
+        cubeCards.add(new CardIdentity("Thirst for Knowledge", ""));
+        cubeCards.add(new CardIdentity("Cryptic Command", ""));
+        cubeCards.add(new CardIdentity("Fact or Fiction", ""));
+        cubeCards.add(new CardIdentity("Gifts Ungiven", ""));
+        cubeCards.add(new CardIdentity("Turnabout", ""));
+        cubeCards.add(new CardIdentity("Force of Will", ""));
+        cubeCards.add(new CardIdentity("Gush", ""));
+        cubeCards.add(new CardIdentity("Mystic Confluence", ""));
+        cubeCards.add(new CardIdentity("Repeal", ""));
+        cubeCards.add(new CardIdentity("Dig Through Time", ""));
+        cubeCards.add(new CardIdentity("Ancestral Vision", ""));
+        cubeCards.add(new CardIdentity("Gitaxian Probe", ""));
+        cubeCards.add(new CardIdentity("Ponder", ""));
+        cubeCards.add(new CardIdentity("Preordain", ""));
+        cubeCards.add(new CardIdentity("Chart a Course", ""));
+        cubeCards.add(new CardIdentity("Time Walk", ""));
+        cubeCards.add(new CardIdentity("Show and Tell", ""));
+        cubeCards.add(new CardIdentity("Timetwister", ""));
+        cubeCards.add(new CardIdentity("Tinker", ""));
+        cubeCards.add(new CardIdentity("Bribery", ""));
+        cubeCards.add(new CardIdentity("Time Warp", ""));
+        cubeCards.add(new CardIdentity("Mind's Desire", ""));
+        cubeCards.add(new CardIdentity("Time Spiral", ""));
+        cubeCards.add(new CardIdentity("Upheaval", ""));
+        cubeCards.add(new CardIdentity("Treasure Cruise", ""));
+        cubeCards.add(new CardIdentity("Search for Azcanta", ""));
+        cubeCards.add(new CardIdentity("Control Magic", ""));
+        cubeCards.add(new CardIdentity("Opposition", ""));
+        cubeCards.add(new CardIdentity("Treachery", ""));
+        cubeCards.add(new CardIdentity("Shelldock Isle", ""));
+        cubeCards.add(new CardIdentity("Tolarian Academy", ""));
+        cubeCards.add(new CardIdentity("Putrid Imp", ""));
+        cubeCards.add(new CardIdentity("Dark Confidant", ""));
+        cubeCards.add(new CardIdentity("Kitesail Freebooter", ""));
+        cubeCards.add(new CardIdentity("Mesmeric Fiend", ""));
+        cubeCards.add(new CardIdentity("Oona's Prowler", ""));
+        cubeCards.add(new CardIdentity("Pack Rat", ""));
+        cubeCards.add(new CardIdentity("Vampire Hexmage", ""));
+        cubeCards.add(new CardIdentity("Bone Shredder", ""));
+        cubeCards.add(new CardIdentity("Hypnotic Specter", ""));
+        cubeCards.add(new CardIdentity("Ophiomancer", ""));
+        cubeCards.add(new CardIdentity("Plaguecrafter", ""));
+        cubeCards.add(new CardIdentity("Vampire Nighthawk", ""));
+        cubeCards.add(new CardIdentity("Gonti, Lord of Luxury", ""));
+        cubeCards.add(new CardIdentity("Nekrataal", ""));
+        cubeCards.add(new CardIdentity("Ravenous Chupacabra", ""));
+        cubeCards.add(new CardIdentity("Shriekmaw", ""));
+        cubeCards.add(new CardIdentity("Grave Titan", ""));
+        cubeCards.add(new CardIdentity("Ink-Eyes, Servant of Oni", ""));
+        cubeCards.add(new CardIdentity("Massacre Wurm", ""));
+        cubeCards.add(new CardIdentity("Tasigur, the Golden Fang", ""));
+        cubeCards.add(new CardIdentity("Sheoldred, Whispering One", ""));
+        cubeCards.add(new CardIdentity("Griselbrand", ""));
+        cubeCards.add(new CardIdentity("Liliana of the Veil", ""));
+        cubeCards.add(new CardIdentity("Liliana, Death's Majesty", ""));
+        cubeCards.add(new CardIdentity("Dark Ritual", ""));
+        cubeCards.add(new CardIdentity("Entomb", ""));
+        cubeCards.add(new CardIdentity("Fatal Push", ""));
+        cubeCards.add(new CardIdentity("Vampiric Tutor", ""));
+        cubeCards.add(new CardIdentity("Cabal Ritual", ""));
+        cubeCards.add(new CardIdentity("Go for the Throat", ""));
+        cubeCards.add(new CardIdentity("Liliana's Triumph", ""));
+        cubeCards.add(new CardIdentity("Shallow Grave", ""));
+        cubeCards.add(new CardIdentity("Ultimate Price", ""));
+        cubeCards.add(new CardIdentity("Corpse Dance", ""));
+        cubeCards.add(new CardIdentity("Dismember", ""));
+        cubeCards.add(new CardIdentity("Hero's Downfall", ""));
+        cubeCards.add(new CardIdentity("Makeshift Mannequin", ""));
+        cubeCards.add(new CardIdentity("Duress", ""));
+        cubeCards.add(new CardIdentity("Imperial Seal", ""));
+        cubeCards.add(new CardIdentity("Inquisition of Kozilek", ""));
+        cubeCards.add(new CardIdentity("Reanimate", ""));
+        cubeCards.add(new CardIdentity("Thoughtseize", ""));
+        cubeCards.add(new CardIdentity("Collective Brutality", ""));
+        cubeCards.add(new CardIdentity("Demonic Tutor", ""));
+        cubeCards.add(new CardIdentity("Exhume", ""));
+        cubeCards.add(new CardIdentity("Hymn to Tourach", ""));
+        cubeCards.add(new CardIdentity("Night's Whisper", ""));
+        cubeCards.add(new CardIdentity("Buried Alive", ""));
+        cubeCards.add(new CardIdentity("Toxic Deluge", ""));
+        cubeCards.add(new CardIdentity("Yawgmoth's Will", ""));
+        cubeCards.add(new CardIdentity("Damnation", ""));
+        cubeCards.add(new CardIdentity("Languish", ""));
+        cubeCards.add(new CardIdentity("Mastermind's Acquisition", ""));
+        cubeCards.add(new CardIdentity("Tendrils of Agony", ""));
+        cubeCards.add(new CardIdentity("Dark Petition", ""));
+        cubeCards.add(new CardIdentity("Living Death", ""));
+        cubeCards.add(new CardIdentity("Mind Twist", ""));
+        cubeCards.add(new CardIdentity("Animate Dead", ""));
+        cubeCards.add(new CardIdentity("Bitterblossom", ""));
+        cubeCards.add(new CardIdentity("Necromancy", ""));
+        cubeCards.add(new CardIdentity("Phyrexian Arena", ""));
+        cubeCards.add(new CardIdentity("Recurring Nightmare", ""));
+        cubeCards.add(new CardIdentity("Yawgmoth's Bargain", ""));
+        cubeCards.add(new CardIdentity("Goblin Guide", ""));
+        cubeCards.add(new CardIdentity("Goblin Welder", ""));
+        cubeCards.add(new CardIdentity("Grim Lavamancer", ""));
+        cubeCards.add(new CardIdentity("Jackal Pup", ""));
+        cubeCards.add(new CardIdentity("Monastery Swiftspear", ""));
+        cubeCards.add(new CardIdentity("Zurgo Bellstriker", ""));
+        cubeCards.add(new CardIdentity("Abbot of Keral Keep", ""));
+        cubeCards.add(new CardIdentity("Dire Fleet Daredevil", ""));
+        cubeCards.add(new CardIdentity("Eidolon of the Great Revel", ""));
+        cubeCards.add(new CardIdentity("Runaway Steam-Kin", ""));
+        cubeCards.add(new CardIdentity("Young Pyromancer", ""));
+        cubeCards.add(new CardIdentity("Goblin Rabblemaster", ""));
+        cubeCards.add(new CardIdentity("Imperial Recruiter", ""));
+        cubeCards.add(new CardIdentity("Magus of the Moon", ""));
+        cubeCards.add(new CardIdentity("Avalanche Riders", ""));
+        cubeCards.add(new CardIdentity("Flametongue Kavu", ""));
+        cubeCards.add(new CardIdentity("Hazoret the Fervent", ""));
+        cubeCards.add(new CardIdentity("Hellrider", ""));
+        cubeCards.add(new CardIdentity("Pia and Kiran Nalaar", ""));
+        cubeCards.add(new CardIdentity("Rekindling Phoenix", ""));
+        cubeCards.add(new CardIdentity("Glorybringer", ""));
+        cubeCards.add(new CardIdentity("Goblin Dark-Dwellers", ""));
+        cubeCards.add(new CardIdentity("Kiki-Jiki, Mirror Breaker", ""));
+        cubeCards.add(new CardIdentity("Siege-Gang Commander", ""));
+        cubeCards.add(new CardIdentity("Thundermaw Hellkite", ""));
+        cubeCards.add(new CardIdentity("Zealous Conscripts", ""));
+        cubeCards.add(new CardIdentity("Inferno Titan", ""));
+        cubeCards.add(new CardIdentity("Chandra, Torch of Defiance", ""));
+        cubeCards.add(new CardIdentity("Daretti, Scrap Savant", ""));
+        cubeCards.add(new CardIdentity("Koth of the Hammer", ""));
+        cubeCards.add(new CardIdentity("Burst Lightning", ""));
+        cubeCards.add(new CardIdentity("Lightning Bolt", ""));
+        cubeCards.add(new CardIdentity("Abrade", ""));
+        cubeCards.add(new CardIdentity("Ancient Grudge", ""));
+        cubeCards.add(new CardIdentity("Desperate Ritual", ""));
+        cubeCards.add(new CardIdentity("Fire // Ice", ""));
+        cubeCards.add(new CardIdentity("Incinerate", ""));
+        cubeCards.add(new CardIdentity("Lightning Strike", ""));
+        cubeCards.add(new CardIdentity("Pyretic Ritual", ""));
+        cubeCards.add(new CardIdentity("Char", ""));
+        cubeCards.add(new CardIdentity("Seething Song", ""));
+        cubeCards.add(new CardIdentity("Through the Breach", ""));
+        cubeCards.add(new CardIdentity("Fireblast", ""));
+        cubeCards.add(new CardIdentity("Chain Lightning", ""));
+        cubeCards.add(new CardIdentity("Faithless Looting", ""));
+        cubeCards.add(new CardIdentity("Firebolt", ""));
+        cubeCards.add(new CardIdentity("Flame Slash", ""));
+        cubeCards.add(new CardIdentity("Mizzium Mortars", ""));
+        cubeCards.add(new CardIdentity("Pyroclasm", ""));
+        cubeCards.add(new CardIdentity("Light Up the Stage", ""));
+        cubeCards.add(new CardIdentity("Underworld Breach", ""));
+        cubeCards.add(new CardIdentity("Wheel of Fortune", ""));
+        cubeCards.add(new CardIdentity("Empty the Warrens", ""));
+        cubeCards.add(new CardIdentity("Fiery Confluence", ""));
+        cubeCards.add(new CardIdentity("Past in Flames", ""));
+        cubeCards.add(new CardIdentity("Banefire", ""));
+        cubeCards.add(new CardIdentity("Burning of Xinye", ""));
+        cubeCards.add(new CardIdentity("Wildfire", ""));
+        cubeCards.add(new CardIdentity("Bonfire of the Damned", ""));
+        cubeCards.add(new CardIdentity("Mana Flare", ""));
+        cubeCards.add(new CardIdentity("Sulfuric Vortex", ""));
+        cubeCards.add(new CardIdentity("Sneak Attack", ""));
+        cubeCards.add(new CardIdentity("Splinter Twin", ""));
+        cubeCards.add(new CardIdentity("Arbor Elf", ""));
+        cubeCards.add(new CardIdentity("Avacyn's Pilgrim", ""));
+        cubeCards.add(new CardIdentity("Birds of Paradise", ""));
+        cubeCards.add(new CardIdentity("Elves of Deep Shadow", ""));
+        cubeCards.add(new CardIdentity("Elvish Mystic", ""));
+        cubeCards.add(new CardIdentity("Fyndhorn Elves", ""));
+        cubeCards.add(new CardIdentity("Joraga Treespeaker", ""));
+        cubeCards.add(new CardIdentity("Llanowar Elves", ""));
+        cubeCards.add(new CardIdentity("Noble Hierarch", ""));
+        cubeCards.add(new CardIdentity("Den Protector", ""));
+        cubeCards.add(new CardIdentity("Devoted Druid", ""));
+        cubeCards.add(new CardIdentity("Fauna Shaman", ""));
+        cubeCards.add(new CardIdentity("Gilded Goose", ""));
+        cubeCards.add(new CardIdentity("Lotus Cobra", ""));
+        cubeCards.add(new CardIdentity("Rofellos, Llanowar Emissary", ""));
+        cubeCards.add(new CardIdentity("Sakura-Tribe Elder", ""));
+        cubeCards.add(new CardIdentity("Scavenging Ooze", ""));
+        cubeCards.add(new CardIdentity("Sylvan Caryatid", ""));
+        cubeCards.add(new CardIdentity("Wall of Blossoms", ""));
+        cubeCards.add(new CardIdentity("Wall of Roots", ""));
+        cubeCards.add(new CardIdentity("Courser of Kruphix", ""));
+        cubeCards.add(new CardIdentity("Eternal Witness", ""));
+        cubeCards.add(new CardIdentity("Ramunap Excavator", ""));
+        cubeCards.add(new CardIdentity("Reclamation Sage", ""));
+        cubeCards.add(new CardIdentity("Tireless Tracker", ""));
+        cubeCards.add(new CardIdentity("Yavimaya Elder", ""));
+        cubeCards.add(new CardIdentity("Master of the Wild Hunt", ""));
+        cubeCards.add(new CardIdentity("Oracle of Mul Daya", ""));
+        cubeCards.add(new CardIdentity("Polukranos, World Eater", ""));
+        cubeCards.add(new CardIdentity("Acidic Slime", ""));
+        cubeCards.add(new CardIdentity("Biogenic Ooze", ""));
+        cubeCards.add(new CardIdentity("Deranged Hermit", ""));
+        cubeCards.add(new CardIdentity("Thragtusk", ""));
+        cubeCards.add(new CardIdentity("Whisperwood Elemental", ""));
+        cubeCards.add(new CardIdentity("Carnage Tyrant", ""));
+        cubeCards.add(new CardIdentity("Primeval Titan", ""));
+        cubeCards.add(new CardIdentity("Avenger of Zendikar", ""));
+        cubeCards.add(new CardIdentity("Craterhoof Behemoth", ""));
+        cubeCards.add(new CardIdentity("Terastodon", ""));
+        cubeCards.add(new CardIdentity("Woodfall Primus", ""));
+        cubeCards.add(new CardIdentity("Dryad of the Ilysian Grove", ""));
+        cubeCards.add(new CardIdentity("Garruk Relentless", ""));
+        cubeCards.add(new CardIdentity("Garruk Wildspeaker", ""));
+        cubeCards.add(new CardIdentity("Garruk, Primal Hunter", ""));
+        cubeCards.add(new CardIdentity("Vivien Reid", ""));
+        cubeCards.add(new CardIdentity("Nature's Claim", ""));
+        cubeCards.add(new CardIdentity("Beast Within", ""));
+        cubeCards.add(new CardIdentity("Channel", ""));
+        cubeCards.add(new CardIdentity("Regrowth", ""));
+        cubeCards.add(new CardIdentity("Kodama's Reach", ""));
+        cubeCards.add(new CardIdentity("Search for Tomorrow", ""));
+        cubeCards.add(new CardIdentity("Eureka", ""));
+        cubeCards.add(new CardIdentity("Harmonize", ""));
+        cubeCards.add(new CardIdentity("Natural Order", ""));
+        cubeCards.add(new CardIdentity("Plow Under", ""));
+        cubeCards.add(new CardIdentity("Primal Command", ""));
+        cubeCards.add(new CardIdentity("Green Sun's Zenith", ""));
+        cubeCards.add(new CardIdentity("Finale of Devastation", ""));
+        cubeCards.add(new CardIdentity("Tooth and Nail", ""));
+        cubeCards.add(new CardIdentity("Fastbond", ""));
+        cubeCards.add(new CardIdentity("Oath of Druids", ""));
+        cubeCards.add(new CardIdentity("Survival of the Fittest", ""));
+        cubeCards.add(new CardIdentity("Sylvan Library", ""));
+        cubeCards.add(new CardIdentity("Heartbeat of Spring", ""));
+        cubeCards.add(new CardIdentity("Wilderness Reclamation", ""));
+        cubeCards.add(new CardIdentity("Gaea's Cradle", ""));
+        cubeCards.add(new CardIdentity("Geist of Saint Traft", ""));
+        cubeCards.add(new CardIdentity("Teferi, Hero of Dominaria", ""));
+        cubeCards.add(new CardIdentity("Sphinx's Revelation", ""));
+        cubeCards.add(new CardIdentity("Fractured Identity", ""));
+        cubeCards.add(new CardIdentity("Celestial Colonnade", ""));
+        cubeCards.add(new CardIdentity("Flooded Strand", ""));
+        cubeCards.add(new CardIdentity("Hallowed Fountain", ""));
+        cubeCards.add(new CardIdentity("Seachrome Coast", ""));
+        cubeCards.add(new CardIdentity("Tundra", ""));
+        cubeCards.add(new CardIdentity("Thief of Sanity", ""));
+        cubeCards.add(new CardIdentity("The Scarab God", ""));
+        cubeCards.add(new CardIdentity("Ashiok, Nightmare Weaver", ""));
+        cubeCards.add(new CardIdentity("Baleful Strix", ""));
+        cubeCards.add(new CardIdentity("Creeping Tar Pit", ""));
+        cubeCards.add(new CardIdentity("Darkslick Shores", ""));
+        cubeCards.add(new CardIdentity("Polluted Delta", ""));
+        cubeCards.add(new CardIdentity("Underground Sea", ""));
+        cubeCards.add(new CardIdentity("Watery Grave", ""));
+        cubeCards.add(new CardIdentity("Daretti, Ingenious Iconoclast", ""));
+        cubeCards.add(new CardIdentity("Kroxa, Titan of Death's Hunger", ""));
+        cubeCards.add(new CardIdentity("Kolaghan's Command", ""));
+        cubeCards.add(new CardIdentity("Rakdos's Return", ""));
+        cubeCards.add(new CardIdentity("Badlands", ""));
+        cubeCards.add(new CardIdentity("Blackcleave Cliffs", ""));
+        cubeCards.add(new CardIdentity("Blood Crypt", ""));
+        cubeCards.add(new CardIdentity("Bloodstained Mire", ""));
+        cubeCards.add(new CardIdentity("Lavaclaw Reaches", ""));
+        cubeCards.add(new CardIdentity("Bloodbraid Elf", ""));
+        cubeCards.add(new CardIdentity("Huntmaster of the Fells", ""));
+        cubeCards.add(new CardIdentity("Dragonlord Atarka", ""));
+        cubeCards.add(new CardIdentity("Manamorphose", ""));
+        cubeCards.add(new CardIdentity("Copperline Gorge", ""));
+        cubeCards.add(new CardIdentity("Raging Ravine", ""));
+        cubeCards.add(new CardIdentity("Stomping Ground", ""));
+        cubeCards.add(new CardIdentity("Taiga", ""));
+        cubeCards.add(new CardIdentity("Wooded Foothills", ""));
+        cubeCards.add(new CardIdentity("Kitchen Finks", ""));
+        cubeCards.add(new CardIdentity("Knight of Autumn", ""));
+        cubeCards.add(new CardIdentity("Knight of the Reliquary", ""));
+        cubeCards.add(new CardIdentity("Trostani Discordant", ""));
+        cubeCards.add(new CardIdentity("Mirari's Wake", ""));
+        cubeCards.add(new CardIdentity("Razorverge Thicket", ""));
+        cubeCards.add(new CardIdentity("Savannah", ""));
+        cubeCards.add(new CardIdentity("Stirring Wildwood", ""));
+        cubeCards.add(new CardIdentity("Temple Garden", ""));
+        cubeCards.add(new CardIdentity("Windswept Heath", ""));
+        cubeCards.add(new CardIdentity("Ashen Rider", ""));
+        cubeCards.add(new CardIdentity("Kaya, Orzhov Usurper", ""));
+        cubeCards.add(new CardIdentity("Tidehollow Sculler", ""));
+        cubeCards.add(new CardIdentity("Anguished Unmaking", ""));
+        cubeCards.add(new CardIdentity("Lingering Souls", ""));
+        cubeCards.add(new CardIdentity("Vindicate", ""));
+        cubeCards.add(new CardIdentity("Unburial Rites", ""));
+        cubeCards.add(new CardIdentity("Concealed Courtyard", ""));
+        cubeCards.add(new CardIdentity("Godless Shrine", ""));
+        cubeCards.add(new CardIdentity("Marsh Flats", ""));
+        cubeCards.add(new CardIdentity("Scrubland", ""));
+        cubeCards.add(new CardIdentity("Shambling Vent", ""));
+        cubeCards.add(new CardIdentity("Vraska, Golgari Queen", ""));
+        cubeCards.add(new CardIdentity("Assassin's Trophy", ""));
+        cubeCards.add(new CardIdentity("Maelstrom Pulse", ""));
+        cubeCards.add(new CardIdentity("Pernicious Deed", ""));
+        cubeCards.add(new CardIdentity("Bayou", ""));
+        cubeCards.add(new CardIdentity("Blooming Marsh", ""));
+        cubeCards.add(new CardIdentity("Hissing Quagmire", ""));
+        cubeCards.add(new CardIdentity("Overgrown Tomb", ""));
+        cubeCards.add(new CardIdentity("Verdant Catacombs", ""));
+        cubeCards.add(new CardIdentity("Edric, Spymaster of Trest", ""));
+        cubeCards.add(new CardIdentity("Trygon Predator", ""));
+        cubeCards.add(new CardIdentity("Hydroid Krasis", ""));
+        cubeCards.add(new CardIdentity("Uro, Titan of Nature's Wrath", ""));
+        cubeCards.add(new CardIdentity("Botanical Sanctum", ""));
+        cubeCards.add(new CardIdentity("Breeding Pool", ""));
+        cubeCards.add(new CardIdentity("Lumbering Falls", ""));
+        cubeCards.add(new CardIdentity("Misty Rainforest", ""));
+        cubeCards.add(new CardIdentity("Tropical Island", ""));
+        cubeCards.add(new CardIdentity("Goblin Electromancer", ""));
+        cubeCards.add(new CardIdentity("Dack Fayden", ""));
+        cubeCards.add(new CardIdentity("Thousand-Year Storm", ""));
+        cubeCards.add(new CardIdentity("Scalding Tarn", ""));
+        cubeCards.add(new CardIdentity("Spirebluff Canal", ""));
+        cubeCards.add(new CardIdentity("Steam Vents", ""));
+        cubeCards.add(new CardIdentity("Volcanic Island", ""));
+        cubeCards.add(new CardIdentity("Wandering Fumarole", ""));
+        cubeCards.add(new CardIdentity("Figure of Destiny", ""));
+        cubeCards.add(new CardIdentity("Ajani Vengeant", ""));
+        cubeCards.add(new CardIdentity("Nahiri, the Harbinger", ""));
+        cubeCards.add(new CardIdentity("Wear // Tear", ""));
+        cubeCards.add(new CardIdentity("Lightning Helix", ""));
+        cubeCards.add(new CardIdentity("Arid Mesa", ""));
+        cubeCards.add(new CardIdentity("Inspiring Vantage", ""));
+        cubeCards.add(new CardIdentity("Needle Spires", ""));
+        cubeCards.add(new CardIdentity("Plateau", ""));
+        cubeCards.add(new CardIdentity("Sacred Foundry", ""));
+        cubeCards.add(new CardIdentity("Sphinx of the Steel Wind", ""));
+        cubeCards.add(new CardIdentity("Nicol Bolas, Dragon-God", ""));
+        cubeCards.add(new CardIdentity("Leovold, Emissary of Trest", ""));
+        cubeCards.add(new CardIdentity("Progenitus", ""));
+        cubeCards.add(new CardIdentity("Kozilek, Butcher of Truth", ""));
+        cubeCards.add(new CardIdentity("Ulamog, the Ceaseless Hunger", ""));
+        cubeCards.add(new CardIdentity("Ulamog, the Infinite Gyre", ""));
+        cubeCards.add(new CardIdentity("Emrakul, the Promised End", ""));
+        cubeCards.add(new CardIdentity("Emrakul, the Aeons Torn", ""));
+        cubeCards.add(new CardIdentity("Karn, Scion of Urza", ""));
+        cubeCards.add(new CardIdentity("Karn Liberated", ""));
+        cubeCards.add(new CardIdentity("Ugin, the Spirit Dragon", ""));
+        cubeCards.add(new CardIdentity("Bomat Courier", ""));
+        cubeCards.add(new CardIdentity("Hangarback Walker", ""));
+        cubeCards.add(new CardIdentity("Phyrexian Revoker", ""));
+        cubeCards.add(new CardIdentity("Metalworker", ""));
+        cubeCards.add(new CardIdentity("Lodestone Golem", ""));
+        cubeCards.add(new CardIdentity("Solemn Simulacrum", ""));
+        cubeCards.add(new CardIdentity("Kuldotha Forgemaster", ""));
+        cubeCards.add(new CardIdentity("Wurmcoil Engine", ""));
+        cubeCards.add(new CardIdentity("Myr Battlesphere", ""));
+        cubeCards.add(new CardIdentity("Sundering Titan", ""));
+        cubeCards.add(new CardIdentity("Walking Ballista", ""));
+        cubeCards.add(new CardIdentity("Blightsteel Colossus", ""));
+        cubeCards.add(new CardIdentity("Black Lotus", ""));
+        cubeCards.add(new CardIdentity("Chrome Mox", ""));
+        cubeCards.add(new CardIdentity("Everflowing Chalice", ""));
+        cubeCards.add(new CardIdentity("Lion's Eye Diamond", ""));
+        cubeCards.add(new CardIdentity("Lotus Bloom", ""));
+        cubeCards.add(new CardIdentity("Mana Crypt", ""));
+        cubeCards.add(new CardIdentity("Mox Diamond", ""));
+        cubeCards.add(new CardIdentity("Mox Emerald", ""));
+        cubeCards.add(new CardIdentity("Mox Jet", ""));
+        cubeCards.add(new CardIdentity("Mox Pearl", ""));
+        cubeCards.add(new CardIdentity("Mox Ruby", ""));
+        cubeCards.add(new CardIdentity("Mox Sapphire", ""));
+        cubeCards.add(new CardIdentity("Mana Vault", ""));
+        cubeCards.add(new CardIdentity("Relic of Progenitus", ""));
+        cubeCards.add(new CardIdentity("Sensei's Divining Top", ""));
+        cubeCards.add(new CardIdentity("Skullclamp", ""));
+        cubeCards.add(new CardIdentity("Sol Ring", ""));
+        cubeCards.add(new CardIdentity("Azorius Signet", ""));
+        cubeCards.add(new CardIdentity("Boros Signet", ""));
+        cubeCards.add(new CardIdentity("Dimir Signet", ""));
+        cubeCards.add(new CardIdentity("Golgari Signet", ""));
+        cubeCards.add(new CardIdentity("Grim Monolith", ""));
+        cubeCards.add(new CardIdentity("Gruul Signet", ""));
+        cubeCards.add(new CardIdentity("Izzet Signet", ""));
+        cubeCards.add(new CardIdentity("Lightning Greaves", ""));
+        cubeCards.add(new CardIdentity("Orzhov Signet", ""));
+        cubeCards.add(new CardIdentity("Rakdos Signet", ""));
+        cubeCards.add(new CardIdentity("Selesnya Signet", ""));
+        cubeCards.add(new CardIdentity("Shrine of Burning Rage", ""));
+        cubeCards.add(new CardIdentity("Simic Signet", ""));
+        cubeCards.add(new CardIdentity("Smuggler's Copter", ""));
+        cubeCards.add(new CardIdentity("Umezawa's Jitte", ""));
+        cubeCards.add(new CardIdentity("Winter Orb", ""));
+        cubeCards.add(new CardIdentity("Basalt Monolith", ""));
+        cubeCards.add(new CardIdentity("Coalition Relic", ""));
+        cubeCards.add(new CardIdentity("Crucible of Worlds", ""));
+        cubeCards.add(new CardIdentity("Oblivion Stone", ""));
+        cubeCards.add(new CardIdentity("Sword of Body and Mind", ""));
+        cubeCards.add(new CardIdentity("Sword of Feast and Famine", ""));
+        cubeCards.add(new CardIdentity("Sword of Fire and Ice", ""));
+        cubeCards.add(new CardIdentity("Sword of Light and Shadow", ""));
+        cubeCards.add(new CardIdentity("Sword of War and Peace", ""));
+        cubeCards.add(new CardIdentity("Tangle Wire", ""));
+        cubeCards.add(new CardIdentity("Worn Powerstone", ""));
+        cubeCards.add(new CardIdentity("Coercive Portal", ""));
+        cubeCards.add(new CardIdentity("Smokestack", ""));
+        cubeCards.add(new CardIdentity("Thran Dynamo", ""));
+        cubeCards.add(new CardIdentity("Batterskull", ""));
+        cubeCards.add(new CardIdentity("Memory Jar", ""));
+        cubeCards.add(new CardIdentity("Mindslaver", ""));
+        cubeCards.add(new CardIdentity("Academy Ruins", ""));
+        cubeCards.add(new CardIdentity("Ancient Tomb", ""));
+        cubeCards.add(new CardIdentity("Bazaar of Baghdad", ""));
+        cubeCards.add(new CardIdentity("Blast Zone", ""));
+        cubeCards.add(new CardIdentity("Field of Ruin", ""));
+        cubeCards.add(new CardIdentity("Library of Alexandria", ""));
+        cubeCards.add(new CardIdentity("Maze of Ith", ""));
+        cubeCards.add(new CardIdentity("Mishra's Factory", ""));
+        cubeCards.add(new CardIdentity("Mishra's Workshop", ""));
+        cubeCards.add(new CardIdentity("Mutavault", ""));
+        cubeCards.add(new CardIdentity("Nykthos, Shrine to Nyx", ""));
+        cubeCards.add(new CardIdentity("Rishadan Port", ""));
+        cubeCards.add(new CardIdentity("Strip Mine", ""));
+        cubeCards.add(new CardIdentity("Wasteland", ""));
+        cubeCards.add(new CardIdentity("Expansion // Explosion", ""));
+        cubeCards.add(new CardIdentity("Giver of Runes", ""));
+        cubeCards.add(new CardIdentity("Winds of Abandon", ""));
+        cubeCards.add(new CardIdentity("Hallowed Spiritkeeper", ""));
+        cubeCards.add(new CardIdentity("Thraben Inspector", ""));
+        cubeCards.add(new CardIdentity("Narset, Parter of Veils", ""));
+        cubeCards.add(new CardIdentity("Force of Negation", ""));
+        cubeCards.add(new CardIdentity("Urza, Lord High Artificer", ""));
+        cubeCards.add(new CardIdentity("Emry, Lurker of the Loch", ""));
+        cubeCards.add(new CardIdentity("Brazen Borrower", ""));
+        cubeCards.add(new CardIdentity("Bolas's Citadel", ""));
+        cubeCards.add(new CardIdentity("Yawgmoth, Thran Physician", ""));
+        cubeCards.add(new CardIdentity("Rotting Regisaur", ""));
+        cubeCards.add(new CardIdentity("Murderous Rider", ""));
+        cubeCards.add(new CardIdentity("Wishclaw Talisman", ""));
+        cubeCards.add(new CardIdentity("Dreadhorde Arcanist", ""));
+        cubeCards.add(new CardIdentity("Seasoned Pyromancer", ""));
+        cubeCards.add(new CardIdentity("Embereth Shieldbreaker", ""));
+        cubeCards.add(new CardIdentity("Nissa, Who Shakes the World", ""));
+        cubeCards.add(new CardIdentity("Questing Beast", ""));
+        cubeCards.add(new CardIdentity("Teferi, Time Raveler", ""));
+        cubeCards.add(new CardIdentity("Angrath's Rampage", ""));
+        cubeCards.add(new CardIdentity("Fallen Shinobi", ""));
+        cubeCards.add(new CardIdentity("Wrenn and Six", ""));
+        cubeCards.add(new CardIdentity("Oko, Thief of Crowns", ""));
+        cubeCards.add(new CardIdentity("Garruk, Cursed Huntsman", ""));
+        cubeCards.add(new CardIdentity("Prismatic Vista", ""));
+        cubeCards.add(new CardIdentity("Golos, Tireless Pilgrim", ""));
+        cubeCards.add(new CardIdentity("Stonecoil Serpent", ""));
+    }
+}
+
diff --git a/Mage.Server/config/config.xml b/Mage.Server/config/config.xml
index 884dd9ebe2..d89f21d53b 100644
--- a/Mage.Server/config/config.xml
+++ b/Mage.Server/config/config.xml
@@ -143,6 +143,7 @@
         <draftCube name="MTGO Vintage Cube December 2018" jar="mage-tournament-booster-draft-${project.version}.jar" className="mage.tournament.cubes.VintageCubeDecember2018"/>
         <draftCube name="MTGO Vintage Cube June 2019" jar="mage-tournament-booster-draft-${project.version}.jar" className="mage.tournament.cubes.VintageCubeJune2019"/>
         <draftCube name="MTGO Vintage Cube December 2019" jar="mage-tournament-booster-draft-${project.version}.jar" className="mage.tournament.cubes.VintageCubeDecember2019"/>
+        <draftCube name="MTGO Vintage Cube April 2020" jar="mage-tournament-booster-draft-${project.version}.jar" className="mage.tournament.cubes.VintageCubeApril2020"/>
         <draftCube name="SCG Con Cube 2018 December" jar="mage-tournament-booster-draft-${project.version}.jar" className="mage.tournament.cubes.ScgConCube2018December"/>
         <draftCube name="The Peasant's Toolbox" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.PeasantsToolboxCube"/>
         <draftCube name="www.MTGCube.com" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.MTGCube"/>
diff --git a/Mage.Server/release/config/config.xml b/Mage.Server/release/config/config.xml
index d6eb9c8884..aac80217ba 100644
--- a/Mage.Server/release/config/config.xml
+++ b/Mage.Server/release/config/config.xml
@@ -137,6 +137,7 @@
         <draftCube name="MTGO Vintage Cube December 2018" jar="mage-tournament-booster-draft-${project.version}.jar" className="mage.tournament.cubes.VintageCubeDecember2018"/>
         <draftCube name="MTGO Vintage Cube June 2019" jar="mage-tournament-booster-draft-${project.version}.jar" className="mage.tournament.cubes.VintageCubeJune2019"/>
         <draftCube name="MTGO Vintage Cube December 2019" jar="mage-tournament-booster-draft-${project.version}.jar" className="mage.tournament.cubes.VintageCubeDecember2019"/>
+        <draftCube name="MTGO Vintage Cube April 2020" jar="mage-tournament-booster-draft-${project.version}.jar" className="mage.tournament.cubes.VintageCubeApril2020"/>
         <draftCube name="SCG Con Cube 2018 December" jar="mage-tournament-booster-draft-${project.version}.jar" className="mage.tournament.cubes.ScgConCube2018December"/>
         <draftCube name="The Peasant's Toolbox" jar="mage-tournament-booster-draft-${project.version}.jar" className="mage.tournament.cubes.PeasantsToolboxCube"/>
         <draftCube name="www.MTGCube.com" jar="mage-tournament-booster-draft-${project.version}.jar" className="mage.tournament.cubes.MTGCube"/>

From 9933420f571adec2a85409aed014d74069a57976 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Wed, 27 May 2020 10:24:21 +0200
Subject: [PATCH 059/586] * Emerge Ability - Fixed that the creature to
 sacrifice had to be selcted twice.

---
 .../mage/abilities/keyword/EmergeAbility.java   | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/Mage/src/main/java/mage/abilities/keyword/EmergeAbility.java b/Mage/src/main/java/mage/abilities/keyword/EmergeAbility.java
index 75ef4762ba..805c78ff46 100644
--- a/Mage/src/main/java/mage/abilities/keyword/EmergeAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/EmergeAbility.java
@@ -2,7 +2,6 @@ package mage.abilities.keyword;
 
 import mage.Mana;
 import mage.abilities.SpellAbility;
-import mage.abilities.costs.common.SacrificeTargetCost;
 import mage.abilities.costs.mana.ManaCost;
 import mage.abilities.costs.mana.ManaCosts;
 import mage.abilities.mana.ManaOptions;
@@ -11,7 +10,6 @@ import mage.constants.Outcome;
 import mage.constants.SpellAbilityType;
 import mage.constants.Zone;
 import mage.filter.common.FilterControlledCreaturePermanent;
-import mage.filter.predicate.mageobject.CardIdPredicate;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
 import mage.players.Player;
@@ -92,11 +90,16 @@ public class EmergeAbility extends SpellAbility {
             TargetPermanent target = new TargetControlledCreaturePermanent(new FilterControlledCreaturePermanent("creature to sacrifice for emerge"));
             if (controller.choose(Outcome.Sacrifice, target, this.getSourceId(), game)) {
                 Permanent creature = game.getPermanent(target.getFirstTarget());
-                CardUtil.reduceCost(this, creature.getConvertedManaCost());
-                FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent(creature.getLogName());
-                filter.add(new CardIdPredicate(creature.getId()));
-                this.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(filter)));
-                return super.activate(game, false);
+                if (creature != null) {
+                    CardUtil.reduceCost(this, creature.getConvertedManaCost());
+                    if (super.activate(game, false)) {
+                        if (creature.sacrifice(getSourceId(), game)) {
+                            return true;
+                        } else {
+                            activated = false;
+                        }
+                    }
+                }
             }
         }
         return false;

From d8bb67cffe8666c95f2515042469656c9ae8e9bd Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Wed, 27 May 2020 10:24:57 +0200
Subject: [PATCH 060/586] * Distended Mindbender - Fixed the not working card
 selection from target players hand.

---
 Mage.Sets/src/mage/cards/d/DistendedMindbender.java | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/d/DistendedMindbender.java b/Mage.Sets/src/mage/cards/d/DistendedMindbender.java
index cff8d820f6..8b6c5813b8 100644
--- a/Mage.Sets/src/mage/cards/d/DistendedMindbender.java
+++ b/Mage.Sets/src/mage/cards/d/DistendedMindbender.java
@@ -24,6 +24,7 @@ import mage.target.common.TargetCardInHand;
 import mage.target.common.TargetOpponent;
 
 import java.util.UUID;
+import mage.constants.Zone;
 
 /**
  * @author fireshoes
@@ -91,13 +92,13 @@ class DistendedMindbenderEffect extends OneShotEffect {
             return false;
         }
         opponent.revealCards(source, opponent.getHand(), game);
-        TargetCard targetThreeOrLess = new TargetCardInHand(1, filterThreeOrLess);
-        TargetCard targetFourOrGreater = new TargetCardInHand(1, filterFourOrGreater);
+        TargetCard targetThreeOrLess = new TargetCard(1, Zone.HAND, filterThreeOrLess);
+        TargetCard targetFourOrGreater = new TargetCard(1, Zone.HAND, filterFourOrGreater);
         Cards toDiscard = new CardsImpl();
-        if (controller.choose(Outcome.Benefit, opponent.getHand(), targetThreeOrLess, game)) {
+        if (controller.chooseTarget(Outcome.Benefit, opponent.getHand(), targetThreeOrLess, source, game)) {
             toDiscard.addAll(targetThreeOrLess.getTargets());
         }
-        if (controller.choose(Outcome.Benefit, opponent.getHand(), targetFourOrGreater, game)) {
+        if (controller.chooseTarget(Outcome.Benefit, opponent.getHand(), targetFourOrGreater, source, game)) {
             toDiscard.addAll(targetFourOrGreater.getTargets());
         }
         opponent.discard(toDiscard, source, game);

From e1c96efa1ebdbf8dc06da63711b54c23ce6f4ded Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Wed, 27 May 2020 13:59:16 +0200
Subject: [PATCH 061/586] * Fixed a problem with selecting cards from other
 players hand, failing because canTarget check with wrong player id.
 Changed/simplified canTarget method of TargetCardInHand to solve that
 problem. (#6532 Gruesome Discovery and Distended Mindbender and probably
 other changed made with 75577cdbe977948cd2f681a1082098e322290f99).

---
 .../cards/abilities/keywords/DiscardTest.java | 41 +++++++++++++++++++
 .../mage/target/common/TargetCardInHand.java  |  6 ++-
 2 files changed, 45 insertions(+), 2 deletions(-)

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DiscardTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DiscardTest.java
index 6126e65dd7..487aeb0ba3 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DiscardTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DiscardTest.java
@@ -120,4 +120,45 @@ public class DiscardTest extends CardTestPlayerBase {
 
         assertHandCount(playerB, "Driven // Despair", 0);
     }
+    
+    
+    /**
+     * Test a discard after selecting the cards from another player    
+     */
+    @Test
+    public void GruesomeDiscoveryTest(){
+        // Target player discards two cards.
+        // Morbid - If a creature died this turn, instead that player reveals their hand, 
+        // you choose two cards from it, then that player discards those cards.        
+        addCard(Zone.HAND, playerA, "Gruesome Discovery", 1); // Sorcery {2}{B}{B}
+        addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4);
+        
+        addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1);
+        
+        addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1);
+        addCard(Zone.HAND, playerB, "Silvercoat Lion");
+        addCard(Zone.HAND, playerB, "Aluren");
+        addCard(Zone.HAND, playerB, "Contagion");
+
+        attack(3, playerA, "Silvercoat Lion");
+        block(3, playerB, "Silvercoat Lion", "Silvercoat Lion");
+        castSpell(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Gruesome Discovery", playerB);
+
+        setChoice(playerA, "Aluren^Contagion");
+        
+        // addTarget(playerA, "Aluren");
+        // addTarget(playerA, "Contagion");
+        
+        setStopAt(3, PhaseStep.END_TURN);
+        execute();
+
+        assertGraveyardCount(playerA, "Silvercoat Lion", 1);
+        assertGraveyardCount(playerB, "Silvercoat Lion", 1);
+        
+        assertGraveyardCount(playerA, "Gruesome Discovery", 1);
+        
+        assertGraveyardCount(playerB, "Aluren", 1);
+        assertGraveyardCount(playerB, "Contagion", 1);
+        assertGraveyardCount(playerB, 3);
+    }  
 }
diff --git a/Mage/src/main/java/mage/target/common/TargetCardInHand.java b/Mage/src/main/java/mage/target/common/TargetCardInHand.java
index dd3a6be708..277b0113e9 100644
--- a/Mage/src/main/java/mage/target/common/TargetCardInHand.java
+++ b/Mage/src/main/java/mage/target/common/TargetCardInHand.java
@@ -41,8 +41,10 @@ public class TargetCardInHand extends TargetCard {
 
     @Override
     public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) {
-        Card card = game.getPlayer(playerId).getHand().get(id, game);
-        return card != null && filter.match(card, source != null ? source.getSourceId() : null, playerId, game);
+        // Has to be a card in the hand of a player in range. We don't know here, from which player's hand so we have to check all possible players
+        // And because a card in hand is never targeted we can omitt specific targeting related checks
+        return game.getState().getZone(id) == Zone.HAND 
+                && game.getState().getPlayersInRange(getTargetController() == null ? playerId : getTargetController(), game).contains(game.getOwnerId(id));
     }
 
     @Override

From f6590e6178c568c87f83b4c699821adf8ed274bf Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Wed, 27 May 2020 14:03:31 +0200
Subject: [PATCH 062/586] * Knollspine Dragon - removed redundant drew card
 info text.

---
 Mage.Sets/src/mage/cards/k/KnollspineDragon.java | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/k/KnollspineDragon.java b/Mage.Sets/src/mage/cards/k/KnollspineDragon.java
index 6b85accd45..63395447f9 100644
--- a/Mage.Sets/src/mage/cards/k/KnollspineDragon.java
+++ b/Mage.Sets/src/mage/cards/k/KnollspineDragon.java
@@ -72,11 +72,10 @@ class KnollspineDragonEffect extends OneShotEffect {
                 if (watcher != null) {
                     int drawAmount = watcher.getAmountOfDamageReceivedThisTurn(targetOpponent.getId());
                     controller.drawCards(drawAmount, source.getSourceId(), game);
-                    game.informPlayers(controller.getLogName() + "draws " + drawAmount + " cards");
                     return true;
                 }
             }
-            game.informPlayers(controller.getLogName() + "drew no cards");
+            game.informPlayers(controller.getLogName() + " drew no cards");
             return true;
         }
         return false;

From 7f3dea8fe4349c6c742ab84ca6f73af2f305c568 Mon Sep 17 00:00:00 2001
From: Dh0mp5eur <dhomps.florian@gmail.com>
Date: Wed, 27 May 2020 16:11:53 +0200
Subject: [PATCH 063/586] Updated DuelCommander ban list

---
 .../src/mage/deck/DuelCommander.java                | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/DuelCommander.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/DuelCommander.java
index 53b287527f..0cb9b35241 100644
--- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/DuelCommander.java
+++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/DuelCommander.java
@@ -13,24 +13,31 @@ public class DuelCommander extends Commander {
         banned.add("Ancient Tomb");
         banned.add("Back to Basics");
         banned.add("Black Lotus");
+        banned.add("Capture of Jingzhou");
+        banned.add("Cavern of Souls");
         banned.add("Channel");
         banned.add("Chrome Mox");
+        banned.add("Deflecting Swat");
         banned.add("Dig Through Time");
         banned.add("Eidolon of the Great Revel");
         banned.add("Emrakul, the Aeons Torn");
         banned.add("Entomb");
         banned.add("Fastbond");
+        banned.add("Field of the Dead");
+        banned.add("Fierce Guardianship");
         banned.add("Fireblast");
         banned.add("Food Chain");
         banned.add("Gaea's Cradle");
-        banned.add("Gifts Ungiven");
         banned.add("Grim Monolith");
         banned.add("Hermit Druid");
+        banned.add("High Tide");
         banned.add("Humility");
         banned.add("Imperial Seal");
         banned.add("Karakas");
         banned.add("Library of Alexandria");
+        banned.add("Lion's Eye Diamond");
         banned.add("Loyal Retainers");
+        banned.add("Lutri, the Spellchaser");
         banned.add("Mana Crypt");
         banned.add("Mana Drain");
         banned.add("Mana Vault");
@@ -53,15 +60,18 @@ public class DuelCommander extends Commander {
         banned.add("Sensei's Divining Top");
         banned.add("Sol Ring");
         banned.add("Strip Mine");
+        banned.add("Temporal Manipulation");
         banned.add("Thassa's Oracle");
         banned.add("The Tabernacle at Pendrell Vale");
         banned.add("Time Vault");
         banned.add("Time Walk");
         banned.add("Timetwister");
+        banned.add("Time Warp");
         banned.add("Tinker");
         banned.add("Tolarian Academy");
         banned.add("Treasure Cruise");
         banned.add("Vampiric Tutor");
+        banned.add("Wasteland");
 
         bannedCommander.add("Arahbo, Roar of the World");
         bannedCommander.add("Baral, Chief of Compliance");
@@ -72,7 +82,6 @@ public class DuelCommander extends Commander {
         bannedCommander.add("Edric, Spymaster of Trest");
         bannedCommander.add("Emry, Lurker of the Loch");
         bannedCommander.add("Geist of Saint Traft");
-        bannedCommander.add("Jace, Vryn's Prodigy");
         bannedCommander.add("Marath, Will of the Wild");
         bannedCommander.add("Najeela, the Blade-Blossom");
         bannedCommander.add("Oloro, Ageless Ascetic");

From f04c0bbc2a309a295b770f9dd69b35d69f23e452 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Wed, 27 May 2020 18:06:04 +0200
Subject: [PATCH 064/586] * Fixed some rule text problems.

---
 Mage.Sets/src/mage/cards/b/BlastZone.java                    | 3 ++-
 .../common/continuous/GainAbilityControlledEffect.java       | 5 +++--
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/b/BlastZone.java b/Mage.Sets/src/mage/cards/b/BlastZone.java
index 982b139f1c..5f33f3a11d 100644
--- a/Mage.Sets/src/mage/cards/b/BlastZone.java
+++ b/Mage.Sets/src/mage/cards/b/BlastZone.java
@@ -38,7 +38,8 @@ public final class BlastZone extends CardImpl {
         // Blast Zone enters the battlefield with a charge counter on it.
         this.addAbility(new EntersBattlefieldAbility(
                 new AddCountersSourceEffect(CounterType.CHARGE.createInstance(1))
-        ));
+                ,"with a charge counter")
+        );
 
         // {T}: Add {C}.
         this.addAbility(new ColorlessManaAbility());
diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityControlledEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityControlledEffect.java
index 4d37942139..2d96eaf08e 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityControlledEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityControlledEffect.java
@@ -26,11 +26,11 @@ public class GainAbilityControlledEffect extends ContinuousEffectImpl {
     protected boolean forceQuotes = false;
 
     public GainAbilityControlledEffect(Ability ability, Duration duration) {
-        this(ability, duration, StaticFilters.FILTER_PERMANENT);
+        this(ability, duration, StaticFilters.FILTER_PERMANENTS);
     }
 
     public GainAbilityControlledEffect(CompoundAbility ability, Duration duration) {
-        this(ability, duration, StaticFilters.FILTER_PERMANENT);
+        this(ability, duration, StaticFilters.FILTER_PERMANENTS);
     }
 
     public GainAbilityControlledEffect(Ability ability, Duration duration, FilterPermanent filter) {
@@ -154,6 +154,7 @@ public class GainAbilityControlledEffect extends ContinuousEffectImpl {
 
     /**
      * Add quotes to gains abilities (by default static abilities don't have it)
+     * @return 
      */
     public GainAbilityControlledEffect withForceQuotes() {
         this.forceQuotes = true;

From 698f0d72f11da8b29f3a56088e856ec8a6f72768 Mon Sep 17 00:00:00 2001
From: Eric Nelson <1895280+Grath@users.noreply.github.com>
Date: Wed, 27 May 2020 21:01:40 -0400
Subject: [PATCH 065/586] Fix Bazaar Trademage's ETB triggered ability.

It's currently implemented as a replacement ability rather than a triggered ability.
---
 Mage.Sets/src/mage/cards/b/BazaarTrademage.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Mage.Sets/src/mage/cards/b/BazaarTrademage.java b/Mage.Sets/src/mage/cards/b/BazaarTrademage.java
index e2cdeab207..942b4406b2 100644
--- a/Mage.Sets/src/mage/cards/b/BazaarTrademage.java
+++ b/Mage.Sets/src/mage/cards/b/BazaarTrademage.java
@@ -28,7 +28,7 @@ public final class BazaarTrademage extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
 
         // When Bazaar Trademage enters the battlefield, draw two cards, then discard three cards.
-        this.addAbility(new EntersBattlefieldAbility(new DrawDiscardControllerEffect(2, 3)));
+        this.addAbility(new EntersBattlefieldTriggeredAbility(new DrawDiscardControllerEffect(2, 3)));
     }
 
     private BazaarTrademage(final BazaarTrademage card) {

From 4dfcaefbf6fbe5f68f94061316059f6cbb4302d5 Mon Sep 17 00:00:00 2001
From: Eric Nelson <1895280+Grath@users.noreply.github.com>
Date: Wed, 27 May 2020 22:00:12 -0400
Subject: [PATCH 066/586] Update BazaarTrademage.java

---
 Mage.Sets/src/mage/cards/b/BazaarTrademage.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Mage.Sets/src/mage/cards/b/BazaarTrademage.java b/Mage.Sets/src/mage/cards/b/BazaarTrademage.java
index 942b4406b2..847ce58f66 100644
--- a/Mage.Sets/src/mage/cards/b/BazaarTrademage.java
+++ b/Mage.Sets/src/mage/cards/b/BazaarTrademage.java
@@ -1,7 +1,7 @@
 package mage.cards.b;
 
 import mage.MageInt;
-import mage.abilities.common.EntersBattlefieldAbility;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.effects.common.DrawDiscardControllerEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;

From 978118148b69c933b94bcbfaa8de048231341794 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Thu, 28 May 2020 22:30:34 +0400
Subject: [PATCH 067/586] Ability refactor: new code to remove abilities from
 permanent;

---
 .../src/mage/cards/a/AnjeFalkenrath.java      |  2 +-
 Mage.Sets/src/mage/cards/b/BloodSun.java      | 10 ++-
 .../src/mage/cards/b/BronzehideLion.java      |  9 +--
 .../src/mage/cards/c/CastThroughTime.java     |  2 +-
 .../src/mage/cards/d/DanceOfTheDead.java      |  4 +-
 Mage.Sets/src/mage/cards/d/DeadlyRecluse.java |  6 +-
 Mage.Sets/src/mage/cards/f/FlameSweep.java    |  4 +-
 .../src/mage/cards/m/MeliraSylvokOutcast.java |  2 +-
 Mage.Sets/src/mage/cards/s/ShoalSerpent.java  |  2 +-
 .../src/mage/cards/w/WishfulMerfolk.java      |  2 +-
 .../main/java/mage/abilities/Abilities.java   | 15 +++-
 .../java/mage/abilities/AbilitiesImpl.java    |  7 +-
 .../src/main/java/mage/abilities/Ability.java |  7 ++
 .../main/java/mage/abilities/AbilityImpl.java | 31 +++++---
 .../mage/abilities/common/LicidAbility.java   |  3 +-
 .../CreaturesCantGetOrHaveAbilityEffect.java  |  4 +-
 .../continuous/LoseAbilityAllEffect.java      | 12 +--
 .../continuous/LoseAbilityAttachedEffect.java |  7 +-
 ...seAbilityOrAnotherAbilityTargetEffect.java |  8 +-
 .../continuous/LoseAbilitySourceEffect.java   |  5 +-
 .../continuous/LoseAbilityTargetEffect.java   | 13 +---
 Mage/src/main/java/mage/cards/Card.java       |  4 +
 .../java/mage/game/permanent/Permanent.java   |  7 +-
 .../mage/game/permanent/PermanentImpl.java    | 76 ++++++++++---------
 24 files changed, 131 insertions(+), 111 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/a/AnjeFalkenrath.java b/Mage.Sets/src/mage/cards/a/AnjeFalkenrath.java
index 9d0e2834dd..70c8b6c5a0 100644
--- a/Mage.Sets/src/mage/cards/a/AnjeFalkenrath.java
+++ b/Mage.Sets/src/mage/cards/a/AnjeFalkenrath.java
@@ -83,7 +83,7 @@ class AnjeFalkenrathTriggeredAbility extends TriggeredAbilityImpl {
         if (card == null) {
             return false;
         }
-        return card.getAbilities(game).stream().anyMatch(ability -> ability instanceof MadnessAbility);
+        return card.getAbilities(game).containsClass(MadnessAbility.class);
     }
 
     @Override
diff --git a/Mage.Sets/src/mage/cards/b/BloodSun.java b/Mage.Sets/src/mage/cards/b/BloodSun.java
index 227b656eaf..0a9e2dd853 100644
--- a/Mage.Sets/src/mage/cards/b/BloodSun.java
+++ b/Mage.Sets/src/mage/cards/b/BloodSun.java
@@ -1,6 +1,8 @@
 
 package mage.cards.b;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
@@ -70,7 +72,13 @@ class BloodSunEffect extends ContinuousEffectImpl {
             for (Permanent permanent : game.getState().getBattlefield().getActivePermanents(StaticFilters.FILTER_LANDS, player.getId(), source.getSourceId(), game)) {
                 switch (layer) {
                     case AbilityAddingRemovingEffects_6:
-                        permanent.getAbilities().removeIf(ability -> ability.getAbilityType() != AbilityType.MANA);
+                        List<Ability> toRemove = new ArrayList<>();
+                        permanent.getAbilities().forEach(a -> {
+                            if (a.getAbilityType() != AbilityType.MANA) {
+                                toRemove.add(a);
+                            }
+                        });
+                        permanent.removeAbilities(toRemove, source.getSourceId(), game);
                         break;
                 }
             }
diff --git a/Mage.Sets/src/mage/cards/b/BronzehideLion.java b/Mage.Sets/src/mage/cards/b/BronzehideLion.java
index a1f7fa245b..5726a08cb0 100644
--- a/Mage.Sets/src/mage/cards/b/BronzehideLion.java
+++ b/Mage.Sets/src/mage/cards/b/BronzehideLion.java
@@ -133,17 +133,14 @@ class BronzehideLionContinuousEffect extends ContinuousEffectImpl {
         if (game.getState().getZoneChangeCounter(source.getSourceId()) > zoneChangeCounter) {
             discard();
         }
-        MageObject sourceObject = game.getPermanent(source.getSourceId());
+        Permanent sourceObject = game.getPermanent(source.getSourceId());
         if (sourceObject == null) {
             sourceObject = game.getPermanentEntering(source.getSourceId());
         }
         if (sourceObject == null) {
             return false;
         }
-        if (!(sourceObject instanceof Permanent)) {
-            return true;
-        }
-        Permanent lion = (Permanent) sourceObject;
+        Permanent lion = sourceObject;
         switch (layer) {
             case TypeChangingEffects_4:
                 lion.getCardType().clear();
@@ -158,7 +155,7 @@ class BronzehideLionContinuousEffect extends ContinuousEffectImpl {
                         toRemove.add(ability);
                     }
                 }
-                lion.getAbilities(game).removeAll(toRemove);
+                lion.removeAbilities(toRemove, source.getSourceId(), game);
 
                 lion.getSpellAbility().getTargets().clear();
                 lion.getSpellAbility().getEffects().clear();
diff --git a/Mage.Sets/src/mage/cards/c/CastThroughTime.java b/Mage.Sets/src/mage/cards/c/CastThroughTime.java
index 1693ae89d5..14b0171384 100644
--- a/Mage.Sets/src/mage/cards/c/CastThroughTime.java
+++ b/Mage.Sets/src/mage/cards/c/CastThroughTime.java
@@ -89,7 +89,7 @@ class GainReboundEffect extends ContinuousEffectImpl {
 
     private void addReboundAbility(Card card, Game game) {
         if (CastThroughTime.filter.match(card, game)) {
-            boolean found = card.getAbilities(game).stream().anyMatch(ability -> ability instanceof ReboundAbility);
+            boolean found = card.getAbilities(game).containsClass(ReboundAbility.class);
             if (!found) {
                 Ability ability = new ReboundAbility();
                 game.getState().addOtherAbility(card, ability);
diff --git a/Mage.Sets/src/mage/cards/d/DanceOfTheDead.java b/Mage.Sets/src/mage/cards/d/DanceOfTheDead.java
index e5c70512b6..53dd4fb364 100644
--- a/Mage.Sets/src/mage/cards/d/DanceOfTheDead.java
+++ b/Mage.Sets/src/mage/cards/d/DanceOfTheDead.java
@@ -240,9 +240,7 @@ class DanceOfTheDeadChangeAbilityEffect extends ContinuousEffectImpl implements
                     abilityToRemove = ability;
                 }
             }
-            if (abilityToRemove != null) {
-                permanent.getAbilities().remove(abilityToRemove);
-            }
+            permanent.removeAbility(abilityToRemove, source.getSourceId(), game);
             permanent.addAbility(newAbility, source.getSourceId(), game);
             return true;
         }
diff --git a/Mage.Sets/src/mage/cards/d/DeadlyRecluse.java b/Mage.Sets/src/mage/cards/d/DeadlyRecluse.java
index b39c37d9dc..4c5d0420b4 100644
--- a/Mage.Sets/src/mage/cards/d/DeadlyRecluse.java
+++ b/Mage.Sets/src/mage/cards/d/DeadlyRecluse.java
@@ -22,8 +22,12 @@ public final class DeadlyRecluse extends CardImpl {
 
         this.subtype.add(SubType.SPIDER);
         this.power = new MageInt(1);
-    this.toughness = new MageInt(2);
+        this.toughness = new MageInt(2);
+
+        // Reach (This creature can block creatures with flying.)
         this.addAbility(ReachAbility.getInstance());
+
+        // Deathtouch (Any amount of damage this deals to a creature is enough to destroy it.)
         this.addAbility(DeathtouchAbility.getInstance());
     }
 
diff --git a/Mage.Sets/src/mage/cards/f/FlameSweep.java b/Mage.Sets/src/mage/cards/f/FlameSweep.java
index 5fb3d7b6c2..6b27948005 100644
--- a/Mage.Sets/src/mage/cards/f/FlameSweep.java
+++ b/Mage.Sets/src/mage/cards/f/FlameSweep.java
@@ -51,8 +51,6 @@ enum FlameSweepPredicate implements ObjectPlayerPredicate<ObjectPlayer<Permanent
         Permanent object = input.getObject();
         UUID playerId = input.getPlayerId();
         return !(object.isControlledBy(playerId)
-                && object.getAbilities(game).stream().anyMatch(
-                ability -> ability.getClass().equals(FlyingAbility.class)
-        ));
+                && object.getAbilities(game).containsClass(FlyingAbility.class));
     }
 }
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/m/MeliraSylvokOutcast.java b/Mage.Sets/src/mage/cards/m/MeliraSylvokOutcast.java
index 3c6124c946..7565cfdca9 100644
--- a/Mage.Sets/src/mage/cards/m/MeliraSylvokOutcast.java
+++ b/Mage.Sets/src/mage/cards/m/MeliraSylvokOutcast.java
@@ -149,7 +149,7 @@ class MeliraSylvokOutcastEffect3 extends ContinuousEffectImpl {
         Set<UUID> opponents = game.getOpponents(source.getControllerId());
         for (Permanent perm : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game)) {
             if (opponents.contains(perm.getControllerId())) {
-                perm.getAbilities().remove(InfectAbility.getInstance());
+                perm.removeAbility(InfectAbility.getInstance(), source.getSourceId(), game);
             }
         }
         return true;
diff --git a/Mage.Sets/src/mage/cards/s/ShoalSerpent.java b/Mage.Sets/src/mage/cards/s/ShoalSerpent.java
index 8769d56081..73a060cff2 100644
--- a/Mage.Sets/src/mage/cards/s/ShoalSerpent.java
+++ b/Mage.Sets/src/mage/cards/s/ShoalSerpent.java
@@ -67,7 +67,7 @@ class ShoalSerpentEffect extends ContinuousEffectImpl {
             switch (layer) {
                 case AbilityAddingRemovingEffects_6:
                     if (sublayer == SubLayer.NA) {
-                        permanent.getAbilities().removeIf(entry -> entry.getId().equals(DefenderAbility.getInstance().getId()));
+                        permanent.removeAbility(DefenderAbility.getInstance(), source.getSourceId(), game);
                     }
                     break;
             }
diff --git a/Mage.Sets/src/mage/cards/w/WishfulMerfolk.java b/Mage.Sets/src/mage/cards/w/WishfulMerfolk.java
index 09ce7ad911..0badf53c39 100644
--- a/Mage.Sets/src/mage/cards/w/WishfulMerfolk.java
+++ b/Mage.Sets/src/mage/cards/w/WishfulMerfolk.java
@@ -68,7 +68,7 @@ class WishfulMerfolkEffect extends ContinuousEffectImpl {
             switch (layer) {
                 case AbilityAddingRemovingEffects_6:
                     if (sublayer == SubLayer.NA) {
-                        permanent.getAbilities().removeIf(entry -> entry.getId().equals(DefenderAbility.getInstance().getId()));
+                        permanent.removeAbility(DefenderAbility.getInstance(), source.getSourceId(), game);
                     }
                     break;
                 case TypeChangingEffects_4:
diff --git a/Mage/src/main/java/mage/abilities/Abilities.java b/Mage/src/main/java/mage/abilities/Abilities.java
index 0d1000d172..cbceb72bc3 100644
--- a/Mage/src/main/java/mage/abilities/Abilities.java
+++ b/Mage/src/main/java/mage/abilities/Abilities.java
@@ -2,9 +2,12 @@
 package mage.abilities;
 
 import java.io.Serializable;
+import java.util.Collection;
 import java.util.List;
 import java.util.Optional;
 import java.util.UUID;
+import java.util.function.Predicate;
+
 import mage.abilities.keyword.ProtectionAbility;
 import mage.abilities.mana.ActivatedManaAbilityImpl;
 import mage.constants.Zone;
@@ -255,7 +258,8 @@ public interface Abilities<T extends Ability> extends List<T>, Serializable {
     boolean containsAll(Abilities<T> abilities);
 
     /**
-     * Searches this set of abilities for the existence of the give class
+     * Searches this set of abilities for the existence of the given class
+     * Warning, it doesn't work with inherited classes (e.g. it's not equal to instanceOf command)
      *
      * @param classObject
      * @return True if the passed in class is also in this set of abilities.
@@ -271,4 +275,13 @@ public interface Abilities<T extends Ability> extends List<T>, Serializable {
     Abilities<T> copy();
 
     String getValue();
+
+    @Deprecated // use permanent.removeAbility instead
+    boolean remove(Object o);
+
+    @Deprecated // use permanent.removeAbility instead
+    boolean removeAll(Collection<?> c);
+
+    @Deprecated // use permanent.removeAbility instead
+    boolean removeIf(Predicate<? super T> filter);
 }
diff --git a/Mage/src/main/java/mage/abilities/AbilitiesImpl.java b/Mage/src/main/java/mage/abilities/AbilitiesImpl.java
index 15c1da8270..d46348ac00 100644
--- a/Mage/src/main/java/mage/abilities/AbilitiesImpl.java
+++ b/Mage/src/main/java/mage/abilities/AbilitiesImpl.java
@@ -232,7 +232,8 @@ public class AbilitiesImpl<T extends Ability> extends ArrayList<T> implements Ab
             if (ability.getId().equals(test.getId())) {
                 return true;
             }
-            if (ability.getOriginalId().equals(test.getId())) {
+            if (ability.getOriginalId().equals(test.getOriginalId())) {
+                // on ability resolve: engine creates ability's copy and generates newId(), so you must use originalId to find that ability in card later
                 return true;
             }
             if (ability instanceof MageSingleton && test instanceof MageSingleton && ability.getRule().equals(test.getRule())) {
@@ -243,7 +244,7 @@ public class AbilitiesImpl<T extends Ability> extends ArrayList<T> implements Ab
     }
 
     @Override
-    public boolean containsRule(T ability) {
+    public boolean containsRule(T ability) { // TODO: remove
         return stream().anyMatch(rule -> rule.getRule().equals(ability.getRule()));
     }
 
@@ -262,7 +263,7 @@ public class AbilitiesImpl<T extends Ability> extends ArrayList<T> implements Ab
     }
 
     @Override
-    public boolean containsKey(UUID abilityId) {
+    public boolean containsKey(UUID abilityId) { // TODO: remove
         return stream().anyMatch(ability -> abilityId.equals(ability.getId()));
     }
 
diff --git a/Mage/src/main/java/mage/abilities/Ability.java b/Mage/src/main/java/mage/abilities/Ability.java
index dd5b811f84..a1ebe909d0 100644
--- a/Mage/src/main/java/mage/abilities/Ability.java
+++ b/Mage/src/main/java/mage/abilities/Ability.java
@@ -522,4 +522,11 @@ public interface Ability extends Controllable, Serializable {
     Ability addCustomOutcome(Outcome customOutcome);
 
     Outcome getCustomOutcome();
+
+    /**
+     * For mtg's instances search, see rules example in 112.10b
+     * @param ability
+     * @return
+     */
+    boolean isSameInstance(Ability ability);
 }
diff --git a/Mage/src/main/java/mage/abilities/AbilityImpl.java b/Mage/src/main/java/mage/abilities/AbilityImpl.java
index 6edcbfc98a..f09b94f616 100644
--- a/Mage/src/main/java/mage/abilities/AbilityImpl.java
+++ b/Mage/src/main/java/mage/abilities/AbilityImpl.java
@@ -938,6 +938,10 @@ public abstract class AbilityImpl implements Ability {
 
     @Override
     public boolean hasSourceObjectAbility(Game game, MageObject source, GameEvent event) {
+        // if source object have this ability
+        // uses for ability.isInUseableZone
+        // replacement and other continues effects can be without source, but active (must return true)
+
         MageObject object = source;
         // for singleton abilities like Flying we can't rely on abilities' source because it's only once in continuous effects
         // so will use the sourceId of the object itself that came as a parameter if it is not null
@@ -949,16 +953,10 @@ public abstract class AbilityImpl implements Ability {
         }
         if (object != null) {
             if (object instanceof Permanent) {
-                if (!((Permanent) object).getAbilities(game).contains(this)) {
-                    return false;
-                }
-                return ((Permanent) object).isPhasedIn();
-            } else if (object instanceof Card) {
-                return ((Card) object).getAbilities(game).contains(this);
-            } else if (!object.getAbilities().contains(this)) { // not sure which object it can still be
-                // check if it's an ability that is temporary gained to a card
-                Abilities<Ability> otherAbilities = game.getState().getAllOtherAbilities(this.getSourceId());
-                return otherAbilities != null && otherAbilities.contains(this);
+                return object.hasAbility(this, game) && ((Permanent) object).isPhasedIn();
+            } else {
+                // cards and other objects
+                return object.hasAbility(this, game);
             }
         }
         return true;
@@ -1264,4 +1262,17 @@ public abstract class AbilityImpl implements Ability {
     public Outcome getCustomOutcome() {
         return this.customOutcome;
     }
+
+    @Override
+    public boolean isSameInstance(Ability ability) {
+        // same instance (by mtg rules) = same object, ID or class+text (you can't check class only cause it can be different by params/text)
+        if (ability == null) {
+            return false;
+        }
+
+        return (this == ability)
+                || (this.getId().equals(ability.getId()))
+                || (this.getOriginalId().equals(ability.getOriginalId()))
+                || (this.getClass() == ability.getClass() && this.getRule(true).equals(ability.getRule(true)));
+    }
 }
diff --git a/Mage/src/main/java/mage/abilities/common/LicidAbility.java b/Mage/src/main/java/mage/abilities/common/LicidAbility.java
index f37f715e26..7963a7808c 100644
--- a/Mage/src/main/java/mage/abilities/common/LicidAbility.java
+++ b/Mage/src/main/java/mage/abilities/common/LicidAbility.java
@@ -130,7 +130,8 @@ class LicidContinuousEffect extends ContinuousEffectImpl {
                             }
                         }
                     }
-                    licid.getAbilities(game).removeAll(toRemove);
+                    licid.removeAbilities(toRemove, source.getSourceId(), game);
+
                     Ability ability = new EnchantAbility("creature");
                     ability.setRuleAtTheTop(true);
                     licid.addAbility(ability, source.getSourceId(), game);
diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/CreaturesCantGetOrHaveAbilityEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/CreaturesCantGetOrHaveAbilityEffect.java
index 51fb06870d..6ea1aabfbe 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/continuous/CreaturesCantGetOrHaveAbilityEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/CreaturesCantGetOrHaveAbilityEffect.java
@@ -45,9 +45,7 @@ public class CreaturesCantGetOrHaveAbilityEffect extends ContinuousEffectImpl {
         if (controller != null) {
             for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) {
                 if (permanent != null) {
-                    while (permanent.getAbilities().remove(ability)) {
-                        // repeat as long as ability can be removed
-                    }
+                    permanent.removeAbility(ability, source.getSourceId(), game);
                 }
             }
             return true;
diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilityAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilityAllEffect.java
index f9a6f40117..dc8daf168c 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilityAllEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilityAllEffect.java
@@ -85,9 +85,7 @@ public class LoseAbilityAllEffect extends ContinuousEffectImpl {
             for (Iterator<MageObjectReference> it = affectedObjectList.iterator(); it.hasNext();) { // filter may not be used again, because object can have changed filter relevant attributes but still geets boost
                 Permanent perm = it.next().getPermanentOrLKIBattlefield(game); //LKI is neccessary for "dies triggered abilities" to work given to permanets  (e.g. Showstopper)
                 if (perm != null) {
-                    for (Ability ability : ability) {
-                        perm.getAbilities().removeIf(entry -> entry.getId().equals(ability.getId()));
-                    }
+                    perm.removeAbilities(ability, source.getSourceId(), game);
                 } else {
                     it.remove();
                     if (affectedObjectList.isEmpty()) {
@@ -99,9 +97,7 @@ public class LoseAbilityAllEffect extends ContinuousEffectImpl {
             for (Permanent perm : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) {
                 if (!(excludeSource && perm.getId().equals(source.getSourceId()))) {
                     System.out.println(game.getTurn() + ", " + game.getPhase() + ": " + "remove from size " + perm.getAbilities().size());
-                    for (Ability ability : ability) {
-                        perm.getAbilities().removeIf(entry -> entry.getId().equals(ability.getId()));
-                    }
+                    perm.removeAbilities(ability, source.getSourceId(), game);
                 }
             }
             // still as long as the prev. permanent is known to the LKI (e.g. Mikaeus, the Unhallowed) so gained dies triggered ability will trigger
@@ -111,9 +107,7 @@ public class LoseAbilityAllEffect extends ContinuousEffectImpl {
                     Permanent perm = (Permanent) mageObject;
                     if (!(excludeSource && perm.getId().equals(source.getSourceId()))) {
                         if (filter.match(perm, source.getSourceId(), source.getControllerId(), game)) {
-                            for (Ability ability : ability) {
-                                perm.getAbilities().removeIf(entry -> entry.getId().equals(ability.getId()));
-                            }
+                            perm.removeAbilities(ability, source.getSourceId(), game);
                         }
                     }
                 }
diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilityAttachedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilityAttachedEffect.java
index 9c43b5c4c3..5007c45494 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilityAttachedEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilityAttachedEffect.java
@@ -43,12 +43,7 @@ public class LoseAbilityAttachedEffect extends ContinuousEffectImpl {
         if (equipment != null && equipment.getAttachedTo() != null) {
             Permanent creature = game.getPermanent(equipment.getAttachedTo());
             if (creature != null) {
-                while (creature.getAbilities().contains(ability)) {
-                    if (!creature.getAbilities().remove(ability)) {
-                        // Something went wrong - ability has an other id?
-                        logger.warn("ability" + ability.getRule() + "couldn't be removed.");
-                    }
-                }
+                creature.removeAbility(ability, source.getSourceId(), game);
             }
         }
         return true;
diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilityOrAnotherAbilityTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilityOrAnotherAbilityTargetEffect.java
index 44f6031387..108f6f9054 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilityOrAnotherAbilityTargetEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilityOrAnotherAbilityTargetEffect.java
@@ -63,13 +63,9 @@ public class LoseAbilityOrAnotherAbilityTargetEffect extends LoseAbilityTargetEf
             if (player.choose(outcome, chooseAbility, game)) {
                 String chosenAbility = chooseAbility.getChoice();
                 if (chosenAbility.equals(ability.getRule())) {
-                    while (permanent.getAbilities().contains(ability)) {
-                        permanent.getAbilities().remove(ability);
-                    }
+                    permanent.removeAbility(ability, source.getSourceId(), game);
                 } else if (chosenAbility.equals(ability2.getRule())) {
-                    while (permanent.getAbilities().contains(ability2)) {
-                        permanent.getAbilities().remove(ability2);
-                    }
+                    permanent.removeAbility(ability2, source.getSourceId(), game);
                 }
             } else {
                 return false;
diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilitySourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilitySourceEffect.java
index 6f5d03f33b..f55e2fea5a 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilitySourceEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilitySourceEffect.java
@@ -56,10 +56,7 @@ public class LoseAbilitySourceEffect extends ContinuousEffectImpl {
     public boolean apply(Game game, Ability source) {
         Permanent permanent = game.getPermanent(source.getSourceId());
         if (permanent != null) {
-            // 112.10
-            while (permanent.getAbilities().remove(ability)) {
-
-            }
+            permanent.removeAbility(ability, source.getSourceId(), game);
         }
         return true;
     }
diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilityTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilityTargetEffect.java
index f8fa800f08..a3195035c3 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilityTargetEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAbilityTargetEffect.java
@@ -45,18 +45,7 @@ public class LoseAbilityTargetEffect extends ContinuousEffectImpl {
     public boolean apply(Game game, Ability source) {
         Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
         if (permanent != null) {
-            if (ability instanceof MageSingleton) {
-                while (permanent.getAbilities().contains(ability)) {
-                    permanent.getAbilities().remove(ability);
-                }
-            } else {
-                for (Iterator<Ability> iter = permanent.getAbilities().iterator(); iter.hasNext();) {
-                    Ability ab = iter.next();
-                    if (ab.getClass().equals(ability.getClass())) {
-                        iter.remove();
-                    }
-                }
-            }
+            permanent.removeAbility(ability, source.getSourceId(), game);
         }
         return true;
     }
diff --git a/Mage/src/main/java/mage/cards/Card.java b/Mage/src/main/java/mage/cards/Card.java
index 81a6877abb..3bb996652b 100644
--- a/Mage/src/main/java/mage/cards/Card.java
+++ b/Mage/src/main/java/mage/cards/Card.java
@@ -26,6 +26,10 @@ public interface Card extends MageObject {
 
     void setOwnerId(UUID ownerId);
 
+    /**
+     * For cards: return all basic and dynamic abilities
+     * For permanents: return all basic and dynamic abilities
+     */
     Abilities<Ability> getAbilities(Game game);
 
     void setSpellAbility(SpellAbility ability);
diff --git a/Mage/src/main/java/mage/game/permanent/Permanent.java b/Mage/src/main/java/mage/game/permanent/Permanent.java
index da45f0341b..5293323922 100644
--- a/Mage/src/main/java/mage/game/permanent/Permanent.java
+++ b/Mage/src/main/java/mage/game/permanent/Permanent.java
@@ -154,15 +154,16 @@ public interface Permanent extends Card, Controllable {
 
     String getValue(GameState state);
 
-    @Deprecated
-    void addAbility(Ability ability, Game game);
-
     void addAbility(Ability ability, UUID sourceId, Game game);
 
     void addAbility(Ability ability, UUID sourceId, Game game, boolean createNewId);
 
     void removeAllAbilities(UUID sourceId, Game game);
 
+    void removeAbility(Ability abilityToRemove, UUID sourceId, Game game);
+
+    void removeAbilities(List<Ability> abilitiesToRemove, UUID sourceId, Game game);
+
     void addLoyaltyUsed();
 
     boolean canLoyaltyBeUsed(Game game);
diff --git a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java
index cee725a145..fcc676c089 100644
--- a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java
+++ b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java
@@ -351,62 +351,70 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
 
     @Override
     public Abilities<Ability> getAbilities() {
-        return abilities;
+        return super.getAbilities();
     }
 
     @Override
     public Abilities<Ability> getAbilities(Game game) {
-        return abilities;
-    }
-
-    /**
-     * @param ability
-     * @param game
-     */
-    @Override
-    public void addAbility(Ability ability, Game game) {
-        if (!abilities.containsKey(ability.getId())) {
-            Ability copyAbility = ability.copy();
-            copyAbility.setControllerId(controllerId);
-            copyAbility.setSourceId(objectId);
-            if (game != null) {
-                game.getState().addAbility(copyAbility, this);
-            }
-            abilities.add(copyAbility);
-        }
+        return super.getAbilities(game);
     }
 
     @Override
     public void addAbility(Ability ability, UUID sourceId, Game game) {
-        addAbility(ability, sourceId, game, true);
+        addAbility(ability, sourceId, game, false);
     }
 
     @Override
+    @Deprecated // use addAbility(Ability ability, UUID sourceId, Game game) instead
     public void addAbility(Ability ability, UUID sourceId, Game game, boolean createNewId) {
+        // singleton abilities -- only one instance
+        // other abilities -- any amount of instances
+        // TODO: no needs in createNewId, so move code to addAbility(Ability ability, UUID sourceId, Game game)
         if (!abilities.containsKey(ability.getId())) {
             Ability copyAbility = ability.copy();
-            if (createNewId) {
-                copyAbility.newId(); // needed so that source can get an ability multiple times (e.g. Raging Ravine)
-            }
+            copyAbility.newId(); // needed so that source can get an ability multiple times (e.g. Raging Ravine)
             copyAbility.setControllerId(controllerId);
             copyAbility.setSourceId(objectId);
+            // triggered abilities must be added to the state().triggers
+            // still as long as the prev. permanent is known to the LKI (e.g. Showstopper) so gained dies triggered ability will trigger
             game.getState().addAbility(copyAbility, sourceId, this);
             abilities.add(copyAbility);
-        } else if (!createNewId) {
-            // triggered abilities must be added to the state().triggerdAbilities
-            // still as long as the prev. permanent is known to the LKI (e.g. Showstopper) so gained dies triggered ability will trigger
-            if (!game.getBattlefield().containsPermanent(this.getId())) {
-                Ability copyAbility = ability.copy();
-                copyAbility.setControllerId(controllerId);
-                copyAbility.setSourceId(objectId);
-                game.getState().addAbility(copyAbility, sourceId, this);
-            }
         }
     }
 
     @Override
     public void removeAllAbilities(UUID sourceId, Game game) {
-        getAbilities().clear();
+        // can't use getAbilities() here -- cause it's can be auto-generated list potentially
+        // TODO: what about triggered abilities? See addAbility above -- triggers adds to GameState
+        abilities.clear();
+    }
+
+    @Override
+    public void removeAbility(Ability abilityToRemove, UUID sourceId, Game game) {
+        if (abilityToRemove == null) {
+            return;
+        }
+
+        // 112.10b  Effects that remove an ability remove all instances of it.
+        List<Ability> toRemove = new ArrayList<>();
+        abilities.forEach(a -> {
+            if (a.isSameInstance(abilityToRemove)) {
+                toRemove.add(a);
+            }
+        });
+
+        // can't use getAbilities() here -- cause it's can be auto-generated list potentially
+        // TODO: what about triggered abilities? See addAbility above -- triggers adds to GameState
+        toRemove.forEach(r -> abilities.remove(r));
+    }
+
+    @Override
+    public void removeAbilities(List<Ability> abilitiesToRemove, UUID sourceId, Game game){
+        if (abilitiesToRemove == null) {
+            return;
+        }
+
+        abilitiesToRemove.forEach(a -> removeAbility(a, sourceId, game));
     }
 
     @Override
@@ -728,7 +736,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
             game.fireEvent(new GameEvent(EventType.GAINED_CONTROL, objectId, objectId, controllerId));
 
             return true;
-        } else if (isCopy()) {// Because the previous copied abilities can be from another controller chnage controller in any case for abilities
+        } else if (isCopy()) {// Because the previous copied abilities can be from another controller - change controller in any case for abilities
             this.getAbilities(game).setControllerId(controllerId);
             game.getContinuousEffects().setController(objectId, controllerId);
         }

From 8af43dc13aa7d2ccdb5673aecbf98b0e48736a66 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Thu, 28 May 2020 22:34:27 +0400
Subject: [PATCH 068/586] Ability refactor: new code to search abilities in
 cards and permanents;

---
 .../java/mage/player/ai/ComputerPlayer.java    |  2 +-
 Mage.Sets/src/mage/cards/c/Chaosphere.java     |  2 +-
 Mage.Sets/src/mage/cards/c/CrimsonRoc.java     |  2 +-
 Mage.Sets/src/mage/cards/d/DenseCanopy.java    |  2 +-
 .../src/mage/cards/m/MomentumRumbler.java      |  2 +-
 .../src/mage/cards/q/QuartzwoodCrasher.java    |  4 ++--
 .../src/mage/cards/r/ReturnFromExtinction.java |  2 +-
 .../src/mage/cards/s/SidarKondoOfJamuraa.java  |  4 ++--
 Mage.Sets/src/mage/cards/s/SlingbowTrap.java   |  2 +-
 .../src/mage/cards/s/StormtideLeviathan.java   |  2 +-
 .../src/mage/cards/t/TaigamOjutaiMaster.java   |  2 +-
 .../cards/continuous/CommandersCastTest.java   |  1 -
 .../mage/test/cards/continuous/WonderTest.java |  2 +-
 Mage/src/main/java/mage/MageObject.java        | 10 +++++++---
 Mage/src/main/java/mage/MageObjectImpl.java    |  6 +++---
 .../main/java/mage/abilities/SpellAbility.java |  2 +-
 .../condition/common/SuspendedCondition.java   |  4 ++--
 .../effects/GainAbilitySpellsEffect.java       |  2 +-
 .../CanBlockOnlyFlyingAttachedEffect.java      |  2 +-
 .../combat/CanBlockOnlyFlyingEffect.java       |  2 +-
 .../GainAbilityControlledSpellsEffect.java     |  2 +-
 .../mage/abilities/keyword/SuspendAbility.java |  9 ++++++++-
 Mage/src/main/java/mage/cards/CardImpl.java    | 18 ++++++++++++++++--
 .../java/mage/designations/Designation.java    |  4 ++--
 .../src/main/java/mage/game/combat/Combat.java |  7 ++++---
 .../main/java/mage/game/command/Commander.java |  6 +++---
 .../main/java/mage/game/command/Emblem.java    |  4 ++--
 .../src/main/java/mage/game/command/Plane.java |  4 ++--
 .../planes/TrailOfTheMageRingsPlane.java       |  2 +-
 Mage/src/main/java/mage/game/stack/Spell.java  |  4 ++--
 .../java/mage/game/stack/StackAbility.java     | 15 ++++++++++++++-
 31 files changed, 85 insertions(+), 47 deletions(-)

diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java
index 4708905b36..501f560347 100644
--- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java
+++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java
@@ -1321,7 +1321,7 @@ public class ComputerPlayer extends PlayerImpl implements Player {
                         if (ability != null && ability.canActivate(playerId, game).canActivate()
                                 && !game.getContinuousEffects().preventedByRuleModification(GameEvent.getEvent(GameEvent.EventType.CAST_SPELL, ability.getSourceId(), ability.getSourceId(), playerId), ability, game, true)) {
                             if (card.getCardType().contains(CardType.INSTANT)
-                                    || card.hasAbility(FlashAbility.getInstance().getId(), game)) {
+                                    || card.hasAbility(FlashAbility.getInstance(), game)) {
                                 playableInstant.add(card);
                             } else {
                                 playableNonInstant.add(card);
diff --git a/Mage.Sets/src/mage/cards/c/Chaosphere.java b/Mage.Sets/src/mage/cards/c/Chaosphere.java
index 12511380fc..73ed95605d 100644
--- a/Mage.Sets/src/mage/cards/c/Chaosphere.java
+++ b/Mage.Sets/src/mage/cards/c/Chaosphere.java
@@ -82,7 +82,7 @@ class ChaosphereEffect extends RestrictionEffect {
         if (attacker == null) {
             return true;
         }
-        return attacker.hasAbility(FlyingAbility.getInstance().getId(), game);
+        return attacker.hasAbility(FlyingAbility.getInstance(), game);
     }
 
     @Override
diff --git a/Mage.Sets/src/mage/cards/c/CrimsonRoc.java b/Mage.Sets/src/mage/cards/c/CrimsonRoc.java
index 6b7d68697c..1dab5eb0ff 100644
--- a/Mage.Sets/src/mage/cards/c/CrimsonRoc.java
+++ b/Mage.Sets/src/mage/cards/c/CrimsonRoc.java
@@ -71,7 +71,7 @@ class CrimsonRocTriggeredAbility extends TriggeredAbilityImpl {
         Permanent permanent = game.getPermanent(event.getTargetId());
         return permanent != null
                 && permanent.isCreature()
-                && !permanent.getAbilities(game).contains(FlyingAbility.getInstance());
+                && !permanent.hasAbility(FlyingAbility.getInstance(), game);
     }
 
     @Override
diff --git a/Mage.Sets/src/mage/cards/d/DenseCanopy.java b/Mage.Sets/src/mage/cards/d/DenseCanopy.java
index 9a43da5da0..3226e49efa 100644
--- a/Mage.Sets/src/mage/cards/d/DenseCanopy.java
+++ b/Mage.Sets/src/mage/cards/d/DenseCanopy.java
@@ -66,7 +66,7 @@ class DenseCanopyCantBlockEffect extends RestrictionEffect {
         if (attacker == null) {
             return true;
         }
-        return attacker.hasAbility(FlyingAbility.getInstance().getId(), game);
+        return attacker.hasAbility(FlyingAbility.getInstance(), game);
     }
 
     @Override
diff --git a/Mage.Sets/src/mage/cards/m/MomentumRumbler.java b/Mage.Sets/src/mage/cards/m/MomentumRumbler.java
index e8d8087f9f..b6875666aa 100644
--- a/Mage.Sets/src/mage/cards/m/MomentumRumbler.java
+++ b/Mage.Sets/src/mage/cards/m/MomentumRumbler.java
@@ -75,6 +75,6 @@ enum MomentumRumblerCondition implements Condition {
         if (permanent == null) {
             return false;
         }
-        return hasAbility == permanent.getAbilities(game).containsKey(FirstStrikeAbility.getInstance().getId());
+        return hasAbility == permanent.hasAbility(FirstStrikeAbility.getInstance(), game);
     }
 }
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/q/QuartzwoodCrasher.java b/Mage.Sets/src/mage/cards/q/QuartzwoodCrasher.java
index 01f05f155e..e83513d223 100644
--- a/Mage.Sets/src/mage/cards/q/QuartzwoodCrasher.java
+++ b/Mage.Sets/src/mage/cards/q/QuartzwoodCrasher.java
@@ -82,7 +82,7 @@ class QuartzwoodCrasherTriggeredAbility extends TriggeredAbilityImpl {
                 && ((DamagedPlayerEvent) event).isCombatDamage()) {
             Permanent creature = game.getPermanent(event.getSourceId());
             if (creature != null && creature.isControlledBy(controllerId)
-                    && creature.hasAbility(TrampleAbility.getInstance().getId(), game)
+                    && creature.hasAbility(TrampleAbility.getInstance(), game)
                     && !damagedPlayerIds.contains(event.getTargetId())) {
                 damagedPlayerIds.add(event.getTargetId());
                 this.getEffects().setTargetPointer(new FixedTarget(event.getTargetId(), game));
@@ -143,7 +143,7 @@ class QuartzwoodCrasherWatcher extends Watcher {
             return;
         }
         Permanent creature = game.getPermanent(event.getSourceId());
-        if (creature == null || !creature.getAbilities(game).containsKey(TrampleAbility.getInstance().getId())) {
+        if (creature == null || !creature.hasAbility(TrampleAbility.getInstance(), game)) {
             return;
         }
         damageMap
diff --git a/Mage.Sets/src/mage/cards/r/ReturnFromExtinction.java b/Mage.Sets/src/mage/cards/r/ReturnFromExtinction.java
index f6decd52d6..6a3f7ce162 100644
--- a/Mage.Sets/src/mage/cards/r/ReturnFromExtinction.java
+++ b/Mage.Sets/src/mage/cards/r/ReturnFromExtinction.java
@@ -88,7 +88,7 @@ class ReturnFromExtinctionTarget extends TargetCardInYourGraveyard {
             return false;
         }
         for (Card card : player.getGraveyard().getCards(filter, sourceId, sourceControllerId, game)) {
-            if (card.isAllCreatureTypes() || card.getAbilities(game).contains(ChangelingAbility.getInstance())) {
+            if (card.isAllCreatureTypes() || card.hasAbility(ChangelingAbility.getInstance(), game)) {
                 if (!subTypes.isEmpty()) {
                     return true;
                 } else {
diff --git a/Mage.Sets/src/mage/cards/s/SidarKondoOfJamuraa.java b/Mage.Sets/src/mage/cards/s/SidarKondoOfJamuraa.java
index f3db71d472..6bbd1ab410 100644
--- a/Mage.Sets/src/mage/cards/s/SidarKondoOfJamuraa.java
+++ b/Mage.Sets/src/mage/cards/s/SidarKondoOfJamuraa.java
@@ -80,8 +80,8 @@ class SidarKondoOfJamuraaCantBlockCreaturesSourceEffect extends RestrictionEffec
 
     @Override
     public boolean applies(Permanent permanent, Ability source, Game game) {
-        if (permanent.hasAbility(FlyingAbility.getInstance().getId(), game)
-                || permanent.hasAbility(ReachAbility.getInstance().getId(), game)) {
+        if (permanent.hasAbility(FlyingAbility.getInstance(), game)
+                || permanent.hasAbility(ReachAbility.getInstance(), game)) {
             return false;
         }
         return game.getOpponents(source.getControllerId()).contains(permanent.getControllerId());
diff --git a/Mage.Sets/src/mage/cards/s/SlingbowTrap.java b/Mage.Sets/src/mage/cards/s/SlingbowTrap.java
index 87f3856419..8fcc5cd8a9 100644
--- a/Mage.Sets/src/mage/cards/s/SlingbowTrap.java
+++ b/Mage.Sets/src/mage/cards/s/SlingbowTrap.java
@@ -61,7 +61,7 @@ enum SlingbowTrapCondition implements Condition {
         for (UUID attackingCreatureId : game.getCombat().getAttackers()) {
             Permanent attackingCreature = game.getPermanent(attackingCreatureId);
             if (attackingCreature != null) {
-                if (attackingCreature.getColor(game).isBlack() && attackingCreature.hasAbility(FlyingAbility.getInstance().getId(), game)) {
+                if (attackingCreature.getColor(game).isBlack() && attackingCreature.hasAbility(FlyingAbility.getInstance(), game)) {
                     return true;
                 }
             }
diff --git a/Mage.Sets/src/mage/cards/s/StormtideLeviathan.java b/Mage.Sets/src/mage/cards/s/StormtideLeviathan.java
index 40e76151ee..0c46713eaf 100644
--- a/Mage.Sets/src/mage/cards/s/StormtideLeviathan.java
+++ b/Mage.Sets/src/mage/cards/s/StormtideLeviathan.java
@@ -91,7 +91,7 @@ public final class StormtideLeviathan extends CardImpl {
                         // land abilities are intrinsic, so add them here, not in layer 6
                         if (!land.hasSubtype(SubType.ISLAND, game)) {
                             land.getSubtype(game).add(SubType.ISLAND);
-                            if (!land.getAbilities(game).contains(new BlueManaAbility())) {
+                            if (!land.getAbilities(game).containsClass(BlueManaAbility.class)) {
                                 land.addAbility(new BlueManaAbility(), source.getSourceId(), game);
                             }
                         }
diff --git a/Mage.Sets/src/mage/cards/t/TaigamOjutaiMaster.java b/Mage.Sets/src/mage/cards/t/TaigamOjutaiMaster.java
index 4aeaa23049..236ce4ff01 100644
--- a/Mage.Sets/src/mage/cards/t/TaigamOjutaiMaster.java
+++ b/Mage.Sets/src/mage/cards/t/TaigamOjutaiMaster.java
@@ -150,7 +150,7 @@ class TaigamOjutaiMasterGainReboundEffect extends ContinuousEffectImpl {
     }
 
     private void addReboundAbility(Card card, Ability source, Game game) {
-        boolean found = card.getAbilities(game).stream().anyMatch(ability -> ability instanceof ReboundAbility);
+        boolean found = card.getAbilities(game).containsClass(ReboundAbility.class);
         if (!found) {
             Ability ability = new ReboundAbility();
             game.getState().addOtherAbility(card, ability);
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/CommandersCastTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/CommandersCastTest.java
index 4caf5347b1..b217f12dda 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/CommandersCastTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/CommandersCastTest.java
@@ -113,7 +113,6 @@ public class CommandersCastTest extends CardTestCommander4Players {
         waitStackResolved(5, PhaseStep.POSTCOMBAT_MAIN);
         checkPermanentCount("after cast 2", 5, PhaseStep.POSTCOMBAT_MAIN, playerA, "Academy Ruins", 1);
 
-//        showBattlefield("end battlefield", 5, PhaseStep.END_TURN, playerA);
         setStopAt(5, PhaseStep.END_TURN);
         setStrictChooseMode(true);
         execute();
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/WonderTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/WonderTest.java
index 440074c3e2..d4c13331a8 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/WonderTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/WonderTest.java
@@ -40,7 +40,7 @@ public class WonderTest extends CardTestPlayerBase {
         // check no flying in graveyard
         for (Card card : playerA.getGraveyard().getCards(currentGame)) {
             if (card.getName().equals("Runeclaw Bear")) {
-                Assert.assertFalse(card.getAbilities().contains(FlyingAbility.getInstance()));
+                Assert.assertFalse(card.hasAbility(FlyingAbility.getInstance(), currentGame));
             }
         }
     }
diff --git a/Mage/src/main/java/mage/MageObject.java b/Mage/src/main/java/mage/MageObject.java
index 744ec43f07..b2c21ab27d 100644
--- a/Mage/src/main/java/mage/MageObject.java
+++ b/Mage/src/main/java/mage/MageObject.java
@@ -41,9 +41,13 @@ public interface MageObject extends MageItem, Serializable {
 
     Set<SuperType> getSuperType();
 
+    /**
+     * For cards: return basic abilities (without dynamic added)
+     * For permanents: return all abilities (dynamic ability inserts into permanent)
+     */
     Abilities<Ability> getAbilities();
 
-    boolean hasAbility(UUID abilityId, Game game);
+    boolean hasAbility(Ability ability, Game game);
 
     ObjectColor getColor(Game game);
 
@@ -180,9 +184,9 @@ public interface MageObject extends MageItem, Serializable {
         }
 
         if (this.isCreature() && otherCard.isCreature()) {
-            if (this.getAbilities().contains(ChangelingAbility.getInstance())
+            if (this.hasAbility(ChangelingAbility.getInstance(), game)
                     || this.isAllCreatureTypes()
-                    || otherCard.getAbilities().contains(ChangelingAbility.getInstance())
+                    || otherCard.hasAbility(ChangelingAbility.getInstance(), game)
                     || otherCard.isAllCreatureTypes()) {
                 return true;
             }
diff --git a/Mage/src/main/java/mage/MageObjectImpl.java b/Mage/src/main/java/mage/MageObjectImpl.java
index b68592bb6b..f25762695f 100644
--- a/Mage/src/main/java/mage/MageObjectImpl.java
+++ b/Mage/src/main/java/mage/MageObjectImpl.java
@@ -132,12 +132,12 @@ public abstract class MageObjectImpl implements MageObject {
     }
 
     @Override
-    public boolean hasAbility(UUID abilityId, Game game) {
-        if (this.getAbilities().containsKey(abilityId)) {
+    public boolean hasAbility(Ability ability, Game game) {
+        if (this.getAbilities().contains(ability)) {
             return true;
         }
         Abilities<Ability> otherAbilities = game.getState().getAllOtherAbilities(getId());
-        return otherAbilities != null && otherAbilities.containsKey(abilityId);
+        return otherAbilities != null && otherAbilities.contains(ability);
     }
 
     @Override
diff --git a/Mage/src/main/java/mage/abilities/SpellAbility.java b/Mage/src/main/java/mage/abilities/SpellAbility.java
index 0df442d16c..1978d8e162 100644
--- a/Mage/src/main/java/mage/abilities/SpellAbility.java
+++ b/Mage/src/main/java/mage/abilities/SpellAbility.java
@@ -71,7 +71,7 @@ public class SpellAbility extends ActivatedAbilityImpl {
         }
         return null != game.getContinuousEffects().asThough(sourceId, AsThoughEffectType.CAST_AS_INSTANT, this, playerId, game) // check this first to allow Offering in main phase
                 || timing == TimingRule.INSTANT
-                || object.hasAbility(FlashAbility.getInstance().getId(), game)
+                || object.hasAbility(FlashAbility.getInstance(), game)
                 || game.canPlaySorcery(playerId);
     }
 
diff --git a/Mage/src/main/java/mage/abilities/condition/common/SuspendedCondition.java b/Mage/src/main/java/mage/abilities/condition/common/SuspendedCondition.java
index dcdd6a8162..ba7dc69d1b 100644
--- a/Mage/src/main/java/mage/abilities/condition/common/SuspendedCondition.java
+++ b/Mage/src/main/java/mage/abilities/condition/common/SuspendedCondition.java
@@ -27,10 +27,10 @@ public enum SuspendedCondition implements Condition {
     public boolean apply(Game game, Ability source) {
         Card card = game.getCard(source.getSourceId());
         if (card != null) {
-            boolean found = card.getAbilities().stream().anyMatch(ability -> ability instanceof SuspendAbility);
+            boolean found = card.getAbilities(game).containsClass(SuspendAbility.class);
 
             if (!found) {
-                found = game.getState().getAllOtherAbilities(source.getSourceId()).stream().anyMatch(ability -> ability instanceof SuspendAbility);
+                found = game.getState().getAllOtherAbilities(source.getSourceId()).containsClass(SuspendAbility.class);
 
             }
             if (found) {
diff --git a/Mage/src/main/java/mage/abilities/effects/GainAbilitySpellsEffect.java b/Mage/src/main/java/mage/abilities/effects/GainAbilitySpellsEffect.java
index 11545d7d27..0d96f2a918 100644
--- a/Mage/src/main/java/mage/abilities/effects/GainAbilitySpellsEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/GainAbilitySpellsEffect.java
@@ -64,7 +64,7 @@ public class GainAbilitySpellsEffect extends ContinuousEffectImpl {
                 if (stackObject.isControlledBy(source.getControllerId())) {
                     Card card = game.getCard(stackObject.getSourceId());
                     if (card != null && filter.match(card, game)) {
-                        if (!card.getAbilities().contains(ability)) {
+                        if (!card.hasAbility(ability, game)) {
                             game.getState().addOtherAbility(card, ability);
                         }
                     }
diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/CanBlockOnlyFlyingAttachedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/CanBlockOnlyFlyingAttachedEffect.java
index ecb95414c9..a3b94d6f0a 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/combat/CanBlockOnlyFlyingAttachedEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/combat/CanBlockOnlyFlyingAttachedEffect.java
@@ -33,7 +33,7 @@ public class CanBlockOnlyFlyingAttachedEffect extends RestrictionEffect {
         if (attacker == null) {
             return true;
         }
-        return attacker.getAbilities().contains(FlyingAbility.getInstance());
+        return attacker.hasAbility(FlyingAbility.getInstance(), game);
     }
 
     @Override
diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/CanBlockOnlyFlyingEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/CanBlockOnlyFlyingEffect.java
index c39e9d3188..4d41916cb4 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/combat/CanBlockOnlyFlyingEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/combat/CanBlockOnlyFlyingEffect.java
@@ -33,7 +33,7 @@ public class CanBlockOnlyFlyingEffect extends RestrictionEffect {
         if (attacker == null) {
             return true;
         }
-        return attacker.getAbilities().contains(FlyingAbility.getInstance());
+        return attacker.hasAbility(FlyingAbility.getInstance(), game);
     }
 
     @Override
diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityControlledSpellsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityControlledSpellsEffect.java
index e58f88cda6..424c996c1a 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityControlledSpellsEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityControlledSpellsEffect.java
@@ -51,7 +51,7 @@ public class GainAbilityControlledSpellsEffect extends ContinuousEffectImpl {
                 if ((stackObject instanceof Spell) && !stackObject.isCopy() && stackObject.isControlledBy(source.getControllerId())) {
                     Spell spell = (Spell) stackObject;
                     if (filter.match(spell, game)) {
-                        if (!spell.getAbilities().contains(ability)) {
+                        if (!spell.hasAbility(ability, game)) {
                             game.getState().addOtherAbility(spell.getCard(), ability);
                         }
                     }
diff --git a/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java b/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java
index 379d81027b..2bca55f26d 100644
--- a/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java
@@ -213,7 +213,7 @@ public class SuspendAbility extends SpecialAction {
         }
         MageObject object = game.getObject(sourceId);
         return new ActivationStatus(object.isInstant()
-                || object.hasAbility(FlashAbility.getInstance().getId(), game)
+                || object.hasAbility(FlashAbility.getInstance(), game)
                 || null != game.getContinuousEffects().asThough(sourceId, 
                         AsThoughEffectType.CAST_AS_INSTANT, this, playerId, game)
                 || game.canPlaySorcery(playerId), null);
@@ -356,6 +356,13 @@ class SuspendPlayCardEffect extends OneShotEffect {
                     }
                 }
                 // remove the abilities from the card
+                // TODO: will not work with Adventure Cards and another auto-generated abilities list
+                // TODO: is it work after blink or return to hand?
+                /*
+                 bug example:
+                 Epochrasite bug: It comes out of suspend, is cast and enters the battlefield. THEN if it's returned to
+                 its owner's hand from battlefield, the bounced Epochrasite can't be cast for the rest of the game.
+                 */
                 card.getAbilities().removeAll(abilitiesToRemove);
             }
             // cast the card for free
diff --git a/Mage/src/main/java/mage/cards/CardImpl.java b/Mage/src/main/java/mage/cards/CardImpl.java
index d5daa56d42..f7d168227b 100644
--- a/Mage/src/main/java/mage/cards/CardImpl.java
+++ b/Mage/src/main/java/mage/cards/CardImpl.java
@@ -285,7 +285,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
 
     /**
      * Gets all current abilities - includes additional abilities added by other
-     * cards or effects
+     * cards or effects. Warning, you can't modify that list.
      *
      * @param game
      * @return A list of {@link Ability} - this collection is not modifiable
@@ -295,15 +295,23 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
         if (game == null) {
             return abilities; // deck editor with empty game
         }
+
         CardState cardState = game.getState().getCardState(this.getId());
-        if (!cardState.hasLostAllAbilities() && (cardState.getAbilities() == null || cardState.getAbilities().isEmpty())) {
+        if (cardState == null) {
             return abilities;
         }
+
+        // collects all abilities
         Abilities<Ability> all = new AbilitiesImpl<>();
+
+        // basic
         if (!cardState.hasLostAllAbilities()) {
             all.addAll(abilities);
         }
+
+        // dynamic
         all.addAll(cardState.getAbilities());
+
         return all;
     }
 
@@ -314,6 +322,12 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
         cardState.getAbilities().clear();
     }
 
+    @Override
+    public boolean hasAbility(Ability ability, Game game) {
+        // getAbilities(game) searches all abilities from base and dynamic lists (other)
+        return this.getAbilities(game).contains(ability);
+    }
+
     /**
      * Public in order to support adding abilities to SplitCardHalf's
      *
diff --git a/Mage/src/main/java/mage/designations/Designation.java b/Mage/src/main/java/mage/designations/Designation.java
index 3554e68c50..4537abe3b0 100644
--- a/Mage/src/main/java/mage/designations/Designation.java
+++ b/Mage/src/main/java/mage/designations/Designation.java
@@ -171,8 +171,8 @@ public abstract class Designation implements MageObject {
     }
 
     @Override
-    public boolean hasAbility(UUID abilityId, Game game) {
-        return abilites.containsKey(abilityId);
+    public boolean hasAbility(Ability ability, Game game) {
+        return this.getAbilities().contains(ability);
     }
 
     @Override
diff --git a/Mage/src/main/java/mage/game/combat/Combat.java b/Mage/src/main/java/mage/game/combat/Combat.java
index c502dae73b..32d834b1a1 100644
--- a/Mage/src/main/java/mage/game/combat/Combat.java
+++ b/Mage/src/main/java/mage/game/combat/Combat.java
@@ -306,7 +306,7 @@ public class Combat implements Serializable, Copyable<Combat> {
                 || getAttackers().size() <= 1) {
             return;
         }
-        boolean canBand = attacker.getAbilities().containsKey(BandingAbility.getInstance().getId());
+        boolean canBand = attacker.hasAbility(BandingAbility.getInstance(), game);
         List<Ability> bandsWithOther = new ArrayList<>();
         for (Ability ability : attacker.getAbilities()) {
             if (ability.getClass().equals(BandsWithOtherAbility.class)) {
@@ -390,7 +390,7 @@ public class Combat implements Serializable, Copyable<Combat> {
                         permanent.addBandedCard(creatureId);
                         attacker.addBandedCard(targetId);
                         if (canBand) {
-                            if (!permanent.getAbilities().containsKey(BandingAbility.getInstance().getId())) {
+                            if (!permanent.hasAbility(BandingAbility.getInstance(), game)) {
                                 filter.add(new AbilityPredicate(BandingAbility.class));
                                 canBandWithOther = false;
                             }
@@ -1289,7 +1289,8 @@ public class Combat implements Serializable, Copyable<Combat> {
         if (attacker != null) {
             if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARE_ATTACKER, defenderId, creatureId, playerId))) {
                 if (addAttackerToCombat(creatureId, defenderId, game)) {
-                    if (!attacker.getAbilities().containsKey(VigilanceAbility.getInstance().getId()) && !attacker.getAbilities().containsKey(JohanVigilanceAbility.getInstance().getId())) {
+                    if (!attacker.hasAbility(VigilanceAbility.getInstance(), game)
+                            && !attacker.hasAbility(JohanVigilanceAbility.getInstance(), game)) {
                         if (!attacker.isTapped()) {
                             attacker.setTapped(true);
                             attackersTappedByAttack.add(attacker.getId());
diff --git a/Mage/src/main/java/mage/game/command/Commander.java b/Mage/src/main/java/mage/game/command/Commander.java
index e8a34c3a7d..c2cd0491bf 100644
--- a/Mage/src/main/java/mage/game/command/Commander.java
+++ b/Mage/src/main/java/mage/game/command/Commander.java
@@ -158,12 +158,12 @@ public class Commander implements CommandObject {
     }
 
     @Override
-    public boolean hasAbility(UUID abilityId, Game game) {
-        if (this.getAbilities().containsKey(abilityId)) {
+    public boolean hasAbility(Ability ability, Game game) {
+        if (this.getAbilities().contains(ability)) {
             return true;
         }
         Abilities<Ability> otherAbilities = game.getState().getAllOtherAbilities(getId());
-        return otherAbilities != null && otherAbilities.containsKey(abilityId);
+        return otherAbilities != null && otherAbilities.contains(ability);
     }
 
     @Override
diff --git a/Mage/src/main/java/mage/game/command/Emblem.java b/Mage/src/main/java/mage/game/command/Emblem.java
index 7a4a8896aa..b0de304a98 100644
--- a/Mage/src/main/java/mage/game/command/Emblem.java
+++ b/Mage/src/main/java/mage/game/command/Emblem.java
@@ -173,8 +173,8 @@ public class Emblem implements CommandObject {
     }
 
     @Override
-    public boolean hasAbility(UUID abilityId, Game game) {
-        return abilites.containsKey(abilityId);
+    public boolean hasAbility(Ability ability, Game game) {
+        return getAbilities().contains(ability);
     }
 
     @Override
diff --git a/Mage/src/main/java/mage/game/command/Plane.java b/Mage/src/main/java/mage/game/command/Plane.java
index 070a55c6ab..be457ec6ed 100644
--- a/Mage/src/main/java/mage/game/command/Plane.java
+++ b/Mage/src/main/java/mage/game/command/Plane.java
@@ -182,8 +182,8 @@ public class Plane implements CommandObject {
     }
 
     @Override
-    public boolean hasAbility(UUID abilityId, Game game) {
-        return abilites.containsKey(abilityId);
+    public boolean hasAbility(Ability ability, Game game) {
+        return getAbilities().contains(ability);
     }
 
     @Override
diff --git a/Mage/src/main/java/mage/game/command/planes/TrailOfTheMageRingsPlane.java b/Mage/src/main/java/mage/game/command/planes/TrailOfTheMageRingsPlane.java
index 6a5371e12f..28e32d4519 100644
--- a/Mage/src/main/java/mage/game/command/planes/TrailOfTheMageRingsPlane.java
+++ b/Mage/src/main/java/mage/game/command/planes/TrailOfTheMageRingsPlane.java
@@ -120,7 +120,7 @@ class TrailOfTheMageRingsReboundEffect extends ContinuousEffectImpl {
 
     private void addReboundAbility(Card card, Ability source, Game game) {
         if (filter.match(card, game)) {
-            boolean found = card.getAbilities().stream().anyMatch(ability -> ability instanceof ReboundAbility);
+            boolean found = card.getAbilities(game).containsClass(ReboundAbility.class);
             if (!found) {
                 Ability ability = new ReboundAbility();
                 game.getState().addOtherAbility(card, ability);
diff --git a/Mage/src/main/java/mage/game/stack/Spell.java b/Mage/src/main/java/mage/game/stack/Spell.java
index e9e262711a..42c6ba27ec 100644
--- a/Mage/src/main/java/mage/game/stack/Spell.java
+++ b/Mage/src/main/java/mage/game/stack/Spell.java
@@ -524,8 +524,8 @@ public class Spell extends StackObjImpl implements Card {
     }
 
     @Override
-    public boolean hasAbility(UUID abilityId, Game game) {
-        return card.hasAbility(abilityId, game);
+    public boolean hasAbility(Ability ability, Game game) {
+        return card.hasAbility(ability, game);
     }
 
     @Override
diff --git a/Mage/src/main/java/mage/game/stack/StackAbility.java b/Mage/src/main/java/mage/game/stack/StackAbility.java
index cb4149bcfe..d7f64cac95 100644
--- a/Mage/src/main/java/mage/game/stack/StackAbility.java
+++ b/Mage/src/main/java/mage/game/stack/StackAbility.java
@@ -178,7 +178,7 @@ public class StackAbility extends StackObjImpl implements Ability {
     }
 
     @Override
-    public boolean hasAbility(UUID abilityId, Game game) {
+    public boolean hasAbility(Ability ability, Game game) {
         return false;
     }
 
@@ -672,4 +672,17 @@ public class StackAbility extends StackObjImpl implements Ability {
     public Outcome getCustomOutcome() {
         return this.ability.getCustomOutcome();
     }
+
+    @Override
+    public boolean isSameInstance(Ability ability) {
+        // same instance (by mtg rules) = same object, ID or class+text (you can't check class only cause it can be different by params/text)
+        if (ability == null) {
+            return false;
+        }
+
+        return (this == ability)
+                || (this.getId().equals(ability.getId()))
+                || (this.getOriginalId().equals(ability.getOriginalId()))
+                || (this.getClass() == ability.getClass() && this.getRule().equals(ability.getRule()));
+    }
 }

From eea808d2d64be0ba322c5cad19ff15375c1af19e Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Thu, 28 May 2020 22:38:31 +0400
Subject: [PATCH 069/586] Ability refactor: source improves, deprecated code
 removed;

---
 .../src/main/java/mage/player/ai/ComputerPlayer.java        | 6 +++---
 Mage.Sets/src/mage/cards/a/AnimateDead.java                 | 4 +---
 Mage.Sets/src/mage/cards/a/ArtisanOfForms.java              | 2 +-
 Mage.Sets/src/mage/cards/b/BludgeonBrawl.java               | 4 ++--
 Mage.Sets/src/mage/cards/d/DacksDuplicate.java              | 4 ++--
 Mage.Sets/src/mage/cards/e/EvilTwin.java                    | 2 +-
 Mage.Sets/src/mage/cards/g/GargoyleSentinel.java            | 2 +-
 Mage.Sets/src/mage/cards/g/GravityWell.java                 | 2 +-
 Mage.Sets/src/mage/cards/h/HisokasGuard.java                | 2 +-
 Mage.Sets/src/mage/cards/p/ProteanThaumaturge.java          | 2 +-
 Mage.Sets/src/mage/cards/s/SakashimaTheImpostor.java        | 2 +-
 Mage.Sets/src/mage/cards/u/UncheckedGrowth.java             | 2 +-
 Mage.Sets/src/mage/cards/u/UnstableShapeshifter.java        | 2 +-
 Mage.Sets/src/mage/cards/v/VraskaTheUnseen.java             | 2 +-
 .../mage/test/serverside/performance/SerializationTest.java | 2 +-
 .../mage/abilities/condition/common/BuybackCondition.java   | 2 +-
 .../mage/abilities/condition/common/DashedCondition.java    | 4 ++--
 .../mage/abilities/condition/common/EvokedCondition.java    | 2 +-
 .../java/mage/abilities/effects/common/CopyTokenEffect.java | 2 +-
 .../continuous/GainProtectionFromTypeTargetEffect.java      | 2 +-
 .../main/java/mage/abilities/keyword/TransformAbility.java  | 6 +++---
 Mage/src/main/java/mage/game/GameImpl.java                  | 2 +-
 Mage/src/main/java/mage/game/permanent/Battlefield.java     | 6 +++---
 Mage/src/main/java/mage/game/permanent/PermanentCard.java   | 2 +-
 Mage/src/main/java/mage/game/permanent/PermanentToken.java  | 2 +-
 Mage/src/main/java/mage/players/PlayerImpl.java             | 4 ++--
 Mage/src/main/java/mage/util/functions/AbilityApplier.java  | 2 +-
 27 files changed, 37 insertions(+), 39 deletions(-)

diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java
index 501f560347..a71a8432fb 100644
--- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java
+++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java
@@ -1267,7 +1267,7 @@ public class ComputerPlayer extends PlayerImpl implements Player {
         //play a land that will allow us to play an unplayable
         for (Mana mana : unplayable.keySet()) {
             for (Card card : lands) {
-                for (ActivatedManaAbilityImpl ability : card.getAbilities().getActivatedManaAbilities(Zone.BATTLEFIELD)) {
+                for (ActivatedManaAbilityImpl ability : card.getAbilities(game).getActivatedManaAbilities(Zone.BATTLEFIELD)) {
                     for (Mana netMana : ability.getNetMana(game)) {
                         if (netMana.enough(mana)) {
                             this.playLand(card, game, false);
@@ -1281,7 +1281,7 @@ public class ComputerPlayer extends PlayerImpl implements Player {
         //play a land that will get us closer to playing an unplayable
         for (Mana mana : unplayable.keySet()) {
             for (Card card : lands) {
-                for (ActivatedManaAbilityImpl ability : card.getAbilities().getActivatedManaAbilities(Zone.BATTLEFIELD)) {
+                for (ActivatedManaAbilityImpl ability : card.getAbilities(game).getActivatedManaAbilities(Zone.BATTLEFIELD)) {
                     for (Mana netMana : ability.getNetMana(game)) {
                         if (mana.contains(netMana)) {
                             this.playLand(card, game, false);
@@ -1362,7 +1362,7 @@ public class ComputerPlayer extends PlayerImpl implements Player {
             }
         }
         for (Card card : graveyard.getCards(game)) {
-            for (ActivatedAbility ability : card.getAbilities().getActivatedAbilities(Zone.GRAVEYARD)) {
+            for (ActivatedAbility ability : card.getAbilities(game).getActivatedAbilities(Zone.GRAVEYARD)) {
                 if (ability.canActivate(playerId, game).canActivate()) {
                     ManaOptions abilityOptions = ability.getManaCosts().getOptions();
                     if (abilityOptions.isEmpty()) {
diff --git a/Mage.Sets/src/mage/cards/a/AnimateDead.java b/Mage.Sets/src/mage/cards/a/AnimateDead.java
index 0f833cc16e..4a2f98ff4f 100644
--- a/Mage.Sets/src/mage/cards/a/AnimateDead.java
+++ b/Mage.Sets/src/mage/cards/a/AnimateDead.java
@@ -225,9 +225,7 @@ class AnimateDeadChangeAbilityEffect extends ContinuousEffectImpl implements Sou
                     abilityToRemove = ability;
                 }
             }
-            if (abilityToRemove != null) {
-                permanent.getAbilities().remove(abilityToRemove);
-            }
+            permanent.removeAbility(abilityToRemove, source.getSourceId(), game);
             permanent.addAbility(newAbility, source.getSourceId(), game);
             return true;
         }
diff --git a/Mage.Sets/src/mage/cards/a/ArtisanOfForms.java b/Mage.Sets/src/mage/cards/a/ArtisanOfForms.java
index 1d8a35af4a..a6848d62d5 100644
--- a/Mage.Sets/src/mage/cards/a/ArtisanOfForms.java
+++ b/Mage.Sets/src/mage/cards/a/ArtisanOfForms.java
@@ -63,7 +63,7 @@ class ArtisanOfFormsApplyToPermanent extends ApplyToPermanent {
 
     @Override
     public boolean apply(Game game, Permanent permanent, Ability source, UUID copyToObjectId) {
-        permanent.addAbility(ArtisanOfForms.createAbility(), game);
+        permanent.addAbility(ArtisanOfForms.createAbility(), source.getSourceId(), game);
         return true;
     }
 }
diff --git a/Mage.Sets/src/mage/cards/b/BludgeonBrawl.java b/Mage.Sets/src/mage/cards/b/BludgeonBrawl.java
index 5979a3dca0..71166b33c8 100644
--- a/Mage.Sets/src/mage/cards/b/BludgeonBrawl.java
+++ b/Mage.Sets/src/mage/cards/b/BludgeonBrawl.java
@@ -122,8 +122,8 @@ class BludgeonBrawlGainAbilityEffect extends ContinuousEffectImpl {
                 Permanent permanent = game.getPermanent(permanentId);
                 if (permanent != null) {
                     int convertedManaCost = permanent.getConvertedManaCost();
-                    permanent.addAbility(new EquipAbility(Outcome.BoostCreature, new GenericManaCost(convertedManaCost)), game);
-                    permanent.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(convertedManaCost, 0)), game);
+                    permanent.addAbility(new EquipAbility(Outcome.BoostCreature, new GenericManaCost(convertedManaCost)), source.getSourceId(), game);
+                    permanent.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(convertedManaCost, 0)), source.getSourceId(), game);
                 }
             }
             return true;
diff --git a/Mage.Sets/src/mage/cards/d/DacksDuplicate.java b/Mage.Sets/src/mage/cards/d/DacksDuplicate.java
index 646597f79c..ef7b040837 100644
--- a/Mage.Sets/src/mage/cards/d/DacksDuplicate.java
+++ b/Mage.Sets/src/mage/cards/d/DacksDuplicate.java
@@ -56,8 +56,8 @@ class DacksDuplicateApplyToPermanent extends ApplyToPermanent {
          * 29/05/2014	The ability of Dack's Duplicate doesn't target the
          * creature.
          */
-        permanent.addAbility(new DethroneAbility(), game);
-        permanent.addAbility(HasteAbility.getInstance(), game);
+        permanent.addAbility(new DethroneAbility(), source.getSourceId(), game);
+        permanent.addAbility(HasteAbility.getInstance(), source.getSourceId(), game);
         return true;
     }
 
diff --git a/Mage.Sets/src/mage/cards/e/EvilTwin.java b/Mage.Sets/src/mage/cards/e/EvilTwin.java
index cda27f37ff..f08e6c3402 100644
--- a/Mage.Sets/src/mage/cards/e/EvilTwin.java
+++ b/Mage.Sets/src/mage/cards/e/EvilTwin.java
@@ -69,7 +69,7 @@ class EvilTwinApplyToPermanent extends ApplyToPermanent {
         Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new ManaCostsImpl("{U}{B}"));
         ability.addCost(new TapSourceCost());
         ability.addTarget(new TargetCreaturePermanent(filter));
-        permanent.addAbility(ability, game);
+        permanent.addAbility(ability, source.getSourceId(), game);
         return true;
     }
 
diff --git a/Mage.Sets/src/mage/cards/g/GargoyleSentinel.java b/Mage.Sets/src/mage/cards/g/GargoyleSentinel.java
index a3240598e9..5e7f34fc2c 100644
--- a/Mage.Sets/src/mage/cards/g/GargoyleSentinel.java
+++ b/Mage.Sets/src/mage/cards/g/GargoyleSentinel.java
@@ -66,7 +66,7 @@ class GargoyleSentinelEffect extends ContinuousEffectImpl {
             switch (layer) {
                 case AbilityAddingRemovingEffects_6:
                     if (sublayer == SubLayer.NA) {
-                        permanent.getAbilities().removeIf(entry -> entry.getId().equals(DefenderAbility.getInstance().getId()));
+                        permanent.removeAbility(DefenderAbility.getInstance(), source.getSourceId(), game);
                         permanent.getAbilities().add(FlyingAbility.getInstance());
                     }
                     break;
diff --git a/Mage.Sets/src/mage/cards/g/GravityWell.java b/Mage.Sets/src/mage/cards/g/GravityWell.java
index 00f083c6d7..0efefec910 100644
--- a/Mage.Sets/src/mage/cards/g/GravityWell.java
+++ b/Mage.Sets/src/mage/cards/g/GravityWell.java
@@ -100,7 +100,7 @@ class GravityWellEffect extends ContinuousEffectImpl {
             switch (layer) {
                 case AbilityAddingRemovingEffects_6:
                     if (sublayer == SubLayer.NA) {
-                        permanent.getAbilities().removeIf(entry -> entry.getId().equals(FlyingAbility.getInstance().getId()));
+                        permanent.removeAbility(FlyingAbility.getInstance(), source.getSourceId(), game);
                     }
                     break;
             }
diff --git a/Mage.Sets/src/mage/cards/h/HisokasGuard.java b/Mage.Sets/src/mage/cards/h/HisokasGuard.java
index 0d11887e2a..886b4aea6d 100644
--- a/Mage.Sets/src/mage/cards/h/HisokasGuard.java
+++ b/Mage.Sets/src/mage/cards/h/HisokasGuard.java
@@ -100,7 +100,7 @@ class HisokasGuardGainAbilityTargetEffect extends ContinuousEffectImpl {
         if (hisokasGuard != null && !hisokasGuard.getConnectedCards("HisokasGuard").isEmpty()) {
             Permanent guardedCreature = game.getPermanent(hisokasGuard.getConnectedCards("HisokasGuard").get(0));
             if (guardedCreature != null && hisokasGuard.isTapped()) {
-                guardedCreature.addAbility(ability, game);
+                guardedCreature.addAbility(ability, source.getSourceId(), game);
                 return true;
             } else {
                 // if guard isn't tapped, the effect is no more valid
diff --git a/Mage.Sets/src/mage/cards/p/ProteanThaumaturge.java b/Mage.Sets/src/mage/cards/p/ProteanThaumaturge.java
index d8ecd23dac..337bbe5b63 100644
--- a/Mage.Sets/src/mage/cards/p/ProteanThaumaturge.java
+++ b/Mage.Sets/src/mage/cards/p/ProteanThaumaturge.java
@@ -72,7 +72,7 @@ class ProteanThaumaturgeApplyToPermanent extends ApplyToPermanent {
 
     @Override
     public boolean apply(Game game, Permanent permanent, Ability source, UUID copyToObjectId) {
-        permanent.addAbility(ProteanThaumaturge.createAbility(), game);
+        permanent.addAbility(ProteanThaumaturge.createAbility(), source.getSourceId(), game);
         return true;
     }
 }
diff --git a/Mage.Sets/src/mage/cards/s/SakashimaTheImpostor.java b/Mage.Sets/src/mage/cards/s/SakashimaTheImpostor.java
index 8fed936baa..a0e23f4e3d 100644
--- a/Mage.Sets/src/mage/cards/s/SakashimaTheImpostor.java
+++ b/Mage.Sets/src/mage/cards/s/SakashimaTheImpostor.java
@@ -64,7 +64,7 @@ class SakashimaTheImpostorApplier extends ApplyToPermanent {
         permanent.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD,
                 new CreateDelayedTriggeredAbilityEffect(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ReturnToHandSourceEffect(true)), false),
                 new ManaCostsImpl("{2}{U}{U}")
-        ), game);
+        ), source.getSourceId(), game);
         return true;
     }
 
diff --git a/Mage.Sets/src/mage/cards/u/UncheckedGrowth.java b/Mage.Sets/src/mage/cards/u/UncheckedGrowth.java
index bcac28c4e4..6ca3ce9e35 100644
--- a/Mage.Sets/src/mage/cards/u/UncheckedGrowth.java
+++ b/Mage.Sets/src/mage/cards/u/UncheckedGrowth.java
@@ -63,7 +63,7 @@ public final class UncheckedGrowth extends CardImpl {
             for (UUID permanentId : targetPointer.getTargets(game, source)) {
                 Permanent permanent = game.getPermanent(permanentId);
                 if (permanent != null && permanent.hasSubtype(SubType.SPIRIT, game)) {
-                    permanent.addAbility(TrampleAbility.getInstance(), game);
+                    permanent.addAbility(TrampleAbility.getInstance(), source.getSourceId(), game);
                     affectedTargets++;
                 }
             }
diff --git a/Mage.Sets/src/mage/cards/u/UnstableShapeshifter.java b/Mage.Sets/src/mage/cards/u/UnstableShapeshifter.java
index d91b45043f..fff1570c3f 100644
--- a/Mage.Sets/src/mage/cards/u/UnstableShapeshifter.java
+++ b/Mage.Sets/src/mage/cards/u/UnstableShapeshifter.java
@@ -77,7 +77,7 @@ class UnstableShapeshifterEffect extends OneShotEffect {
         if (targetCreature != null && permanent != null) {
             Permanent blueprintPermanent = game.copyPermanent(Duration.Custom, targetCreature, permanent.getId(), source, new EmptyApplyToPermanent());
             blueprintPermanent.addAbility(new EntersBattlefieldAllTriggeredAbility(Zone.BATTLEFIELD,
-                    new UnstableShapeshifterEffect(), filterAnotherCreature, false, SetTargetPointer.PERMANENT, ""), game);
+                    new UnstableShapeshifterEffect(), filterAnotherCreature, false, SetTargetPointer.PERMANENT, ""), source.getSourceId(), game);
             return true;
         }
         return false;
diff --git a/Mage.Sets/src/mage/cards/v/VraskaTheUnseen.java b/Mage.Sets/src/mage/cards/v/VraskaTheUnseen.java
index 172a230eaa..3cc609ca21 100644
--- a/Mage.Sets/src/mage/cards/v/VraskaTheUnseen.java
+++ b/Mage.Sets/src/mage/cards/v/VraskaTheUnseen.java
@@ -86,7 +86,7 @@ class VraskaTheUnseenGainAbilityEffect extends ContinuousEffectImpl {
     public boolean apply(Game game, Ability source) {
         Permanent permanent = game.getPermanent(source.getSourceId());
         if (permanent != null) {
-            permanent.addAbility(ability, game);
+            permanent.addAbility(ability, source.getSourceId(), game);
             return true;
         }
         return false;
diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/performance/SerializationTest.java b/Mage.Tests/src/test/java/org/mage/test/serverside/performance/SerializationTest.java
index 1720bdcf10..ecb308c9a8 100644
--- a/Mage.Tests/src/test/java/org/mage/test/serverside/performance/SerializationTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/serverside/performance/SerializationTest.java
@@ -40,7 +40,7 @@ public class SerializationTest extends CardTestPlayerBase {
         currentGame.addPermanent(permanent);
 
         // mark damage from infected ability
-        permanent.addAbility(InfectAbility.getInstance(), currentGame);
+        permanent.addAbility(InfectAbility.getInstance(), null, currentGame);
         permanent.markDamage(1, permanent.getId(), currentGame, false, false);
 
         // test compress (it uses default java serialization)
diff --git a/Mage/src/main/java/mage/abilities/condition/common/BuybackCondition.java b/Mage/src/main/java/mage/abilities/condition/common/BuybackCondition.java
index f61f4ead82..e565ea72f3 100644
--- a/Mage/src/main/java/mage/abilities/condition/common/BuybackCondition.java
+++ b/Mage/src/main/java/mage/abilities/condition/common/BuybackCondition.java
@@ -17,7 +17,7 @@ public enum BuybackCondition implements Condition {
     public boolean apply(Game game, Ability source) {
         Card card = game.getCard(source.getSourceId());
         if (card != null) {
-            return card.getAbilities().stream()
+            return card.getAbilities(game).stream()
                     .filter(a -> a instanceof BuybackAbility)
                     .anyMatch(a -> ((BuybackAbility) a).isBuybackActivated(game));
         }
diff --git a/Mage/src/main/java/mage/abilities/condition/common/DashedCondition.java b/Mage/src/main/java/mage/abilities/condition/common/DashedCondition.java
index 72b75e44c8..dd7c1e8790 100644
--- a/Mage/src/main/java/mage/abilities/condition/common/DashedCondition.java
+++ b/Mage/src/main/java/mage/abilities/condition/common/DashedCondition.java
@@ -19,9 +19,9 @@ public enum DashedCondition implements Condition {
     public boolean apply(Game game, Ability source) {
         Card card = game.getCard(source.getSourceId());
         if (card != null) {
-            return card.getAbilities().stream()
+            return card.getAbilities(game).stream()
                     .filter(a -> a instanceof DashAbility)
-                    .anyMatch(d -> ((DashAbility)d).isActivated(source, game));
+                    .anyMatch(d -> ((DashAbility) d).isActivated(source, game));
 
         }
         return false;
diff --git a/Mage/src/main/java/mage/abilities/condition/common/EvokedCondition.java b/Mage/src/main/java/mage/abilities/condition/common/EvokedCondition.java
index 82103a9f05..7733f475f7 100644
--- a/Mage/src/main/java/mage/abilities/condition/common/EvokedCondition.java
+++ b/Mage/src/main/java/mage/abilities/condition/common/EvokedCondition.java
@@ -22,7 +22,7 @@ public enum EvokedCondition implements Condition {
     public boolean apply(Game game, Ability source) {
         Card card = game.getCard(source.getSourceId());
         if (card != null) {
-            return card.getAbilities().stream()
+            return card.getAbilities(game).stream()
                     .filter(ab -> ab instanceof EvokeAbility)
                     .anyMatch(evoke -> ((EvokeAbility) evoke).isActivated(source, game));
         }
diff --git a/Mage/src/main/java/mage/abilities/effects/common/CopyTokenEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CopyTokenEffect.java
index 63b3018d23..d5de78070d 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/CopyTokenEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/CopyTokenEffect.java
@@ -41,7 +41,7 @@ public class CopyTokenEffect extends ContinuousEffectImpl {
         }
         permanent.getAbilities().clear();
         for (Ability ability : token.getAbilities()) {
-            permanent.addAbility(ability, game);
+            permanent.addAbility(ability, source.getSourceId(), game);
         }
         permanent.getPower().setValue(token.getPower().getValue());
         permanent.getToughness().setValue(token.getToughness().getValue());
diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainProtectionFromTypeTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainProtectionFromTypeTargetEffect.java
index d2e00db536..c620c0c4b3 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainProtectionFromTypeTargetEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainProtectionFromTypeTargetEffect.java
@@ -38,7 +38,7 @@ public class GainProtectionFromTypeTargetEffect extends GainAbilityTargetEffect
     public boolean apply(Game game, Ability source) {
         Permanent creature = game.getPermanent(source.getFirstTarget());
         if (creature != null) {
-            creature.addAbility(ability, game);
+            creature.addAbility(ability, source.getSourceId(), game);
             return true;
         }
         return false;
diff --git a/Mage/src/main/java/mage/abilities/keyword/TransformAbility.java b/Mage/src/main/java/mage/abilities/keyword/TransformAbility.java
index fb90104d2d..199560a103 100644
--- a/Mage/src/main/java/mage/abilities/keyword/TransformAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/TransformAbility.java
@@ -38,7 +38,7 @@ public class TransformAbility extends SimpleStaticAbility {
         return "";
     }
 
-    public static void transform(Permanent permanent, Card sourceCard, Game game) {
+    public static void transform(Permanent permanent, Card sourceCard, Game game, Ability source) {
 
         if (sourceCard == null) {
             return;
@@ -63,7 +63,7 @@ public class TransformAbility extends SimpleStaticAbility {
         permanent.setExpansionSetCode(sourceCard.getExpansionSetCode());
         permanent.getAbilities().clear();
         for (Ability ability : sourceCard.getAbilities()) {
-            permanent.addAbility(ability, game);
+            permanent.addAbility(ability, source == null ? null : source.getSourceId(), game, false);
         }
         permanent.getPower().modifyBaseValue(sourceCard.getPower().getValue());
         permanent.getToughness().modifyBaseValue(sourceCard.getToughness().getValue());
@@ -105,7 +105,7 @@ class TransformEffect extends ContinuousEffectImpl {
             return false;
         }
 
-        TransformAbility.transform(permanent, card, game);
+        TransformAbility.transform(permanent, card, game, source);
 
         return true;
 
diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java
index d0930ad3f0..612783bf0a 100644
--- a/Mage/src/main/java/mage/game/GameImpl.java
+++ b/Mage/src/main/java/mage/game/GameImpl.java
@@ -1647,7 +1647,7 @@ public abstract class GameImpl implements Game, Serializable {
             }
             newBluePrint.assignNewId();
             if (copyFromPermanent.isTransformed()) {
-                TransformAbility.transform(newBluePrint, newBluePrint.getSecondCardFace(), this);
+                TransformAbility.transform(newBluePrint, newBluePrint.getSecondCardFace(), this, source);
             }
         }
         if (applier != null) {
diff --git a/Mage/src/main/java/mage/game/permanent/Battlefield.java b/Mage/src/main/java/mage/game/permanent/Battlefield.java
index 6aacd3e45f..d2e11c2c48 100644
--- a/Mage/src/main/java/mage/game/permanent/Battlefield.java
+++ b/Mage/src/main/java/mage/game/permanent/Battlefield.java
@@ -327,16 +327,16 @@ public class Battlefield implements Serializable {
         }
     }
 
-    public List<Permanent> getPhasedIn(UUID controllerId) {
+    public List<Permanent> getPhasedIn(Game game, UUID controllerId) {
         return field.values()
                 .stream()
-                .filter(perm -> perm.getAbilities().containsKey(PhasingAbility.getInstance().getId())
+                .filter(perm -> perm.hasAbility(PhasingAbility.getInstance(), game)
                 && perm.isPhasedIn()
                 && perm.isControlledBy(controllerId))
                 .collect(Collectors.toList());
     }
 
-    public List<Permanent> getPhasedOut(UUID controllerId) {
+    public List<Permanent> getPhasedOut(Game game, UUID controllerId) {
         return field.values()
                 .stream()
                 .filter(perm -> !perm.isPhasedIn() && perm.isControlledBy(controllerId))
diff --git a/Mage/src/main/java/mage/game/permanent/PermanentCard.java b/Mage/src/main/java/mage/game/permanent/PermanentCard.java
index bb387139e6..f66b260eea 100644
--- a/Mage/src/main/java/mage/game/permanent/PermanentCard.java
+++ b/Mage/src/main/java/mage/game/permanent/PermanentCard.java
@@ -53,7 +53,7 @@ public class PermanentCard extends PermanentImpl {
             if (game.getState().getValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + getId()) != null) {
                 game.getState().setValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + getId(), null);
                 setTransformed(true);
-                TransformAbility.transform(this, getSecondCardFace(), game);
+                TransformAbility.transform(this, getSecondCardFace(), game, null);
             }
         }
     }
diff --git a/Mage/src/main/java/mage/game/permanent/PermanentToken.java b/Mage/src/main/java/mage/game/permanent/PermanentToken.java
index ba809c2056..48c4f4bae6 100644
--- a/Mage/src/main/java/mage/game/permanent/PermanentToken.java
+++ b/Mage/src/main/java/mage/game/permanent/PermanentToken.java
@@ -59,7 +59,7 @@ public class PermanentToken extends PermanentImpl {
         } else {
             // first time -> create ContinuousEffects only once
             for (Ability ability : token.getAbilities()) {
-                this.addAbility(ability, game);
+                this.addAbility(ability, null, game);
             }
         }
         this.abilities.setControllerId(this.controllerId);
diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java
index f79bc6d165..5c8dd00ea5 100644
--- a/Mage/src/main/java/mage/players/PlayerImpl.java
+++ b/Mage/src/main/java/mage/players/PlayerImpl.java
@@ -1759,8 +1759,8 @@ public abstract class PlayerImpl implements Player, Serializable {
     @Override
     public void phasing(Game game) {
         //20091005 - 502.1
-        List<Permanent> phasedOut = game.getBattlefield().getPhasedOut(playerId);
-        for (Permanent permanent : game.getBattlefield().getPhasedIn(playerId)) {
+        List<Permanent> phasedOut = game.getBattlefield().getPhasedOut(game, playerId);
+        for (Permanent permanent : game.getBattlefield().getPhasedIn(game, playerId)) {
             // 502.15i When a permanent phases out, any local enchantments or Equipment
             // attached to that permanent phase out at the same time. This alternate way of
             // phasing out is known as phasing out "indirectly." An enchantment or Equipment
diff --git a/Mage/src/main/java/mage/util/functions/AbilityApplier.java b/Mage/src/main/java/mage/util/functions/AbilityApplier.java
index abbc57d354..215ba18200 100644
--- a/Mage/src/main/java/mage/util/functions/AbilityApplier.java
+++ b/Mage/src/main/java/mage/util/functions/AbilityApplier.java
@@ -21,7 +21,7 @@ public class AbilityApplier extends ApplyToPermanent {
 
     @Override
     public boolean apply(Game game, Permanent permanent, Ability source, UUID copyToObjectId) {
-        permanent.addAbility(ability, game);
+        permanent.addAbility(ability, source.getSourceId(), game);
         return true;
     }
 

From f3f1c29926415c81645bdb9b11183e9aa336fdff Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Thu, 28 May 2020 23:02:20 +0400
Subject: [PATCH 070/586] Ability refactor: face down;

---
 .../mage/cards/m/MuragandaPetroglyphs.java    | 27 +++++++++--
 .../abilities/keywords/ManifestTest.java      | 46 +++++++++++++++++-
 .../single/fut/MuragandaPetroglyphsTest.java  | 33 +++++++++++--
 .../abilities/common/TurnFaceUpAbility.java   |  1 +
 .../BecomesFaceDownCreatureAllEffect.java     | 47 +++++++++----------
 .../BecomesFaceDownCreatureEffect.java        | 28 ++++++-----
 .../java/mage/game/permanent/Permanent.java   |  1 +
 .../mage/game/permanent/PermanentImpl.java    |  1 -
 8 files changed, 137 insertions(+), 47 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/m/MuragandaPetroglyphs.java b/Mage.Sets/src/mage/cards/m/MuragandaPetroglyphs.java
index 257e555abf..6271916674 100644
--- a/Mage.Sets/src/mage/cards/m/MuragandaPetroglyphs.java
+++ b/Mage.Sets/src/mage/cards/m/MuragandaPetroglyphs.java
@@ -1,8 +1,5 @@
-
 package mage.cards.m;
 
-import java.util.Objects;
-import java.util.UUID;
 import mage.MageObject;
 import mage.abilities.Abilities;
 import mage.abilities.Ability;
@@ -20,8 +17,10 @@ import mage.filter.common.FilterCreaturePermanent;
 import mage.filter.predicate.Predicate;
 import mage.game.Game;
 
+import java.util.Objects;
+import java.util.UUID;
+
 /**
- *
  * @author anonymous
  */
 public final class MuragandaPetroglyphs extends CardImpl {
@@ -54,6 +53,12 @@ public final class MuragandaPetroglyphs extends CardImpl {
 
 class NoAbilityPredicate implements Predicate<MageObject> {
 
+    // Muraganda Petroglyphs gives a bonus only to creatures that have no rules text at all. This includes true vanilla
+    // creatures (such as Grizzly Bears), face-down creatures, many tokens, and creatures that have lost their abilities
+    // (due to Ovinize, for example). Any ability of any kind, whether or not the ability functions in the on the
+    // battlefield zone, including things like “Cycling {2}” means the creature doesn’t get the bonus.
+    // (2007-05-01)
+
     @Override
     public boolean apply(MageObject input, Game game) {
         boolean isFaceDown = false;
@@ -65,8 +70,20 @@ class NoAbilityPredicate implements Predicate<MageObject> {
             abilities = input.getAbilities();
         }
         if (isFaceDown) {
+            // Some Auras and Equipment grant abilities to creatures, meaning the affected creature would no longer
+            // get the +2/+2 bonus. For example, Flight grants flying to the enchanted creature. Other Auras and
+            // Equipment do not, meaning the affected creature would continue to get the +2/+2 bonus. For example,
+            // Dehydration states something now true about the enchanted creature, but doesn’t give it any abilities.
+            // Auras and Equipment that grant abilities will use the words “gains” or “has,” and they’ll list a keyword
+            // ability or an ability in quotation marks.
+            // (2007-05-01)
+
             for (Ability ability : abilities) {
-                if (!ability.getSourceId().equals(input.getId()) && !ability.getClass().equals(JohanVigilanceAbility.class)) {
+                if (ability.getWorksFaceDown()) {
+                    // inner face down abilities like turn up and becomes creature
+                    continue;
+                }
+                if (!Objects.equals(ability.getClass(), SpellAbility.class) && !ability.getClass().equals(JohanVigilanceAbility.class)) {
                     return false;
                 }
             }
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ManifestTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ManifestTest.java
index 850580b7b4..82a81887b7 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ManifestTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ManifestTest.java
@@ -274,9 +274,12 @@ public class ManifestTest extends CardTestPlayerBase {
 
     // Check if a Megamorph card is manifested and turned face up by their megamorph ability
     // it gets the +1/+1 counter.
+    // 701.33c
+    // If a card with morph is manifested, its controller may turn that card face up using
+    // either the procedure described in rule 702.36e to turn a face-down permanent with morph face up
+    // or the procedure described above to turn a manifested permanent face up.
     @Test
-    public void testManifestMegamorph() {
-
+    public void testManifestMegamorph_TurnUpByMegamorphCost() {
         addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2);
         addCard(Zone.BATTLEFIELD, playerB, "Forest", 6);
         // {1}{B}, {T}, Sacrifice another creature: Manifest the top card of your library.
@@ -295,6 +298,7 @@ public class ManifestTest extends CardTestPlayerBase {
 
         activateAbility(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "{5}{G}: Turn");
 
+        setStrictChooseMode(true);
         setStopAt(2, PhaseStep.END_TURN);
         execute();
         assertAllCommandsUsed();
@@ -310,7 +314,45 @@ public class ManifestTest extends CardTestPlayerBase {
         assertPowerToughness(playerB, "Aerie Bowmasters", 4, 5); // 3/4  and the +1/+1 counter from Megamorph
         Permanent aerie = getPermanent("Aerie Bowmasters", playerB);
         Assert.assertTrue("Aerie Bowmasters has to be green", aerie != null && aerie.getColor(currentGame).isGreen());
+    }
 
+    @Test
+    public void testManifestMegamorph_TurnUpBySimpleCost() {
+        addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2);
+        addCard(Zone.BATTLEFIELD, playerB, "Forest", 4);
+        // {1}{B}, {T}, Sacrifice another creature: Manifest the top card of your library.
+        addCard(Zone.BATTLEFIELD, playerB, "Qarsi High Priest", 1);
+        addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1);
+
+        // {2}{G}{G}
+        // Reach (This creature can block creatures with flying.)
+        // Megamorph {5}{G}
+        addCard(Zone.LIBRARY, playerB, "Aerie Bowmasters", 1);
+        addCard(Zone.LIBRARY, playerB, "Mountain", 1);
+
+        skipInitShuffling();
+
+        activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{1}{B}, {T}, Sacrifice another creature");
+        setChoice(playerB, "Silvercoat Lion");
+
+        activateAbility(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "{2}{G}{G}: Turn");
+
+        setStrictChooseMode(true);
+        setStopAt(2, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        // no life gain
+        assertLife(playerA, 20);
+        assertLife(playerB, 20);
+
+        assertGraveyardCount(playerB, "Silvercoat Lion", 1);
+
+        assertPermanentCount(playerB, EmptyNames.FACE_DOWN_CREATURE.toString(), 0);
+        assertPermanentCount(playerB, "Aerie Bowmasters", 1);
+        assertPowerToughness(playerB, "Aerie Bowmasters", 3, 4); // 3/4 without counter (megamorph not used)
+        Permanent aerie = getPermanent("Aerie Bowmasters", playerB);
+        Assert.assertTrue("Aerie Bowmasters has to be green", aerie != null && aerie.getColor(currentGame).isGreen());
     }
 
     /**
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/fut/MuragandaPetroglyphsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/fut/MuragandaPetroglyphsTest.java
index a7856156d9..4345d94086 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/single/fut/MuragandaPetroglyphsTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/fut/MuragandaPetroglyphsTest.java
@@ -1,5 +1,6 @@
 package org.mage.test.cards.single.fut;
 
+import mage.abilities.keyword.HasteAbility;
 import mage.constants.EmptyNames;
 import mage.constants.PhaseStep;
 import mage.constants.Zone;
@@ -31,8 +32,10 @@ public class MuragandaPetroglyphsTest extends CardTestPlayerBase {
 
         addCard(Zone.BATTLEFIELD, playerA, "Muraganda Petroglyphs", 1);
 
+        setStrictChooseMode(true);
         setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
         execute();
+        assertAllCommandsUsed();
 
         assertPowerToughness(playerA, "Grizzly Bears", 4, 4, Filter.ComparisonScope.Any);
         assertPowerToughness(playerB, "Grizzly Bears", 4, 4, Filter.ComparisonScope.Any);
@@ -41,15 +44,20 @@ public class MuragandaPetroglyphsTest extends CardTestPlayerBase {
 
     @Test
     public void faceDownCreaturesTest() {
+        // Morph {4}{G}
         addCard(Zone.HAND, playerA, "Pine Walker");
         addCard(Zone.BATTLEFIELD, playerA, "Forest", 3);
+        //
+        // Creatures with no abilities get +2/+2.
         addCard(Zone.BATTLEFIELD, playerA, "Muraganda Petroglyphs", 1);
 
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Pine Walker");
         setChoice(playerA, "Yes"); // cast it face down as 2/2 creature
 
+        setStrictChooseMode(true);
         setStopAt(1, PhaseStep.BEGIN_COMBAT);
         execute();
+        assertAllCommandsUsed();
 
         assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
         assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 4, 4);
@@ -57,21 +65,26 @@ public class MuragandaPetroglyphsTest extends CardTestPlayerBase {
 
     @Test
     public void faceDownGainedAbilityTest() {
+        // Morph {4}{G}
         addCard(Zone.HAND, playerA, "Pine Walker");
         addCard(Zone.BATTLEFIELD, playerA, "Forest", 5);
         addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
         addCard(Zone.BATTLEFIELD, playerA, "Mass Hysteria"); // All creatures have haste.
 
+        // Creatures with no abilities get +2/+2.
         addCard(Zone.BATTLEFIELD, playerA, "Muraganda Petroglyphs", 1);
 
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Pine Walker");
         setChoice(playerA, "Yes"); // cast it face down as 2/2 creature
 
+        setStrictChooseMode(true);
         setStopAt(1, PhaseStep.BEGIN_COMBAT);
         execute();
+        assertAllCommandsUsed();
 
         assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
-        assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 2);
+        //assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 2); // no boost (permanent have haste)
+        assertAbility(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), HasteAbility.getInstance(), true);
     }
 
     @Test
@@ -83,8 +96,10 @@ public class MuragandaPetroglyphsTest extends CardTestPlayerBase {
 
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Raise the Alarm");
 
+        setStrictChooseMode(true);
         setStopAt(1, PhaseStep.BEGIN_COMBAT);
         execute();
+        assertAllCommandsUsed();
 
         assertPowerToughness(playerA, "Soldier", 3, 3);
     }
@@ -99,8 +114,10 @@ public class MuragandaPetroglyphsTest extends CardTestPlayerBase {
 
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ovinize", "Goblin Guide");
 
+        setStrictChooseMode(true);
         setStopAt(1, PhaseStep.BEGIN_COMBAT);
         execute();
+        assertAllCommandsUsed();
 
         assertPowerToughness(playerB, "Goblin Guide", 2, 3);
     }
@@ -110,8 +127,10 @@ public class MuragandaPetroglyphsTest extends CardTestPlayerBase {
         addCard(Zone.BATTLEFIELD, playerA, "Hundroog", 1); // Cycling {3}, 4/7
         addCard(Zone.BATTLEFIELD, playerA, "Muraganda Petroglyphs", 1);
 
+        setStrictChooseMode(true);
         setStopAt(1, PhaseStep.BEGIN_COMBAT);
         execute();
+        assertAllCommandsUsed();
 
         assertPowerToughness(playerA, "Hundroog", 4, 7);
     }
@@ -126,11 +145,12 @@ public class MuragandaPetroglyphsTest extends CardTestPlayerBase {
         addCard(Zone.BATTLEFIELD, playerA, "Muraganda Petroglyphs", 1);
         addCard(Zone.HAND, playerA, "Vastwood Zendikon");
 
-
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Vastwood Zendikon", "Forest");
 
+        setStrictChooseMode(true);
         setStopAt(1, PhaseStep.BEGIN_COMBAT);
         execute();
+        assertAllCommandsUsed();
 
         assertPowerToughness(playerA, "Forest", 6, 4);
 
@@ -160,8 +180,10 @@ public class MuragandaPetroglyphsTest extends CardTestPlayerBase {
 
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dehydration", "Runeclaw Bear");
 
+        setStrictChooseMode(true);
         setStopAt(1, PhaseStep.BEGIN_COMBAT);
         execute();
+        assertAllCommandsUsed();
 
         assertPowerToughness(playerA, "Grizzly Bears", 4, 2);
 
@@ -179,11 +201,14 @@ public class MuragandaPetroglyphsTest extends CardTestPlayerBase {
         addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1);
 
         addCard(Zone.HAND, playerA, "Shadow Slice"); // {4}{B}
-        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Shadow Slice");
-        setChoice(playerA, "Grizzly Bears");
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Shadow Slice", playerB);
+        setChoice(playerA, "Yes"); // do cipher
+        addTarget(playerA, "Grizzly Bears");
 
+        setStrictChooseMode(true);
         setStopAt(1, PhaseStep.BEGIN_COMBAT);
         execute();
+        assertAllCommandsUsed();
 
         assertPowerToughness(playerA, "Grizzly Bears", 2, 2);
     }
diff --git a/Mage/src/main/java/mage/abilities/common/TurnFaceUpAbility.java b/Mage/src/main/java/mage/abilities/common/TurnFaceUpAbility.java
index de4daaa7ef..7a76fc7955 100644
--- a/Mage/src/main/java/mage/abilities/common/TurnFaceUpAbility.java
+++ b/Mage/src/main/java/mage/abilities/common/TurnFaceUpAbility.java
@@ -40,6 +40,7 @@ public class TurnFaceUpAbility extends SpecialAction {
         this.usesStack = false;
         this.abilityType = AbilityType.SPECIAL_ACTION;
         this.setRuleVisible(false); // will be made visible only to controller in CardView
+        this.setWorksFaceDown(true);
     }
 
     public TurnFaceUpAbility(final TurnFaceUpAbility ability) {
diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureAllEffect.java
index d91a9a4d3d..202788360b 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureAllEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureAllEffect.java
@@ -1,11 +1,5 @@
-
 package mage.abilities.effects.common.continuous;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
 import mage.MageObjectReference;
 import mage.ObjectColor;
 import mage.abilities.Ability;
@@ -13,23 +7,20 @@ import mage.abilities.common.TurnFaceUpAbility;
 import mage.abilities.effects.ContinuousEffectImpl;
 import mage.abilities.keyword.MorphAbility;
 import mage.cards.Card;
-import mage.constants.CardType;
-import mage.constants.Duration;
-import mage.constants.Layer;
-import mage.constants.Outcome;
-import mage.constants.SubLayer;
+import mage.constants.*;
 import mage.filter.FilterPermanent;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
 
+import java.util.*;
+
 /**
- *
  * @author LevelX2
  */
 
 public class BecomesFaceDownCreatureAllEffect extends ContinuousEffectImpl implements SourceEffect {
 
-    protected Map<UUID,Ability> turnFaceUpAbilityMap = new HashMap<>();
+    protected Map<UUID, Ability> turnFaceUpAbilityMap = new HashMap<>();
     protected FilterPermanent filter;
 
     public BecomesFaceDownCreatureAllEffect(FilterPermanent filter) {
@@ -40,7 +31,7 @@ public class BecomesFaceDownCreatureAllEffect extends ContinuousEffectImpl imple
 
     public BecomesFaceDownCreatureAllEffect(final BecomesFaceDownCreatureAllEffect effect) {
         super(effect);
-        for (Map.Entry<UUID,Ability> entry: effect.turnFaceUpAbilityMap.entrySet()) {
+        for (Map.Entry<UUID, Ability> entry : effect.turnFaceUpAbilityMap.entrySet()) {
             this.turnFaceUpAbilityMap.put(entry.getKey(), entry.getValue());
         }
         this.filter = effect.filter.copy();
@@ -54,16 +45,16 @@ public class BecomesFaceDownCreatureAllEffect extends ContinuousEffectImpl imple
     @Override
     public void init(Ability source, Game game) {
         super.init(source, game);
-        for (Permanent perm: game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) {
+        for (Permanent perm : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) {
             if (!perm.isFaceDown(game) && !perm.isTransformable()) {
                 affectedObjectList.add(new MageObjectReference(perm, game));
                 perm.setFaceDown(true, game);
                 // check for Morph
                 Card card = game.getCard(perm.getId());
                 if (card != null) {
-                    for (Ability ability: card.getAbilities()) {
+                    for (Ability ability : card.getAbilities()) {
                         if (ability instanceof MorphAbility) {
-                            this.turnFaceUpAbilityMap.put(card.getId(), new TurnFaceUpAbility(((MorphAbility)ability).getMorphCosts()));
+                            this.turnFaceUpAbilityMap.put(card.getId(), new TurnFaceUpAbility(((MorphAbility) ability).getMorphCosts()));
                         }
                     }
                 }
@@ -74,7 +65,7 @@ public class BecomesFaceDownCreatureAllEffect extends ContinuousEffectImpl imple
     @Override
     public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
         boolean targetExists = false;
-        for (MageObjectReference mor: affectedObjectList) {
+        for (MageObjectReference mor : affectedObjectList) {
             Permanent permanent = mor.getPermanent(game);
             if (permanent != null && permanent.isFaceDown(game)) {
                 targetExists = true;
@@ -92,27 +83,35 @@ public class BecomesFaceDownCreatureAllEffect extends ContinuousEffectImpl imple
                         break;
                     case AbilityAddingRemovingEffects_6:
                         Card card = game.getCard(permanent.getId()); //
-                        List<Ability> abilities = new ArrayList<>();
+                        List<Ability> abilitiesToRemove = new ArrayList<>();
                         for (Ability ability : permanent.getAbilities()) {
+
+                            // keep gained abilities from other sources, removes only own (card text)
                             if (card != null && !card.getAbilities().contains(ability)) {
-                                // gained abilities from other sources won't be removed
                                 continue;
                             }
-                            // TODO: Add flag "works also face down" to ability and use it to control ability removement instead of instanceof check
+
+                            // 701.33c
+                            // If a card with morph is manifested, its controller may turn that card face up using
+                            // either the procedure described in rule 702.36e to turn a face-down permanent with morph face up
+                            // or the procedure described above to turn a manifested permanent face up.
+                            //
+                            // so keep all tune face up abilities and other face down compatible
                             if (ability.getWorksFaceDown()) {
                                 ability.setRuleVisible(false);
                                 continue;
                             }
+
                             if (!ability.getRuleVisible() && !ability.getEffects().isEmpty()) {
                                 if (ability.getEffects().get(0) instanceof BecomesFaceDownCreatureAllEffect) {
                                     continue;
                                 }
                             }
-                            abilities.add(ability);
+                            abilitiesToRemove.add(ability);
                         }
-                        permanent.getAbilities().removeAll(abilities);
+                        permanent.removeAbilities(abilitiesToRemove, source.getSourceId(), game);
                         if (turnFaceUpAbilityMap.containsKey(permanent.getId())) {
-                            permanent.addAbility(turnFaceUpAbilityMap.get(permanent.getId()), game);
+                            permanent.addAbility(turnFaceUpAbilityMap.get(permanent.getId()), source.getSourceId(), game);
                         }
                         break;
                     case PTChangingEffects_7:
diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureEffect.java
index 3471377788..06e5f1c012 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureEffect.java
@@ -1,8 +1,5 @@
-
 package mage.abilities.effects.common.continuous;
 
-import java.util.ArrayList;
-import java.util.List;
 import mage.MageObjectReference;
 import mage.ObjectColor;
 import mage.abilities.Ability;
@@ -12,14 +9,13 @@ import mage.abilities.costs.Costs;
 import mage.abilities.costs.CostsImpl;
 import mage.abilities.effects.ContinuousEffectImpl;
 import mage.cards.Card;
-import mage.constants.CardType;
-import mage.constants.Duration;
-import mage.constants.Layer;
-import mage.constants.Outcome;
-import mage.constants.SubLayer;
+import mage.constants.*;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * This effect lets the card be a 2/2 face-down creature, with no text, no name,
  * no subtypes, and no mana cost, if it's face down on the battlefield. And it
@@ -149,21 +145,31 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl implemen
                     Card card = game.getCard(permanent.getId()); //
                     List<Ability> abilitiesToRemove = new ArrayList<>();
                     for (Ability ability : permanent.getAbilities()) {
+
+                        // keep gained abilities from other sources, removes only own (card text)
                         if (card != null && !card.getAbilities().contains(ability)) {
-                            // gained abilities from other sources won't be removed
                             continue;
                         }
+
+                        // 701.33c
+                        // If a card with morph is manifested, its controller may turn that card face up using
+                        // either the procedure described in rule 702.36e to turn a face-down permanent with morph face up
+                        // or the procedure described above to turn a manifested permanent face up.
+                        //
+                        // so keep all tune face up abilities and other face down compatible
                         if (ability.getWorksFaceDown()) {
                             ability.setRuleVisible(false);
                             continue;
-                        } else if (!ability.getRuleVisible() && !ability.getEffects().isEmpty()) {
+                        }
+
+                        if (!ability.getRuleVisible() && !ability.getEffects().isEmpty()) {
                             if (ability.getEffects().get(0) instanceof BecomesFaceDownCreatureEffect) {
                                 continue;
                             }
                         }
                         abilitiesToRemove.add(ability);
                     }
-                    permanent.getAbilities().removeAll(abilitiesToRemove);
+                    permanent.removeAbilities(abilitiesToRemove, source.getSourceId(), game);
                     if (turnFaceUpAbility != null) {
                         permanent.addAbility(turnFaceUpAbility, source.getSourceId(), game);
                     }
diff --git a/Mage/src/main/java/mage/game/permanent/Permanent.java b/Mage/src/main/java/mage/game/permanent/Permanent.java
index 5293323922..a8f040086e 100644
--- a/Mage/src/main/java/mage/game/permanent/Permanent.java
+++ b/Mage/src/main/java/mage/game/permanent/Permanent.java
@@ -156,6 +156,7 @@ public interface Permanent extends Card, Controllable {
 
     void addAbility(Ability ability, UUID sourceId, Game game);
 
+    @Deprecated // use addAbility(Ability ability, UUID sourceId, Game game) instead
     void addAbility(Ability ability, UUID sourceId, Game game, boolean createNewId);
 
     void removeAllAbilities(UUID sourceId, Game game);
diff --git a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java
index fcc676c089..81208ef8e6 100644
--- a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java
+++ b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java
@@ -365,7 +365,6 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
     }
 
     @Override
-    @Deprecated // use addAbility(Ability ability, UUID sourceId, Game game) instead
     public void addAbility(Ability ability, UUID sourceId, Game game, boolean createNewId) {
         // singleton abilities -- only one instance
         // other abilities -- any amount of instances

From d63a3e88cee4c4b57b86e76f09913700b2a5b17d Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Thu, 28 May 2020 23:07:36 +0400
Subject: [PATCH 071/586] Ability refactor: fixed broken effects timestamp in
 some use cases

---
 Mage.Sets/src/mage/cards/s/Showstopper.java   |  3 -
 .../abilities/other/GainAbilitiesTest.java    | 70 ++++++++++++++++
 .../LandTypeChangingEffectsTest.java          | 27 +++++--
 .../test/cards/planeswalker/GideonTest.java   |  9 ++-
 .../cards/triggers/dies/ShowstopperTest.java  | 79 +++++++++++++++++--
 .../abilities/effects/ContinuousEffects.java  | 67 ++++++++++------
 6 files changed, 213 insertions(+), 42 deletions(-)
 create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/GainAbilitiesTest.java

diff --git a/Mage.Sets/src/mage/cards/s/Showstopper.java b/Mage.Sets/src/mage/cards/s/Showstopper.java
index b4c5fc75e8..c8b70747a2 100644
--- a/Mage.Sets/src/mage/cards/s/Showstopper.java
+++ b/Mage.Sets/src/mage/cards/s/Showstopper.java
@@ -1,4 +1,3 @@
-
 package mage.cards.s;
 
 import java.util.UUID;
@@ -34,8 +33,6 @@ public final class Showstopper extends CardImpl {
     public Showstopper (UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{B}{R}");
 
-
-
         // Until end of turn, creatures you control gain "When this creature dies, it deals 2 damage to target creature an opponent controls."
         TriggeredAbility ability = new DiesTriggeredAbility(new DamageTargetEffect(2, "it"), false);
         Target target = new TargetCreaturePermanent(filter2);
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/GainAbilitiesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/GainAbilitiesTest.java
new file mode 100644
index 0000000000..03a690f696
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/GainAbilitiesTest.java
@@ -0,0 +1,70 @@
+package org.mage.test.cards.abilities.other;
+
+import mage.abilities.effects.common.DrawCardSourceControllerEffect;
+import mage.abilities.keyword.VigilanceAbility;
+import mage.constants.PhaseStep;
+import mage.constants.Zone;
+import mage.game.permanent.Permanent;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestPlayerBase;
+
+/**
+ * @author JayDi85
+ */
+public class GainAbilitiesTest extends CardTestPlayerBase {
+
+    @Test
+    public void test_AttachmentSingleton() {
+        // {2}{W}
+        // Enchanted creature gets +2/+2.
+        // Enchanted creature has vigilance as long as you control a black or green permanent.
+        addCard(Zone.HAND, playerA, "Abzan Runemark@attach", 2);
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 6);
+        //
+        addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears@bear", 1); // 2/2
+
+        // attach all
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "@attach.1", "@bear");
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "@attach.2", "@bear");
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+        checkAbility("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "@bear", VigilanceAbility.class, true);
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        Permanent permanent = getPermanent("Balduvian Bears");
+        Assert.assertEquals("must have only 1 singleton ability instance from two attachments",
+                1, permanent.getAbilities(currentGame).stream().filter(a -> a instanceof VigilanceAbility).count());
+    }
+
+    @Test
+    public void test_AttachmentUnique() {
+        // {R}
+        // Enchanted creature has "{R}, {T}, Discard a card: Draw a card."
+        addCard(Zone.HAND, playerA, "Epiphany Storm@attach", 2);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
+        //
+        addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears@bear", 1); // 2/2
+
+        // attach all
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "@attach.1", "@bear");
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "@attach.2", "@bear");
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+        //checkAbility("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "@bear", VigilanceAbility.class, true);
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        Permanent permanent = getPermanent("Balduvian Bears");
+        Assert.assertEquals("must have 2 dynamic ability instances from two attachments",
+                2, permanent.getAbilities(currentGame).stream().filter(
+                        a -> a.getEffects().stream().anyMatch(e -> e instanceof DrawCardSourceControllerEffect)
+                ).count());
+    }
+
+}
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/LandTypeChangingEffectsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/LandTypeChangingEffectsTest.java
index 224c316f9a..d011d952b3 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/LandTypeChangingEffectsTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/LandTypeChangingEffectsTest.java
@@ -14,20 +14,17 @@ import org.junit.Test;
 import org.mage.test.serverside.base.CardTestPlayerBase;
 
 /**
- *
  * @author LevelX2
  */
 public class LandTypeChangingEffectsTest extends CardTestPlayerBase {
 
     /**
-     *
      * Playing a commander game. Opponent had a Magus of the Moon, and I later
      * dropped a Chromatic Lantern.
-     *
+     * <p>
      * I was not allowed to use the Chromatic Lantern's ability. Since layers
      * are tricky I asked on the Judge's chat to confirm and the user "luma"
      * said it should work on this scenario.
-     *
      */
     @Test
     public void testMagusOfTheMoonAndChromaticLantern() {
@@ -42,8 +39,10 @@ public class LandTypeChangingEffectsTest extends CardTestPlayerBase {
 
         castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Chromatic Lantern");
 
+        setStrictChooseMode(true);
         setStopAt(2, PhaseStep.BEGIN_COMBAT);
         execute();
+        assertAllCommandsUsed();
 
         assertPermanentCount(playerB, "Chromatic Lantern", 1);
 
@@ -66,8 +65,11 @@ public class LandTypeChangingEffectsTest extends CardTestPlayerBase {
         castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Chromatic Lantern");
 
         castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Magus of the Moon");
+
+        setStrictChooseMode(true);
         setStopAt(3, PhaseStep.BEGIN_COMBAT);
         execute();
+        assertAllCommandsUsed();
 
         assertPermanentCount(playerB, "Chromatic Lantern", 1);
         assertPermanentCount(playerA, "Magus of the Moon", 1);
@@ -97,8 +99,11 @@ public class LandTypeChangingEffectsTest extends CardTestPlayerBase {
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Aquitect's Will", "Forbidding Watchtower");
 
         activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{1}{W}:");
+
+        setStrictChooseMode(true);
         setStopAt(2, PhaseStep.BEGIN_COMBAT);
         execute();
+        assertAllCommandsUsed();
 
         assertGraveyardCount(playerA, "Aquitect's Will", 1);
 
@@ -128,8 +133,10 @@ public class LandTypeChangingEffectsTest extends CardTestPlayerBase {
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bloodmoon);
         playLand(1, PhaseStep.POSTCOMBAT_MAIN, playerA, urborgtoy);
 
+        setStrictChooseMode(true);
         setStopAt(2, PhaseStep.PRECOMBAT_MAIN);
         execute();
+        assertAllCommandsUsed();
 
         assertPermanentCount(playerA, bloodmoon, 1);
         assertPermanentCount(playerA, urborgtoy, 1);
@@ -157,8 +164,10 @@ public class LandTypeChangingEffectsTest extends CardTestPlayerBase {
         playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, urborgtoy);
         castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, bloodmoon);
 
+        setStrictChooseMode(true);
         setStopAt(2, PhaseStep.PRECOMBAT_MAIN);
         execute();
+        assertAllCommandsUsed();
 
         assertPermanentCount(playerA, bloodmoon, 1);
         assertPermanentCount(playerA, urborgtoy, 1);
@@ -176,7 +185,7 @@ public class LandTypeChangingEffectsTest extends CardTestPlayerBase {
        In terms of time-stamp order, Urborg was down first, then Kormus Bell, then Quicksilver.
        When I put a flood counter on a basic swamp, it would become a 0/0 instead of a 1/1 and die.
      */
-    
+
     @Test
     public void testCormusBellAfterUrborg() {
         // Land - Legendary
@@ -198,14 +207,14 @@ public class LandTypeChangingEffectsTest extends CardTestPlayerBase {
         castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Kormus Bell");
 
         castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Quicksilver Fountain");
-        
+
         addTarget(playerA, "Mountain");
-        
+
         setStrictChooseMode(true);
         setStopAt(3, PhaseStep.POSTCOMBAT_MAIN);
         execute();
         assertAllCommandsUsed();
-        
+
         assertPermanentCount(playerA, urborgtoy, 1);
         assertPermanentCount(playerA, "Kormus Bell", 1);
         assertPermanentCount(playerB, "Quicksilver Fountain", 1);
@@ -245,8 +254,10 @@ public class LandTypeChangingEffectsTest extends CardTestPlayerBase {
         addCard(Zone.BATTLEFIELD, playerA, "Stormtide Leviathan"); // all lands are islands in addition to their other types
         addCard(Zone.BATTLEFIELD, playerA, "Darksteel Citadel");  // land has indestructible ability
 
+        setStrictChooseMode(true);
         setStopAt(3, PhaseStep.POSTCOMBAT_MAIN);
         execute();
+        assertAllCommandsUsed();
 
         Permanent darksteel = getPermanent("Darksteel Citadel", playerA.getId());
         Assert.assertNotNull(darksteel);
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/GideonTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/GideonTest.java
index 0363ca6c1e..a8302a41b6 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/GideonTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/GideonTest.java
@@ -1,4 +1,3 @@
-
 package org.mage.test.cards.planeswalker;
 
 import mage.abilities.keyword.IndestructibleAbility;
@@ -11,7 +10,6 @@ import org.junit.Test;
 import org.mage.test.serverside.base.CardTestPlayerBase;
 
 /**
- *
  * @author LevelX2
  */
 public class GideonTest extends CardTestPlayerBase {
@@ -116,16 +114,23 @@ public class GideonTest extends CardTestPlayerBase {
         // Equip {2}
         addCard(Zone.BATTLEFIELD, playerB, "Stitcher's Graft", 1);
 
+        // transform
         attack(2, playerB, "Kytheon, Hero of Akros");
         attack(2, playerB, "Silvercoat Lion");
         attack(2, playerB, "Pillarfield Ox");
+        checkPermanentCount("after transform", 2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Gideon, Battle-Forged", 1);
 
+        // become creature and equip
         activateAbility(4, PhaseStep.PRECOMBAT_MAIN, playerB, "0: Until ");
+        waitStackResolved(4, PhaseStep.PRECOMBAT_MAIN);
         activateAbility(4, PhaseStep.PRECOMBAT_MAIN, playerB, "Equip {2}", "Gideon, Battle-Forged");
+
         attack(4, playerB, "Gideon, Battle-Forged"); // 7 damage
 
+        setStrictChooseMode(true);
         setStopAt(5, PhaseStep.PRECOMBAT_MAIN);
         execute();
+        assertAllCommandsUsed();
 
         assertPermanentCount(playerB, "Silvercoat Lion", 1);
         assertLife(playerA, 7);
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/ShowstopperTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/ShowstopperTest.java
index fbbbcf4275..23e9e34c1e 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/ShowstopperTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/ShowstopperTest.java
@@ -1,4 +1,3 @@
-
 package org.mage.test.cards.triggers.dies;
 
 import mage.constants.PhaseStep;
@@ -7,7 +6,6 @@ import org.junit.Test;
 import org.mage.test.serverside.base.CardTestPlayerBase;
 
 /**
- *
  * @author LevelX2
  */
 
@@ -16,10 +14,9 @@ public class ShowstopperTest extends CardTestPlayerBase {
     /**
      * Tests that the dies triggered ability of silvercoat lion (gained by Showstopper)
      * triggers as he dies from Lightning Bolt
-     *
      */
     @Test
-    public void testDiesTriggeredAbility() {
+    public void test_OneTrigger() {
         // Showstopper   Instant  {1}{B}{R}
         // Until end of turn, creatures you control gain "When this creature dies, it deals 2 damage to target creature an opponent controls."
         addCard(Zone.HAND, playerA, "Showstopper");
@@ -30,10 +27,14 @@ public class ShowstopperTest extends CardTestPlayerBase {
         addCard(Zone.BATTLEFIELD, playerB, "Ornithopter", 1);
 
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Showstopper");
-        castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", "Silvercoat Lion");
 
+        castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", "Silvercoat Lion");
+        addTarget(playerA, "Ornithopter");
+
+        setStrictChooseMode(true);
         setStopAt(1, PhaseStep.END_TURN);
         execute();
+        assertAllCommandsUsed();
 
         assertLife(playerA, 20);
         assertLife(playerB, 20);
@@ -43,12 +44,13 @@ public class ShowstopperTest extends CardTestPlayerBase {
 
         assertGraveyardCount(playerB, "Ornithopter", 1);
     }
+
     /**
      * Test if Showstopper is called twice
      */
 
     @Test
-    public void testTwoDiesTriggeredAbilities() {
+    public void test_TwoTriggers() {
         // Showstopper   Instant  {1}{B}{R}
         // Until end of turn, creatures you control gain "When this creature dies, it deals 2 damage to target creature an opponent controls."
         addCard(Zone.HAND, playerA, "Showstopper", 2);
@@ -62,12 +64,16 @@ public class ShowstopperTest extends CardTestPlayerBase {
 
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Showstopper");
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Showstopper");
+
         castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Lightning Bolt", "Silvercoat Lion");
+        setChoice(playerA, "When {this} dies"); // choose from two triggers
         addTarget(playerA, "Ornithopter");
         addTarget(playerA, "Grizzly Bears");
 
+        setStrictChooseMode(true);
         setStopAt(1, PhaseStep.END_TURN);
         execute();
+        assertAllCommandsUsed();
 
         assertLife(playerA, 20);
         assertLife(playerB, 20);
@@ -79,4 +85,65 @@ public class ShowstopperTest extends CardTestPlayerBase {
         assertGraveyardCount(playerB, "Ornithopter", 1);
     }
 
+    @Test
+    public void test_TwoTriggersAndCopies() {
+        // Showstopper   Instant  {1}{B}{R}
+        // Until end of turn, creatures you control gain "When this creature dies, it deals 2 damage to target creature an opponent controls."
+        addCard(Zone.HAND, playerA, "Showstopper", 2);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
+        addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
+        addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1);
+        addCard(Zone.HAND, playerB, "Lightning Bolt");
+        addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1);
+        addCard(Zone.BATTLEFIELD, playerB, "Ornithopter", 1);
+        addCard(Zone.BATTLEFIELD, playerB, "Grizzly Bears", 1);
+        addCard(Zone.BATTLEFIELD, playerB, "Alchemist's Apprentice", 1);
+        addCard(Zone.BATTLEFIELD, playerB, "Augmenting Automaton", 1);
+        //
+        // When you next cast an instant spell, cast a sorcery spell, or activate a loyalty ability this turn, copy that spell or ability twice.
+        // You may choose new targets for the copies.
+        addCard(Zone.HAND, playerA, "Repeated Reverberation", 1); // {2}{R}{R}
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4);
+
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 4);
+
+        // first spell
+        activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {B}", 2);
+        activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Showstopper");
+
+        // prepare copy
+        activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}", 4);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Repeated Reverberation");
+
+        // second spell with 2x copy
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Showstopper");
+
+        castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Lightning Bolt", "Silvercoat Lion");
+        setChoice(playerA, "When {this} dies"); // choose from 4 triggers
+        setChoice(playerA, "When {this} dies"); // choose from 4 triggers
+        setChoice(playerA, "When {this} dies"); // choose from 4 triggers
+        addTarget(playerA, "Ornithopter");
+        addTarget(playerA, "Grizzly Bears");
+        addTarget(playerA, "Alchemist's Apprentice");
+        addTarget(playerA, "Augmenting Automaton");
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertLife(playerA, 20);
+        assertLife(playerB, 20);
+
+        assertGraveyardCount(playerA, "Showstopper", 2);
+        assertGraveyardCount(playerA, "Silvercoat Lion", 1);
+
+        assertGraveyardCount(playerB, "Grizzly Bears", 1);
+        assertGraveyardCount(playerB, "Ornithopter", 1);
+        assertGraveyardCount(playerB, "Alchemist's Apprentice", 1);
+        assertGraveyardCount(playerB, "Augmenting Automaton", 1);
+    }
+
 }
\ No newline at end of file
diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java b/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java
index d0ec027be5..f32f87e537 100644
--- a/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java
+++ b/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java
@@ -57,7 +57,7 @@ public class ContinuousEffects implements Serializable {
     //    private final PlaneswalkerRedirectionEffect planeswalkerRedirectionEffect;
     private final AuraReplacementEffect auraReplacementEffect;
 
-    private final List<ContinuousEffect> previous = new ArrayList<>();
+    private final Map<String, List<ContinuousEffect>> lastEffectsListOnLayer = new HashMap<>(); // helps to find out new effect timestamps
 
     // note all effect/abilities that were only added temporary
     private final Map<ContinuousEffect, Set<Ability>> temporaryEffects = new HashMap<>();
@@ -178,6 +178,16 @@ public class ContinuousEffects implements Serializable {
     }
 
     public synchronized List<ContinuousEffect> getLayeredEffects(Game game) {
+        return getLayeredEffects(game, "main");
+    }
+
+    /**
+     * Return effects list ordered by timestamps (timestamps are automaticity generates from new/old lists on same layer)
+     *
+     * @param timestampGroupName workaround to fix broken timestamps on effect's add/remove between different layers
+     * @return effects list ordered by timestamp
+     */
+    public synchronized List<ContinuousEffect> getLayeredEffects(Game game, String timestampGroupName) {
         List<ContinuousEffect> layerEffects = new ArrayList<>();
         for (ContinuousEffect effect : layeredEffects) {
             switch (effect.getDuration()) {
@@ -202,9 +212,14 @@ public class ContinuousEffects implements Serializable {
             }
         }
 
-        updateTimestamps(layerEffects);
+        updateTimestamps(timestampGroupName, layerEffects);
+        layerEffects.sort(Comparator.comparingLong(ContinuousEffect::getOrder));
+        /* debug effects apply order:
+        if (game.getStep() != null) System.out.println("layr - " + game.getTurnNum() + "." + game.getStep().getType() + ": layers " + layerEffects.size()
+                + " - " + layerEffects.stream().map(l -> l.getClass().getSimpleName()).collect(Collectors.joining(", "))
+                + " - " + callName);
+        //*/
 
-        Collections.sort(layerEffects, Comparator.comparingLong(ContinuousEffect::getOrder));
         return layerEffects;
     }
 
@@ -215,17 +230,23 @@ public class ContinuousEffects implements Serializable {
      * Ability.#isInUseableZone(Game, boolean) method in
      * #getLayeredEffects(Game).
      *
+     * It must be called with different timestamp group name (otherwise sort order will be changed for add/remove effects, see Urborg and Bloodmoon test)
+     *
      * @param layerEffects
      */
-    private synchronized void updateTimestamps(List<ContinuousEffect> layerEffects) {
+    private synchronized void updateTimestamps(String timestampGroupName, List<ContinuousEffect> layerEffects) {
+        if (!lastEffectsListOnLayer.containsKey(timestampGroupName)) {
+            lastEffectsListOnLayer.put(timestampGroupName, new ArrayList<>());
+        }
+        List<ContinuousEffect> prevs = lastEffectsListOnLayer.get(timestampGroupName);
         for (ContinuousEffect continuousEffect : layerEffects) {
             // check if it's new, then set order
-            if (!previous.contains(continuousEffect)) {
+            if (!prevs.contains(continuousEffect)) {
                 setOrder(continuousEffect);
             }
         }
-        previous.clear();
-        previous.addAll(layerEffects);
+        prevs.clear();
+        prevs.addAll(layerEffects);
     }
 
     public void setOrder(ContinuousEffect effect) {
@@ -425,12 +446,12 @@ public class ContinuousEffects implements Serializable {
             return false;
         }
         boolean exists = true;
-        if (!object.getAbilities().contains(ability)) {
+        if (!object.hasAbility(ability, game)) {
             exists = false;
             if (object instanceof PermanentCard) {
                 PermanentCard permanent = (PermanentCard) object;
                 if (permanent.isTransformable() && event.getType() == GameEvent.EventType.TRANSFORMED) {
-                    exists = permanent.getCard().getAbilities().contains(ability);
+                    exists = permanent.getCard().hasAbility(ability, game);
                 }
             }
         } else if (object instanceof PermanentCard) {
@@ -903,7 +924,7 @@ public class ContinuousEffects implements Serializable {
     //20091005 - 613
     public synchronized void apply(Game game) {
         removeInactiveEffects(game);
-        List<ContinuousEffect> activeLayerEffects = getLayeredEffects(game);
+        List<ContinuousEffect> activeLayerEffects = getLayeredEffects(game); // main call
 
         List<ContinuousEffect> layer = filterLayeredEffects(activeLayerEffects, Layer.CopyEffects_1);
         for (ContinuousEffect effect : layer) {
@@ -914,7 +935,7 @@ public class ContinuousEffects implements Serializable {
         }
         //Reload layerEffect if copy effects were applied
         if (!layer.isEmpty()) {
-            activeLayerEffects = getLayeredEffects(game);
+            activeLayerEffects = getLayeredEffects(game, "layer_1");
         }
 
         layer = filterLayeredEffects(activeLayerEffects, Layer.ControlChangingEffects_2);
@@ -936,16 +957,16 @@ public class ContinuousEffects implements Serializable {
             game.getBattlefield().resetPermanentsControl();
         }
 
-        applyLayer(activeLayerEffects, Layer.TextChangingEffects_3, game);
-        applyLayer(activeLayerEffects, Layer.TypeChangingEffects_4, game);
-        applyLayer(activeLayerEffects, Layer.ColorChangingEffects_5, game);
+        applyLayer(activeLayerEffects, Layer.TextChangingEffects_3, game, "layer_3");
+        applyLayer(activeLayerEffects, Layer.TypeChangingEffects_4, game, "layer_4");
+        applyLayer(activeLayerEffects, Layer.ColorChangingEffects_5, game, "layer_5");
 
         Map<ContinuousEffect, List<Ability>> appliedEffectAbilities = new HashMap<>();
         boolean done = false;
         Map<ContinuousEffect, Set<UUID>> waitingEffects = new LinkedHashMap<>();
         Set<UUID> appliedEffects = new HashSet<>();
         applyCounters.apply(Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, null, game);
-        activeLayerEffects = getLayeredEffects(game);
+        activeLayerEffects = getLayeredEffects(game, "layer_6");
 
         while (!done) { // loop needed if a added effect adds again an effect (e.g. Level 5- of Joraga Treespeaker)
             done = true;
@@ -1000,7 +1021,7 @@ public class ContinuousEffects implements Serializable {
                     effect.apply(Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, ability, game);
                     done = false;
                     // list must be updated after each applied effect (eg. if "Turn to Frog" removes abilities)
-                    activeLayerEffects = getLayeredEffects(game);
+                    activeLayerEffects = getLayeredEffects(game, "apply");
                 }
                 appliedEffects.add(effect.getId());
 
@@ -1027,7 +1048,7 @@ public class ContinuousEffects implements Serializable {
                         entry.getKey().apply(Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, ability, game);
                         done = false;
                         // list must be updated after each applied effect (eg. if "Turn to Frog" removes abilities)
-                        activeLayerEffects = getLayeredEffects(game);
+                        activeLayerEffects = getLayeredEffects(game, "apply");
                     }
                     appliedEffects.add(entry.getKey().getId());
                     iterator.remove();
@@ -1083,10 +1104,10 @@ public class ContinuousEffects implements Serializable {
 
     private boolean abilityActive(Ability ability, Game game) {
         MageObject object = game.getObject(ability.getSourceId());
-        return object != null && object.hasAbility(ability.getId(), game);
+        return object != null && object.hasAbility(ability, game);
     }
 
-    private void applyLayer(List<ContinuousEffect> activeLayerEffects, Layer currentLayer, Game game) {
+    private void applyLayer(List<ContinuousEffect> activeLayerEffects, Layer currentLayer, Game game, String timestampGroupName) {
         List<ContinuousEffect> layer = filterLayeredEffects(activeLayerEffects, currentLayer);
         // layer is a list of all effects at the current layer
         if (!layer.isEmpty()) {
@@ -1109,7 +1130,7 @@ public class ContinuousEffects implements Serializable {
                 applyContinuousEffect(effect, currentLayer, game);
                 // add it to the applied effects list
                 appliedEffects.add(effect.getId());
-                layer = getLayeredEffects(game);
+                layer = getLayeredEffects(game, timestampGroupName);
 
                 // check waiting effects to see if it has anything to check
                 if (!waitingEffects.isEmpty()) {
@@ -1120,7 +1141,7 @@ public class ContinuousEffects implements Serializable {
                             applyContinuousEffect(entry.getKey(), currentLayer, game);
                             // add it to the applied effects list
                             appliedEffects.add(entry.getKey().getId());
-                            layer = getLayeredEffects(game);
+                            layer = getLayeredEffects(game, timestampGroupName);
                         }
                     }
                 }
@@ -1131,7 +1152,7 @@ public class ContinuousEffects implements Serializable {
                             applyContinuousEffect(entry.getKey(), currentLayer, game);
                             // add it to the applied effects list
                             appliedEffects.add(entry.getKey().getId());
-                            layer = getLayeredEffects(game);
+                            layer = getLayeredEffects(game, timestampGroupName);
                         }
                     }
                 }
@@ -1154,7 +1175,7 @@ public class ContinuousEffects implements Serializable {
         if (!(effect instanceof BecomesFaceDownCreatureEffect)
                 && (effect != null && !effect.getDuration().equals(Duration.Custom))) { // Custom effects do not depend on the creating permanent
             if (card != null) {
-                return card.getAbilities(game).contains(ability);
+                return card.hasAbility(ability, game);
             }
         }
 

From c656bea31caadd16498062530ee2976c0388be86 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Thu, 28 May 2020 23:24:30 +0400
Subject: [PATCH 072/586] Ability refactor: removed deprecated code

---
 Mage.Sets/src/mage/cards/e/Excavator.java                 | 2 +-
 Mage.Sets/src/mage/cards/m/Martyrdom.java                 | 2 +-
 Mage.Sets/src/mage/cards/s/ShieldsOfVelisVel.java         | 2 +-
 Mage.Sets/src/mage/cards/t/TestamentOfFaith.java          | 2 +-
 .../java/mage/abilities/effects/common/CopyEffect.java    | 4 ++--
 .../effects/common/continuous/GainAbilityAllEffect.java   | 6 +++---
 .../common/continuous/GainAbilityAttachedEffect.java      | 2 +-
 .../common/continuous/GainAbilityControlledEffect.java    | 6 +++---
 .../common/continuous/GainAbilityPairedEffect.java        | 2 +-
 .../common/continuous/GainAbilitySourceEffect.java        | 2 +-
 .../common/continuous/GainAbilityTargetEffect.java        | 2 +-
 .../java/mage/abilities/keyword/TransformAbility.java     | 2 +-
 Mage/src/main/java/mage/game/permanent/Permanent.java     | 3 ---
 Mage/src/main/java/mage/game/permanent/PermanentImpl.java | 8 --------
 14 files changed, 17 insertions(+), 28 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/e/Excavator.java b/Mage.Sets/src/mage/cards/e/Excavator.java
index 827f0d2c21..9516f6212c 100644
--- a/Mage.Sets/src/mage/cards/e/Excavator.java
+++ b/Mage.Sets/src/mage/cards/e/Excavator.java
@@ -112,7 +112,7 @@ class ExcavatorEffect extends ContinuousEffectImpl implements SourceEffect {
             if (permanent != null) {
                 for(Ability ability : abilities)
                 {
-                    permanent.addAbility(ability, source.getSourceId(), game, false);
+                    permanent.addAbility(ability, source.getSourceId(), game);
                 }
             }
         }
diff --git a/Mage.Sets/src/mage/cards/m/Martyrdom.java b/Mage.Sets/src/mage/cards/m/Martyrdom.java
index 067ad07278..a693c43277 100644
--- a/Mage.Sets/src/mage/cards/m/Martyrdom.java
+++ b/Mage.Sets/src/mage/cards/m/Martyrdom.java
@@ -66,7 +66,7 @@ class MartyrdomGainAbilityTargetEffect extends ContinuousEffectImpl {
         if (permanent != null) {
             ActivatedAbilityImpl ability = new MartyrdomActivatedAbility(source.getControllerId());
             ability.setMayActivate(TargetController.ANY);
-            permanent.addAbility(ability, source.getSourceId(), game, false);
+            permanent.addAbility(ability, source.getSourceId(), game);
             return true;
         }
         return false;
diff --git a/Mage.Sets/src/mage/cards/s/ShieldsOfVelisVel.java b/Mage.Sets/src/mage/cards/s/ShieldsOfVelisVel.java
index 5afe8b026c..edc7d925e2 100644
--- a/Mage.Sets/src/mage/cards/s/ShieldsOfVelisVel.java
+++ b/Mage.Sets/src/mage/cards/s/ShieldsOfVelisVel.java
@@ -78,7 +78,7 @@ class ShieldsOfVelisVelGainEffect extends ContinuousEffectImpl {
         for (Iterator<MageObjectReference> it = affectedObjectList.iterator(); it.hasNext();) {
             Permanent permanent = it.next().getPermanent(game);
             if (permanent != null) {
-                permanent.addAbility(ChangelingAbility.getInstance(), source.getSourceId(), game, false);
+                permanent.addAbility(ChangelingAbility.getInstance(), source.getSourceId(), game);
             } else {
                 it.remove();
             }
diff --git a/Mage.Sets/src/mage/cards/t/TestamentOfFaith.java b/Mage.Sets/src/mage/cards/t/TestamentOfFaith.java
index 0e99542953..a0980c9bec 100644
--- a/Mage.Sets/src/mage/cards/t/TestamentOfFaith.java
+++ b/Mage.Sets/src/mage/cards/t/TestamentOfFaith.java
@@ -111,7 +111,7 @@ class TestamentOfFaithBecomesCreatureSourceEffect extends ContinuousEffectImpl i
                     if (sublayer == SubLayer.NA) {
                         if (!token.getAbilities().isEmpty()) {
                             for (Ability ability: token.getAbilities()) {
-                                permanent.addAbility(ability, source.getSourceId(), game, false);
+                                permanent.addAbility(ability, source.getSourceId(), game);
                             }
                         }
                     }
diff --git a/Mage/src/main/java/mage/abilities/effects/common/CopyEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CopyEffect.java
index d75fda572e..ae3f6c5e86 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/CopyEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/CopyEffect.java
@@ -115,11 +115,11 @@ public class CopyEffect extends ContinuousEffectImpl {
         permanent.removeAllAbilities(source.getSourceId(), game);
         if (copyFromObject instanceof Permanent) {
             for (Ability ability : ((Permanent) copyFromObject).getAbilities(game)) {
-                permanent.addAbility(ability, getSourceId(), game, false); // no new Id so consumed replacement effects are known while new continuousEffects.apply happen.
+                permanent.addAbility(ability, getSourceId(), game);
             }
         } else {
             for (Ability ability : copyFromObject.getAbilities()) {
-                permanent.addAbility(ability, getSourceId(), game, false); // no new Id so consumed replacement effects are known while new continuousEffects.apply happen.
+                permanent.addAbility(ability, getSourceId(), game);
             }
         }
 
diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAllEffect.java
index fca600ed88..36d96f04b6 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAllEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAllEffect.java
@@ -87,7 +87,7 @@ public class GainAbilityAllEffect extends ContinuousEffectImpl {
             for (Iterator<MageObjectReference> it = affectedObjectList.iterator(); it.hasNext(); ) { // filter may not be used again, because object can have changed filter relevant attributes but still geets boost
                 Permanent permanent = it.next().getPermanentOrLKIBattlefield(game); //LKI is neccessary for "dies triggered abilities" to work given to permanets  (e.g. Showstopper)
                 if (permanent != null) {
-                    permanent.addAbility(ability, source.getSourceId(), game, false);
+                    permanent.addAbility(ability, source.getSourceId(), game);
                 } else {
                     it.remove(); // no longer on the battlefield, remove reference to object
                     if (affectedObjectList.isEmpty()) {
@@ -99,7 +99,7 @@ public class GainAbilityAllEffect extends ContinuousEffectImpl {
             setRuntimeData(source, game);
             for (Permanent perm : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) {
                 if (!(excludeSource && perm.getId().equals(source.getSourceId())) && selectedByRuntimeData(perm, source, game)) {
-                    perm.addAbility(ability, source.getSourceId(), game, false);
+                    perm.addAbility(ability, source.getSourceId(), game);
                 }
             }
             // still as long as the prev. permanent is known to the LKI (e.g. Mikaeus, the Unhallowed) so gained dies triggered ability will trigger
@@ -109,7 +109,7 @@ public class GainAbilityAllEffect extends ContinuousEffectImpl {
                     Permanent perm = (Permanent) mageObject;
                     if (!(excludeSource && perm.getId().equals(source.getSourceId())) && selectedByRuntimeData(perm, source, game)) {
                         if (filter.match(perm, source.getSourceId(), source.getControllerId(), game)) {
-                            perm.addAbility(ability, source.getSourceId(), game, false);
+                            perm.addAbility(ability, source.getSourceId(), game);
                         }
                     }
                 }
diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAttachedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAttachedEffect.java
index b9fc862152..959f3cddd5 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAttachedEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAttachedEffect.java
@@ -88,7 +88,7 @@ public class GainAbilityAttachedEffect extends ContinuousEffectImpl {
             }
         }
         if (permanent != null) {
-            permanent.addAbility(ability, source.getSourceId(), game, false);
+            permanent.addAbility(ability, source.getSourceId(), game);
         }
         return true;
     }
diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityControlledEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityControlledEffect.java
index 2d96eaf08e..1dcc227e8d 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityControlledEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityControlledEffect.java
@@ -87,7 +87,7 @@ public class GainAbilityControlledEffect extends ContinuousEffectImpl {
                 Permanent perm = it.next().getPermanentOrLKIBattlefield(game); //LKI is neccessary for "dies triggered abilities" to work given to permanets  (e.g. Showstopper)
                 if (perm != null) {
                     for (Ability abilityToAdd : ability) {
-                        perm.addAbility(abilityToAdd, source.getSourceId(), game, false);
+                        perm.addAbility(abilityToAdd, source.getSourceId(), game);
                     }
                 } else {
                     it.remove();
@@ -100,7 +100,7 @@ public class GainAbilityControlledEffect extends ContinuousEffectImpl {
             for (Permanent perm : game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(), game)) {
                 if (!(excludeSource && perm.getId().equals(source.getSourceId()))) {
                     for (Ability abilityToAdd : ability) {
-                        perm.addAbility(abilityToAdd, source.getSourceId(), game, false);
+                        perm.addAbility(abilityToAdd, source.getSourceId(), game);
                     }
                 }
             }
@@ -112,7 +112,7 @@ public class GainAbilityControlledEffect extends ContinuousEffectImpl {
                     if (!(excludeSource && perm.getId().equals(source.getSourceId()))) {
                         if (filter.match(perm, source.getSourceId(), source.getControllerId(), game)) {
                             for (Ability abilityToAdd : ability) {
-                                perm.addAbility(abilityToAdd, source.getSourceId(), game, false);
+                                perm.addAbility(abilityToAdd, source.getSourceId(), game);
                             }
                         }
                     }
diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityPairedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityPairedEffect.java
index 6c8c4b0daa..a1c5554330 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityPairedEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityPairedEffect.java
@@ -43,7 +43,7 @@ public class GainAbilityPairedEffect extends ContinuousEffectImpl {
             Permanent paired = permanent.getPairedCard().getPermanent(game);
             if (paired != null && paired.getPairedCard() != null && paired.getPairedCard().equals(new MageObjectReference(permanent, game))) {
                 permanent.addAbility(ability, source.getSourceId(), game);
-                paired.addAbility(ability, source.getSourceId(), game, false);
+                paired.addAbility(ability, source.getSourceId(), game);
                 return true;
 
             } else {
diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilitySourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilitySourceEffect.java
index ddf5809187..c3a3507f5e 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilitySourceEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilitySourceEffect.java
@@ -103,7 +103,7 @@ public class GainAbilitySourceEffect extends ContinuousEffectImpl implements Sou
                 permanent = game.getPermanent(source.getSourceId());
             }
             if (permanent != null) {
-                permanent.addAbility(ability, source.getSourceId(), game, false);
+                permanent.addAbility(ability, source.getSourceId(), game);
                 return true;
             }
         }
diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityTargetEffect.java
index 5647423c5d..adff4f92a4 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityTargetEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityTargetEffect.java
@@ -112,7 +112,7 @@ public class GainAbilityTargetEffect extends ContinuousEffectImpl {
             for (UUID permanentId : targetPointer.getTargets(game, source)) {
                 Permanent permanent = game.getPermanentOrLKIBattlefield(permanentId);
                 if (permanent != null) {
-                    permanent.addAbility(ability, source.getSourceId(), game, false);
+                    permanent.addAbility(ability, source.getSourceId(), game);
                     affectedTargets++;
                 }
             }
diff --git a/Mage/src/main/java/mage/abilities/keyword/TransformAbility.java b/Mage/src/main/java/mage/abilities/keyword/TransformAbility.java
index 199560a103..8c1022e122 100644
--- a/Mage/src/main/java/mage/abilities/keyword/TransformAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/TransformAbility.java
@@ -63,7 +63,7 @@ public class TransformAbility extends SimpleStaticAbility {
         permanent.setExpansionSetCode(sourceCard.getExpansionSetCode());
         permanent.getAbilities().clear();
         for (Ability ability : sourceCard.getAbilities()) {
-            permanent.addAbility(ability, source == null ? null : source.getSourceId(), game, false);
+            permanent.addAbility(ability, source == null ? null : source.getSourceId(), game);
         }
         permanent.getPower().modifyBaseValue(sourceCard.getPower().getValue());
         permanent.getToughness().modifyBaseValue(sourceCard.getToughness().getValue());
diff --git a/Mage/src/main/java/mage/game/permanent/Permanent.java b/Mage/src/main/java/mage/game/permanent/Permanent.java
index a8f040086e..7999673d27 100644
--- a/Mage/src/main/java/mage/game/permanent/Permanent.java
+++ b/Mage/src/main/java/mage/game/permanent/Permanent.java
@@ -156,9 +156,6 @@ public interface Permanent extends Card, Controllable {
 
     void addAbility(Ability ability, UUID sourceId, Game game);
 
-    @Deprecated // use addAbility(Ability ability, UUID sourceId, Game game) instead
-    void addAbility(Ability ability, UUID sourceId, Game game, boolean createNewId);
-
     void removeAllAbilities(UUID sourceId, Game game);
 
     void removeAbility(Ability abilityToRemove, UUID sourceId, Game game);
diff --git a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java
index 81208ef8e6..e093335625 100644
--- a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java
+++ b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java
@@ -361,14 +361,8 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
 
     @Override
     public void addAbility(Ability ability, UUID sourceId, Game game) {
-        addAbility(ability, sourceId, game, false);
-    }
-
-    @Override
-    public void addAbility(Ability ability, UUID sourceId, Game game, boolean createNewId) {
         // singleton abilities -- only one instance
         // other abilities -- any amount of instances
-        // TODO: no needs in createNewId, so move code to addAbility(Ability ability, UUID sourceId, Game game)
         if (!abilities.containsKey(ability.getId())) {
             Ability copyAbility = ability.copy();
             copyAbility.newId(); // needed so that source can get an ability multiple times (e.g. Raging Ravine)
@@ -383,7 +377,6 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
 
     @Override
     public void removeAllAbilities(UUID sourceId, Game game) {
-        // can't use getAbilities() here -- cause it's can be auto-generated list potentially
         // TODO: what about triggered abilities? See addAbility above -- triggers adds to GameState
         abilities.clear();
     }
@@ -402,7 +395,6 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
             }
         });
 
-        // can't use getAbilities() here -- cause it's can be auto-generated list potentially
         // TODO: what about triggered abilities? See addAbility above -- triggers adds to GameState
         toRemove.forEach(r -> abilities.remove(r));
     }

From 40c01a04c498ae7ffd205ff7dada4ffa5c8bb8cb Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Fri, 29 May 2020 09:18:49 +0400
Subject: [PATCH 073/586] Typo

---
 Mage.Sets/src/mage/cards/b/BreyaEtheriumShaper.java | 2 +-
 Mage.Sets/src/mage/cards/t/TogetherForever.java     | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/b/BreyaEtheriumShaper.java b/Mage.Sets/src/mage/cards/b/BreyaEtheriumShaper.java
index 23796bd56c..b5339588b4 100644
--- a/Mage.Sets/src/mage/cards/b/BreyaEtheriumShaper.java
+++ b/Mage.Sets/src/mage/cards/b/BreyaEtheriumShaper.java
@@ -40,7 +40,7 @@ public final class BreyaEtheriumShaper extends CardImpl {
         // When Breya, Etherium Shaper enters the battlefield, create two 1/1 blue Thopter artifact creature tokens with flying.
         this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new ThopterToken(), 2)));
 
-        // {2}, Sacrifice two artifacts: Choose one &mdash; Breya deals 3 damage to target player.
+        // {2}, Sacrifice two artifacts: Choose one &mdash; Breya deals 3 damage to target player or planeswalker.
         Ability ability = new SimpleActivatedAbility(
                 Zone.BATTLEFIELD,
                 new DamageTargetEffect(3),
diff --git a/Mage.Sets/src/mage/cards/t/TogetherForever.java b/Mage.Sets/src/mage/cards/t/TogetherForever.java
index 7ce28b8a1e..7bb233674e 100644
--- a/Mage.Sets/src/mage/cards/t/TogetherForever.java
+++ b/Mage.Sets/src/mage/cards/t/TogetherForever.java
@@ -40,7 +40,7 @@ public final class TogetherForever extends CardImpl {
     public TogetherForever(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}{W}");
 
-        // When Together Forever enters the battlefield, support 2. (Put a +1/+1 counter on each of up to two other target creatures.)
+        // When Together Forever enters the battlefield, support 2. (Put a +1/+1 counter on each of up to two target creatures.)
         this.addAbility(new SupportAbility(this, 2, false));
 
         // {1}: Choose target creature with a counter on it. When that creature dies this turn, return that card to its owner's hand.

From 32ce1d85e9d845e55cf09fe78df788f066e983d6 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Fri, 29 May 2020 14:41:24 +0200
Subject: [PATCH 074/586] * Fix of some problems of zone change related
 triggered abilities that had not been correctly implemented (fixes #6586).

---
 .../mage/cards/a/AnaxHardenedInTheForge.java  | 56 +++++++-------
 Mage.Sets/src/mage/cards/p/PawnOfUlamog.java  | 75 +++++--------------
 .../other/SakashimaTheImpostorTest.java       | 45 +++++++++++
 .../mage/abilities/TriggeredAbilityImpl.java  | 17 +++--
 .../common/ZoneChangeAllTriggeredAbility.java |  3 +
 5 files changed, 102 insertions(+), 94 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/a/AnaxHardenedInTheForge.java b/Mage.Sets/src/mage/cards/a/AnaxHardenedInTheForge.java
index 2412c0a40f..b55489b2c7 100644
--- a/Mage.Sets/src/mage/cards/a/AnaxHardenedInTheForge.java
+++ b/Mage.Sets/src/mage/cards/a/AnaxHardenedInTheForge.java
@@ -1,7 +1,6 @@
 package mage.cards.a;
 
 import mage.MageInt;
-import mage.abilities.TriggeredAbilityImpl;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.dynamicvalue.common.DevotionCount;
 import mage.abilities.effects.common.CreateTokenEffect;
@@ -12,17 +11,27 @@ import mage.constants.*;
 import mage.game.Game;
 import mage.game.events.GameEvent;
 import mage.game.events.ZoneChangeEvent;
-import mage.game.permanent.PermanentToken;
 import mage.game.permanent.token.SatyrCantBlockToken;
 
-import java.util.Objects;
 import java.util.UUID;
+import mage.abilities.common.DiesThisOrAnotherCreatureTriggeredAbility;
+import mage.abilities.effects.Effect;
+import mage.filter.common.FilterCreaturePermanent;
+import mage.filter.predicate.Predicates;
+import mage.filter.predicate.permanent.TokenPredicate;
 
 /**
  * @author TheElk801
  */
 public final class AnaxHardenedInTheForge extends CardImpl {
 
+    private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nontoken creature you control");
+
+    static {
+        filter.add(TargetController.YOU.getControllerPredicate());
+        filter.add(Predicates.not(TokenPredicate.instance));
+    }
+
     public AnaxHardenedInTheForge(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{1}{R}{R}");
 
@@ -38,8 +47,9 @@ public final class AnaxHardenedInTheForge extends CardImpl {
                         .setText("{this}'s power is equal to your devotion to red")
         ).addHint(DevotionCount.R.getHint()));
 
-        // Whenever Anax or another nontoken creature you control dies, create a 1/1 red Satyr creature token with "This creature can't block." If the creature had power 4 or greater, create two of those tokens instead.
-        this.addAbility(new AnaxHardenedInTheForgeTriggeredAbility());
+        // Whenever Anax or another nontoken creature you control dies, create a 1/1 red Satyr creature token 
+        // with "This creature can't block." If the creature had power 4 or greater, create two of those tokens instead.
+        this.addAbility(new AnaxHardenedInTheForgeTriggeredAbility(null, false, filter));
     }
 
     private AnaxHardenedInTheForge(final AnaxHardenedInTheForge card) {
@@ -52,10 +62,10 @@ public final class AnaxHardenedInTheForge extends CardImpl {
     }
 }
 
-class AnaxHardenedInTheForgeTriggeredAbility extends TriggeredAbilityImpl {
+class AnaxHardenedInTheForgeTriggeredAbility extends DiesThisOrAnotherCreatureTriggeredAbility {
 
-    AnaxHardenedInTheForgeTriggeredAbility() {
-        super(Zone.BATTLEFIELD, null, false);
+    AnaxHardenedInTheForgeTriggeredAbility(Effect effect, boolean optional, FilterCreaturePermanent filter) {
+        super(effect, optional, filter);
     }
 
     private AnaxHardenedInTheForgeTriggeredAbility(final AnaxHardenedInTheForgeTriggeredAbility ability) {
@@ -67,33 +77,21 @@ class AnaxHardenedInTheForgeTriggeredAbility extends TriggeredAbilityImpl {
         return new AnaxHardenedInTheForgeTriggeredAbility(this);
     }
 
-    @Override
-    public boolean checkEventType(GameEvent event, Game game) {
-        return event.getType() == GameEvent.EventType.ZONE_CHANGE;
-    }
-
     @Override
     public boolean checkTrigger(GameEvent event, Game game) {
-        ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
-        if (!zEvent.isDiesEvent()) {
-            return false;
+        if (super.checkTrigger(event, game)) {
+            int tokenCount = ((ZoneChangeEvent) event).getTarget().getPower().getValue() > 3 ? 2 : 1;
+            this.getEffects().clear();
+            this.addEffect(new CreateTokenEffect(new SatyrCantBlockToken(), tokenCount));
+            return true;
         }
-        if (!zEvent.getTarget().getId().equals(getSourceId())
-                && (zEvent.getTarget() instanceof PermanentToken
-                || !zEvent.getTarget().isCreature()
-                || !Objects.equals(zEvent.getTarget().getControllerId(), getControllerId()))) {
-            return false;
-        }
-        int tokenCount = zEvent.getTarget().getPower().getValue() > 3 ? 2 : 1;
-        this.getEffects().clear();
-        this.addEffect(new CreateTokenEffect(new SatyrCantBlockToken(), tokenCount));
-        return true;
+        return false;
     }
 
     @Override
     public String getRule() {
-        return "Whenever {this} or another nontoken creature you control dies, " +
-                "create a 1/1 red Satyr creature token with \"This creature can't block.\" " +
-                "If the creature had power 4 or greater, create two of those tokens instead.";
+        return "Whenever {this} or another nontoken creature you control dies, "
+                + "create a 1/1 red Satyr creature token with \"This creature can't block.\" "
+                + "If the creature had power 4 or greater, create two of those tokens instead.";
     }
 }
diff --git a/Mage.Sets/src/mage/cards/p/PawnOfUlamog.java b/Mage.Sets/src/mage/cards/p/PawnOfUlamog.java
index d9aa4601a3..222b1b8f45 100644
--- a/Mage.Sets/src/mage/cards/p/PawnOfUlamog.java
+++ b/Mage.Sets/src/mage/cards/p/PawnOfUlamog.java
@@ -1,22 +1,17 @@
-
 package mage.cards.p;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.MageObject;
-import mage.abilities.TriggeredAbilityImpl;
+import mage.abilities.common.DiesThisOrAnotherCreatureTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.SubType;
-import mage.constants.Zone;
-import mage.game.Game;
-import mage.game.events.GameEvent;
-import mage.game.events.GameEvent.EventType;
-import mage.game.events.ZoneChangeEvent;
-import mage.game.permanent.Permanent;
-import mage.game.permanent.PermanentToken;
+import mage.constants.TargetController;
+import mage.filter.common.FilterCreaturePermanent;
+import mage.filter.predicate.Predicates;
+import mage.filter.predicate.permanent.TokenPredicate;
 import mage.game.permanent.token.EldraziSpawnToken;
 
 /**
@@ -24,16 +19,25 @@ import mage.game.permanent.token.EldraziSpawnToken;
  * @author North
  */
 public final class PawnOfUlamog extends CardImpl {
+    
+    private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nontoken creature you control");
 
+    static {
+        filter.add(TargetController.YOU.getControllerPredicate());
+        filter.add(Predicates.not(TokenPredicate.instance));
+    }
+    
     public PawnOfUlamog(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}{B}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{B}");
         this.subtype.add(SubType.VAMPIRE);
         this.subtype.add(SubType.SHAMAN);
 
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
 
-        this.addAbility(new PawnOfUlamogTriggeredAbility());
+        // Whenever Pawn of Ulamog or another nontoken creature you control dies, you may create a 0/1 colorless 
+        // Eldrazi Spawn creature token. It has "Sacrifice this creature: Add {C}."
+        this.addAbility(new DiesThisOrAnotherCreatureTriggeredAbility(new CreateTokenEffect(new EldraziSpawnToken()), true, filter));
     }
 
     public PawnOfUlamog(final PawnOfUlamog card) {
@@ -44,49 +48,4 @@ public final class PawnOfUlamog extends CardImpl {
     public PawnOfUlamog copy() {
         return new PawnOfUlamog(this);
     }
-}
-
-class PawnOfUlamogTriggeredAbility extends TriggeredAbilityImpl {
-
-    public PawnOfUlamogTriggeredAbility() {
-        super(Zone.BATTLEFIELD, new CreateTokenEffect(new EldraziSpawnToken()), true);
-    }
-
-    public PawnOfUlamogTriggeredAbility(PawnOfUlamogTriggeredAbility ability) {
-        super(ability);
-    }
-
-    @Override
-    public PawnOfUlamogTriggeredAbility copy() {
-        return new PawnOfUlamogTriggeredAbility(this);
-    }
-
-    @Override
-    public boolean checkEventType(GameEvent event, Game game) {
-        return event.getType() == EventType.ZONE_CHANGE;
-    }
-
-    @Override
-    public boolean checkTrigger(GameEvent event, Game game) {
-            UUID targetId = event.getTargetId();
-            MageObject card = game.getLastKnownInformation(targetId, Zone.BATTLEFIELD);
-            if (card instanceof Permanent && !(card instanceof PermanentToken)) {
-                Permanent permanent = (Permanent) card;
-                ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
-                if (zEvent.isDiesEvent()
-                        && permanent.isControlledBy(this.controllerId)
-                        && (targetId.equals(this.getSourceId())
-                            || (permanent.isCreature()
-                                && !targetId.equals(this.getSourceId())
-                                && !(permanent instanceof PermanentToken)))) {
-                    return true;
-                }
-            }
-        return false;
-    }
-
-    @Override
-    public String getRule() {
-        return "Whenever Pawn of Ulamog or another nontoken creature you control dies, you may create a 0/1 colorless Eldrazi Spawn creature token. It has \"Sacrifice this creature: Add {C}.\"";
-    }
-}
+}
\ No newline at end of file
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/SakashimaTheImpostorTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/SakashimaTheImpostorTest.java
index 267229cccd..b6cbba80e8 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/SakashimaTheImpostorTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/SakashimaTheImpostorTest.java
@@ -9,10 +9,17 @@ import org.mage.test.serverside.base.CardTestPlayerBase;
  * Created by alexsandro on 06/03/17.
  */
 public class SakashimaTheImpostorTest extends CardTestPlayerBase {
+
     @Test
     public void copySpellStutterTest() {
+        // Flash, Flying
+        // When Spellstutter Sprite enters the battlefield, counter target spell with converted mana cost X or less, 
+        // where X is the number of Faeries you control.        
         addCard(Zone.BATTLEFIELD, playerA, "Spellstutter Sprite", 1);
         addCard(Zone.BATTLEFIELD, playerB, "Island", 4);
+        // You may have Sakashima the Impostor enter the battlefield as a copy of any creature on the battlefield, 
+        // except its name is Sakashima the Impostor, it's legendary in addition to its other types,
+        // and it has "{2}{U}{U}: Return Sakashima the Impostor to its owner's hand at the beginning of the next end step."        
         addCard(Zone.HAND, playerB, "Sakashima the Impostor", 4);
 
         castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Sakashima the Impostor");
@@ -25,4 +32,42 @@ public class SakashimaTheImpostorTest extends CardTestPlayerBase {
 
         assertPowerToughness(playerB, "Sakashima the Impostor", 1, 1);
     }
+
+    /**
+     * I played Sakashima the Imposter copying an opponents Pawn of Ulamaog.
+     * Sakashima gained the following ability: "Whenever Pawn of Ulamog or
+     * another nontoken creature you control dies, you may create a 0/1
+     * colorless Eldrazi Spawn creature token. It has "Sacrifice this creature:
+     * Add {C}." Then Sakashima died due to combat damage and the ability did
+     * not trigger.
+     *
+     */
+    @Test
+    public void copyDiesTriggeredTest() {
+        // Whenever Pawn of Ulamog or another nontoken creature you control dies, you may create a 0/1 colorless 
+        // Eldrazi Spawn creature token. It has "Sacrifice this creature: Add {C}."        
+        addCard(Zone.BATTLEFIELD, playerA, "Pawn of Ulamog", 1); // Creature 2/2
+        addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); // Creature 2/2
+
+        addCard(Zone.BATTLEFIELD, playerB, "Island", 4);
+        // You may have Sakashima the Impostor enter the battlefield as a copy of any creature on the battlefield, 
+        // except its name is Sakashima the Impostor, it's legendary in addition to its other types,
+        // and it has "{2}{U}{U}: Return Sakashima the Impostor to its owner's hand at the beginning of the next end step."        
+        addCard(Zone.HAND, playerB, "Sakashima the Impostor", 4);
+
+        castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Sakashima the Impostor");
+        setChoice(playerB, "Pawn of Ulamog");
+
+        attack(4, playerB, "Sakashima the Impostor");
+        block(4, playerA, "Silvercoat Lion", "Sakashima the Impostor");
+                
+        setStopAt(4, PhaseStep.POSTCOMBAT_MAIN);
+        execute();
+
+        assertGraveyardCount(playerA, "Silvercoat Lion", 1);
+        assertGraveyardCount(playerB, "Sakashima the Impostor", 1);        
+        
+        assertPermanentCount(playerA, "Eldrazi Spawn", 1);
+        assertPermanentCount(playerB, "Eldrazi Spawn", 1);
+    }
 }
diff --git a/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java b/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java
index e037461998..5542547c16 100644
--- a/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java
+++ b/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java
@@ -171,21 +171,24 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge
                         }
                     }
                 }
+                if (isLeavesTheBattlefieldTrigger()) {
+                    source = zce.getTarget();
+                }
+                break;
             case DESTROYED_PERMANENT:
                 if (isLeavesTheBattlefieldTrigger()) {
-                    if (event.getType() == EventType.DESTROYED_PERMANENT) {
-                        source = game.getLastKnownInformation(getSourceId(), Zone.BATTLEFIELD);
-                    } else if (((ZoneChangeEvent) event).getTarget() != null) {
-                        source = ((ZoneChangeEvent) event).getTarget();
-                    } else {
-                        source = game.getLastKnownInformation(getSourceId(), event.getZone());
-                    }
+                    source = game.getLastKnownInformation(getSourceId(), Zone.BATTLEFIELD);
                 }
+                break;
             case PHASED_OUT:
             case PHASED_IN:
+                if (isLeavesTheBattlefieldTrigger()) {
+                    source = game.getLastKnownInformation(getSourceId(), event.getZone());
+                }
                 if (this.zone == Zone.ALL || game.getLastKnownInformation(getSourceId(), zone) != null) {
                     return this.hasSourceObjectAbility(game, source, event);
                 }
+                break;
         }
         return super.isInUseableZone(game, source, event);
     }
diff --git a/Mage/src/main/java/mage/abilities/common/ZoneChangeAllTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/ZoneChangeAllTriggeredAbility.java
index 9d97496682..10ee16f538 100644
--- a/Mage/src/main/java/mage/abilities/common/ZoneChangeAllTriggeredAbility.java
+++ b/Mage/src/main/java/mage/abilities/common/ZoneChangeAllTriggeredAbility.java
@@ -27,6 +27,9 @@ public class ZoneChangeAllTriggeredAbility extends TriggeredAbilityImpl {
 
     public ZoneChangeAllTriggeredAbility(Zone zone, Zone fromZone, Zone toZone, Effect effect, FilterPermanent filter, String rule, boolean optional) {
         super(zone, effect, optional);
+        if (fromZone == Zone.BATTLEFIELD) {
+            setLeavesTheBattlefieldTrigger(true);
+        }        
         this.fromZone = fromZone;
         this.toZone = toZone;
         this.rule = rule;

From 3333cf3287a29e1a2543a3884f5b93b9802d93f9 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Fri, 29 May 2020 16:59:56 +0200
Subject: [PATCH 075/586] * Added test for #6581.

---
 .../cards/abilities/keywords/GoadTest.java    | 81 +++++++++++++++++++
 1 file changed, 81 insertions(+)
 create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/GoadTest.java

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/GoadTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/GoadTest.java
new file mode 100644
index 0000000000..b644006b87
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/GoadTest.java
@@ -0,0 +1,81 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.mage.test.cards.abilities.keywords;
+
+import java.io.FileNotFoundException;
+import mage.constants.MultiplayerAttackOption;
+import mage.constants.PhaseStep;
+import mage.constants.RangeOfInfluence;
+import mage.constants.Zone;
+import mage.game.FreeForAll;
+import mage.game.Game;
+import mage.game.GameException;
+import mage.game.mulligan.MulliganType;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestMultiPlayerBase;
+
+/**
+ *
+ * @author LevelX2
+ */
+public class GoadTest extends CardTestMultiPlayerBase {
+
+    @Override
+    protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
+        Game game = new FreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, MulliganType.GAME_DEFAULT.getMulligan(0), 40);
+        // Player order: A -> D -> C -> B
+        playerA = createPlayer(game, playerA, "PlayerA");
+        playerB = createPlayer(game, playerB, "PlayerB");
+        playerC = createPlayer(game, playerC, "PlayerC");
+        playerD = createPlayer(game, playerD, "PlayerD");
+        return game;
+    }
+
+    /**
+     * In a game of commander, my opponent gained control of Marisi, Breaker of
+     * Coils (until end of turn) and did combat damage to another player. This
+     * caused the creatures damaged by Marisi's controller to be goaded.
+     * However, when the goaded creatures went to attack, they could not attack
+     * me but could attack the (former) controller of Marisi.
+     */
+    @Test
+    public void goadWithNotOwnedCreatureTest() {
+        // Your opponents can't cast spells during combat.
+        // Whenever a creature you control deals combat damage to a player, goad each creature that player controls
+        // (Until your next turn, that creature attacks each combat if able and attacks a player other than you if able.)
+        addCard(Zone.BATTLEFIELD, playerA, "Marisi, Breaker of the Coil", 1); // Creature 5/4
+
+        // Untap target creature an opponent controls and gain control of it until end of turn. 
+        // That creature gains haste until end of turn. 
+        // When you lose control of the creature, tap it.
+        addCard(Zone.HAND, playerD, "Ray of Command"); // Instant {3}{U}
+        addCard(Zone.BATTLEFIELD, playerD, "Island", 4);
+        
+        addCard(Zone.BATTLEFIELD, playerC, "Silvercoat Lion", 3); // Creature 2/2
+        
+        castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerD, "Ray of Command", "Marisi, Breaker of the Coil");
+        
+        attack(2, playerD, "Marisi, Breaker of the Coil", playerC);
+        
+        attack(3, playerC, "Silvercoat Lion", playerA);
+        attack(3, playerC, "Silvercoat Lion", playerB);
+        attack(3, playerC, "Silvercoat Lion", playerD);
+        
+        setStopAt(4, PhaseStep.BEGIN_COMBAT);
+        execute();
+
+        
+        assertGraveyardCount(playerD, "Ray of Command", 1);
+        assertPermanentCount(playerA, "Marisi, Breaker of the Coil", 1);
+                
+        assertLife(playerC, 35);
+
+        assertLife(playerB, 38);        
+        assertLife(playerA, 38);
+        assertLife(playerD, 38);                
+    }
+
+}

From d5c46816be2c7512deafa6fcd97b67f41838845c Mon Sep 17 00:00:00 2001
From: 18ths <13023067+18ths@users.noreply.github.com>
Date: Sat, 30 May 2020 21:52:22 +0200
Subject: [PATCH 076/586] fixed world rule with range of influence

---
 .../rules/WorldEnchantmentsRuleTest.java      | 39 +++++++++++++++----
 Mage/src/main/java/mage/game/GameImpl.java    |  8 +++-
 2 files changed, 38 insertions(+), 9 deletions(-)

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/rules/WorldEnchantmentsRuleTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/rules/WorldEnchantmentsRuleTest.java
index 0d8fa4034a..6a5258b212 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/rules/WorldEnchantmentsRuleTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/rules/WorldEnchantmentsRuleTest.java
@@ -4,14 +4,14 @@ package org.mage.test.cards.rules;
 import mage.constants.PhaseStep;
 import mage.constants.Zone;
 import org.junit.Test;
-import org.mage.test.serverside.base.CardTestPlayerBase;
+import org.mage.test.serverside.base.CardTestMultiPlayerBase;
 
 /**
  *
  * @author LevelX2
  */
 
-public class WorldEnchantmentsRuleTest extends CardTestPlayerBase {
+public class WorldEnchantmentsRuleTest extends CardTestMultiPlayerBase {
 
     /**
      * 704.5m If two or more permanents have the supertype world, all except the one that has had 
@@ -21,24 +21,47 @@ public class WorldEnchantmentsRuleTest extends CardTestPlayerBase {
      * 
      */
     @Test
-    public void TestTwoWorldEnchantsments() {
+    public void TestTwoWorldEnchantments() {
         addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
         addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
         addCard(Zone.HAND, playerA, "Nether Void", 1);
         addCard(Zone.HAND, playerA, "Silvercoat Lion", 1);
         
-        addCard(Zone.BATTLEFIELD, playerB, "Swamp", 7);
-        addCard(Zone.HAND, playerB, "Nether Void", 1);
+        addCard(Zone.BATTLEFIELD, playerD, "Swamp", 7);
+        addCard(Zone.HAND, playerD, "Nether Void", 1);
 
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Nether Void");
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Silvercoat Lion"); // just needed to get different craete time to second Nether Void
-        castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Nether Void");
+        castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerD, "Nether Void");
                 
         setStopAt(2, PhaseStep.END_TURN);
         execute();
+        assertAllCommandsUsed();
 
         assertPermanentCount(playerA, "Nether Void", 0);
-        assertPermanentCount(playerB, "Nether Void", 1);
+        assertPermanentCount(playerD, "Nether Void", 1);
+    }
+
+    // 801.12 The "world rule" applies to a permanent only if other world permanents are within its controller's range of influence.
+    @Test
+    public void TestTwoWorldEnchantmentsNotInRangeOfInfluence() {
+        addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
+        addCard(Zone.HAND, playerA, "Nether Void", 1);
+        addCard(Zone.HAND, playerA, "Silvercoat Lion", 1);
+
+        addCard(Zone.BATTLEFIELD, playerC, "Swamp", 7);
+        addCard(Zone.HAND, playerC, "Nether Void", 1);
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Nether Void");
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Silvercoat Lion"); // just needed to get different craete time to second Nether Void
+        castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerC, "Nether Void");
+
+        setStopAt(3, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerA, "Nether Void", 1);
+        assertPermanentCount(playerC, "Nether Void", 1);
     }
-        
 }
\ No newline at end of file
diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java
index 612783bf0a..bed1000a87 100644
--- a/Mage/src/main/java/mage/game/GameImpl.java
+++ b/Mage/src/main/java/mage/game/GameImpl.java
@@ -2235,13 +2235,19 @@ public abstract class GameImpl implements Game, Serializable {
                     newestPermanent = null;
                 }
             }
+
+            PlayerList newestPermanentControllerRange = state.getPlayersInRange(newestPermanent.getControllerId(), this);
+
+            // 801.12 The "world rule" applies to a permanent only if other world permanents are within its controller's range of influence.
             for (Permanent permanent : worldEnchantment) {
-                if (!Objects.equals(newestPermanent, permanent)) {
+                if (newestPermanentControllerRange.contains(permanent.getControllerId())
+                        && !Objects.equals(newestPermanent, permanent)) {
                     movePermanentToGraveyardWithInfo(permanent);
                     somethingHappened = true;
                 }
             }
         }
+
         //TODO: implement the rest
 
         return somethingHappened;

From e7684e4bba6905747e26def099708f522be9fbac Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Mon, 1 Jun 2020 09:37:17 +0400
Subject: [PATCH 077/586] Test framework improves: * added command to check
 graveyard count command; * added wrong cast/activate command auto-checks in
 strict mode;

---
 .../cards/continuous/IrencragFeatTest.java    | 46 ++++++++++--
 .../cost/adventure/AdventureCardsTest.java    | 41 +++++++----
 .../java/org/mage/test/player/TestPlayer.java | 71 ++++++++++++++++---
 .../base/impl/CardTestPlayerAPIImpl.java      |  6 ++
 .../java/mage/abilities/SpellAbility.java     |  6 +-
 5 files changed, 137 insertions(+), 33 deletions(-)

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/IrencragFeatTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/IrencragFeatTest.java
index 93f09009b9..3ebb83bc4c 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/IrencragFeatTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/IrencragFeatTest.java
@@ -18,14 +18,23 @@ public class IrencragFeatTest extends CardTestPlayerBase {
         addCard(Zone.HAND, playerA, "Irencrag Feat", 1);
         addCard(Zone.HAND, playerA, "Dwarven Trader", 2);
 
+        // 1
+        checkPlayableAbility("can cast before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Dwarven Trader", true);
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Irencrag Feat");
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+
+        // 2
+        checkPlayableAbility("can cast after feat 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Dwarven Trader", true);
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dwarven Trader");
-        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dwarven Trader");
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+
+        // 3
+        checkPlayableAbility("can't cast after feat 2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Dwarven Trader", false);
 
         setStrictChooseMode(true);
         setStopAt(1, PhaseStep.BEGIN_COMBAT);
         execute();
-        //assertAllCommandsUsed(); // second trader must be restricted to cast
+        assertAllCommandsUsed();
 
         assertHandCount(playerA, "Dwarven Trader", 1);
         assertPermanentCount(playerA, "Dwarven Trader", 1);
@@ -38,16 +47,32 @@ public class IrencragFeatTest extends CardTestPlayerBase {
         addCard(Zone.HAND, playerA, "Irencrag Feat", 1);
         addCard(Zone.HAND, playerA, "Dwarven Trader", 4);
 
+        // 1
+        checkPlayableAbility("can cast before feat 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Dwarven Trader", true);
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dwarven Trader");
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+
+        // 2
+        checkPlayableAbility("can cast before feat 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Dwarven Trader", true);
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dwarven Trader");
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+
+        // 3
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Irencrag Feat");
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+
+        // 4
+        checkPlayableAbility("can cast after feat 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Dwarven Trader", true);
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dwarven Trader");
-        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dwarven Trader");
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+
+        // 5
+        checkPlayableAbility("can't cast after feat 2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Dwarven Trader", false);
 
         setStrictChooseMode(true);
         setStopAt(1, PhaseStep.BEGIN_COMBAT);
         execute();
-        //assertAllCommandsUsed(); // second trader must be restricted to cast
+        assertAllCommandsUsed();
 
         assertHandCount(playerA, "Dwarven Trader", 1);
         assertPermanentCount(playerA, "Dwarven Trader", 3);
@@ -59,6 +84,7 @@ public class IrencragFeatTest extends CardTestPlayerBase {
         addCard(Zone.HAND, playerA, "Irencrag Feat", 1);
         addCard(Zone.HAND, playerA, "Lightning Bolt", 4);
 
+        // no restrictions for stack
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB);
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB);
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Irencrag Feat");
@@ -80,17 +106,25 @@ public class IrencragFeatTest extends CardTestPlayerBase {
         addCard(Zone.HAND, playerA, "Irencrag Feat", 1);
         addCard(Zone.HAND, playerA, "Lightning Bolt", 4);
 
+        // on stack before
+        checkPlayableAbility("can cast before feat 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Lightning Bolt", true);
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB);
+        checkPlayableAbility("can cast before feat 2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Lightning Bolt", true);
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB);
+
+        // feat
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Irencrag Feat");
         waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+
+        // after resolve
+        checkPlayableAbility("can cast after feat 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Lightning Bolt", true);
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB);
-        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB);
+        checkPlayableAbility("can't cast after feat 2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Lightning Bolt", false);
 
         setStrictChooseMode(true);
         setStopAt(1, PhaseStep.BEGIN_COMBAT);
         execute();
-        //assertAllCommandsUsed(0); // second bolt must be restricted to cast
+        assertAllCommandsUsed();
 
         assertHandCount(playerA, "Lightning Bolt", 4 - 3);
         assertLife(playerB, 20 - 3 * 3);
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/adventure/AdventureCardsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/adventure/AdventureCardsTest.java
index 6fdacc5560..c73adcb959 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/cost/adventure/AdventureCardsTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/adventure/AdventureCardsTest.java
@@ -39,14 +39,20 @@ public class AdventureCardsTest extends CardTestPlayerBase {
 
     @Test
     public void testCantCastTreatsToShareTwice() {
-        setStrictChooseMode(true);
-        addCard(Zone.BATTLEFIELD, playerA, "Forest", 2);
         addCard(Zone.HAND, playerA, "Curious Pair");
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 2);
+
+        checkPlayableAbility("can play on first", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Treats to Share", true);
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share");
-        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share");
+
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+        checkPlayableAbility("can't play on second", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Treats to Share", false);
+
+        setStrictChooseMode(true);
         setStopAt(1, PhaseStep.BEGIN_COMBAT);
         execute();
-        assertActionsCount(playerA, 1);
+        assertAllCommandsUsed();
+
         assertHandCount(playerA, 0);
         assertPermanentCount(playerA, "Food", 1);
         assertExileCount(playerA, "Curious Pair", 1);
@@ -430,18 +436,21 @@ public class AdventureCardsTest extends CardTestPlayerBase {
 
     @Test
     public void testCantCastCuriousPairWithMelek() {
-        setStrictChooseMode(true);
-        addCard(Zone.BATTLEFIELD, playerA, "Melek, Izzet Paragon");
-        addCard(Zone.BATTLEFIELD, playerA, "Forest", 2);
         removeAllCardsFromLibrary(playerA);
+
+        addCard(Zone.BATTLEFIELD, playerA, "Melek, Izzet Paragon");
+        //
         addCard(Zone.LIBRARY, playerA, "Curious Pair");
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 2);
 
-        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Curious Pair");
+        checkPlayableAbility("can't play", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Curious Pair", false);
+        checkPlayableAbility("can play", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Treats to Share", true);
 
+        setStrictChooseMode(true);
         setStopAt(1, PhaseStep.BEGIN_COMBAT);
         execute();
+        assertAllCommandsUsed();
 
-        assertActionsCount(playerA, 1);
         assertPermanentCount(playerA, "Curious Pair", 0);
         assertLibraryCount(playerA, 1);
     }
@@ -477,19 +486,21 @@ public class AdventureCardsTest extends CardTestPlayerBase {
 
     @Test
     public void testCantCastTreatsToShareWithGarruksHorde() {
-        setStrictChooseMode(true);
-        addCard(Zone.BATTLEFIELD, playerA, "Garruk's Horde");
-        addCard(Zone.BATTLEFIELD, playerA, "Forest");
         removeAllCardsFromLibrary(playerA);
+
+        addCard(Zone.BATTLEFIELD, playerA, "Garruk's Horde");
+        //
         addCard(Zone.LIBRARY, playerA, "Curious Pair");
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 2);
 
-        // showAvaileableAbilities("abils", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
-        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share");
+        checkPlayableAbility("can play", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Curious Pair", true);
+        checkPlayableAbility("can't play", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Treats to Share", false);
 
+        setStrictChooseMode(true);
         setStopAt(1, PhaseStep.BEGIN_COMBAT);
         execute();
+        assertAllCommandsUsed();
 
-        assertActionsCount(playerA, 1);
         assertPermanentCount(playerA, "Food", 0);
         assertLibraryCount(playerA, 1);
     }
diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
index f5bf8f8b2a..fdb665dd5e 100644
--- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
+++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
@@ -42,6 +42,7 @@ import mage.game.match.Match;
 import mage.game.match.MatchPlayer;
 import mage.game.permanent.Permanent;
 import mage.game.permanent.PermanentToken;
+import mage.game.stack.Spell;
 import mage.game.stack.StackObject;
 import mage.game.tournament.Tournament;
 import mage.player.ai.ComputerPlayer;
@@ -430,6 +431,8 @@ public class TestPlayer implements Player {
         // two search mode: for cards/permanents (strict) and for abilities (like)
         if (object instanceof Ability) {
             return object.getName().startsWith(nameOrAlias);
+        } else if (object instanceof Spell) {
+            return ((Spell) object).getSpellAbility().getName().startsWith(nameOrAlias);
         } else {
             return object.getName().equals(nameOrAlias);
         }
@@ -668,11 +671,13 @@ public class TestPlayer implements Player {
                     }
                 } else if (action.getAction().startsWith("waitStackResolved")) {
                     if (game.getStack().isEmpty()) {
-                        // can use next command
+                        // all done, can use next command
                         actions.remove(action);
+                        continue;
                     } else {
-                        // need pass to empty stack
-                        break;
+                        // need to wait (don't remove command)
+                        tryToPlayPriority(game);
+                        return true;
                     }
                 } else if (action.getAction().startsWith("playerAction:")) {
                     String command = action.getAction();
@@ -692,6 +697,7 @@ public class TestPlayer implements Player {
                             game.concede(getId());
                             ((GameImpl) game).checkConcede();
                             actions.remove(action);
+                            return true;
                         }
                     }
                 } else if (action.getAction().startsWith(AI_PREFIX)) {
@@ -786,6 +792,13 @@ public class TestPlayer implements Player {
                             wasProccessed = true;
                         }
 
+                        // check graveyard count: card name, count
+                        if (params[0].equals(CHECK_COMMAND_GRAVEYARD_COUNT) && params.length == 3) {
+                            assertGraveyardCount(action, game, computerPlayer, params[1], Integer.parseInt(params[2]));
+                            actions.remove(action);
+                            wasProccessed = true;
+                        }
+
                         // check hand count: count
                         if (params[0].equals(CHECK_COMMAND_HAND_COUNT) && params.length == 2) {
                             assertHandCount(action, game, computerPlayer, Integer.parseInt(params[1]));
@@ -842,7 +855,9 @@ public class TestPlayer implements Player {
                             wasProccessed = true;
                         }
                     }
-                    if (!wasProccessed) {
+                    if (wasProccessed) {
+                        return true;
+                    } else {
                         Assert.fail("Unknow check command or params: " + command);
                     }
                 } else if (action.getAction().startsWith(SHOW_PREFIX)) {
@@ -938,17 +953,23 @@ public class TestPlayer implements Player {
                         }
                     }
 
-                    if (!wasProccessed) {
+                    if (wasProccessed) {
+                        return true;
+                    } else {
                         Assert.fail("Unknow show command or params: " + command);
                     }
                 }
-            }
-        }
-        if (AIPlayer) {
-            computerPlayer.priority(game);
-        } else {
-            computerPlayer.pass(game);
+
+                // you don't need to use stack command all the time, so some cast commands can be skiped to next check
+                if (game.getStack().isEmpty()) {
+                    this.chooseStrictModeFailed("cast/activate", game,
+                            "Can't find available command - " + action.getAction() + " (use checkPlayableAbility for non castable checks)", true);
+                }
+            } // turn/step
         }
+
+        tryToPlayPriority(game);
+
         // check to prevent endless loops
         if (numberOfActions == actions.size()) {
             foundNoAction++;
@@ -962,6 +983,14 @@ public class TestPlayer implements Player {
         return false;
     }
 
+    private void tryToPlayPriority(Game game) {
+        if (AIPlayer) {
+            computerPlayer.priority(game);
+        } else {
+            computerPlayer.pass(game);
+        }
+    }
+
     private Permanent findPermanentWithAssert(PlayerAction action, Game game, Player player, String cardName) {
         for (Permanent perm : game.getBattlefield().getAllPermanents()) {
             // need by controller
@@ -1208,6 +1237,17 @@ public class TestPlayer implements Player {
         Assert.assertEquals(action.getActionName() + " - card " + permanentName + " must exists in exile zone with " + count + " instances", count, foundedCount);
     }
 
+    private void assertGraveyardCount(PlayerAction action, Game game, Player player, String permanentName, int count) {
+        int foundedCount = 0;
+        for (Card card : player.getGraveyard().getCards(game)) {
+            if (isObjectHaveTargetNameOrAlias(card, permanentName) && card.isOwnedBy(player.getId())) {
+                foundedCount++;
+            }
+        }
+
+        Assert.assertEquals(action.getActionName() + " - card " + permanentName + " must exists in graveyard zone with " + count + " instances", count, foundedCount);
+    }
+
     private void assertHandCount(PlayerAction action, Game game, Player player, int count) {
         Assert.assertEquals(action.getActionName() + " - hand must contain " + count, count, player.getHand().size());
     }
@@ -1668,7 +1708,16 @@ public class TestPlayer implements Player {
     }
 
     private void chooseStrictModeFailed(String choiceType, Game game, String reason) {
+        chooseStrictModeFailed(choiceType, game, reason, false);
+    }
+
+    private void chooseStrictModeFailed(String choiceType, Game game, String reason, boolean printAbilities) {
         if (strictChooseMode && !AICanChooseInStrictMode) {
+            if (printAbilities) {
+                printStart("Available abilities for " + computerPlayer.getName());
+                printAbilities(game, computerPlayer.getPlayable(game, true));
+                printEnd();
+            }
             Assert.fail("Missing " + choiceType + " def for"
                     + " turn " + game.getTurnNum()
                     + ", step " + (game.getStep() != null ? game.getStep().getType().name() : "not started")
diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
index 8727880ee6..6d5b96b9cf 100644
--- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
+++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
@@ -93,6 +93,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     public static final String CHECK_COMMAND_PERMANENT_COUNT = "PERMANENT_COUNT";
     public static final String CHECK_COMMAND_PERMANENT_COUNTERS = "PERMANENT_COUNTERS";
     public static final String CHECK_COMMAND_EXILE_COUNT = "EXILE_COUNT";
+    public static final String CHECK_COMMAND_GRAVEYARD_COUNT = "GRAVEYARD_COUNT";
     public static final String CHECK_COMMAND_HAND_COUNT = "HAND_COUNT";
     public static final String CHECK_COMMAND_HAND_CARD_COUNT = "HAND_CARD_COUNT";
     public static final String CHECK_COMMAND_COMMAND_CARD_COUNT = "COMMAND_CARD_COUNT";
@@ -380,6 +381,11 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
         check(checkName, turnNum, step, player, CHECK_COMMAND_EXILE_COUNT, permanentName, count.toString());
     }
 
+    public void checkGraveyardCount(String checkName, int turnNum, PhaseStep step, TestPlayer player, String permanentName, Integer count) {
+        //Assert.assertNotEquals("", permanentName);
+        check(checkName, turnNum, step, player, CHECK_COMMAND_GRAVEYARD_COUNT, permanentName, count.toString());
+    }
+
     public void checkHandCount(String checkName, int turnNum, PhaseStep step, TestPlayer player, Integer count) {
         check(checkName, turnNum, step, player, CHECK_COMMAND_HAND_COUNT, count.toString());
     }
diff --git a/Mage/src/main/java/mage/abilities/SpellAbility.java b/Mage/src/main/java/mage/abilities/SpellAbility.java
index 1978d8e162..a0ee47b51c 100644
--- a/Mage/src/main/java/mage/abilities/SpellAbility.java
+++ b/Mage/src/main/java/mage/abilities/SpellAbility.java
@@ -145,9 +145,13 @@ public class SpellAbility extends ActivatedAbilityImpl {
         this.costs.clearPaid();
     }
 
+    public String getName() {
+        return this.name;
+    }
+
     @Override
     public String toString() {
-        return this.name;
+        return getName();
     }
 
     @Override

From 121dc3501ef3eb89cc35b317fcde40b1367eb319 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Mon, 1 Jun 2020 09:41:48 +0400
Subject: [PATCH 078/586] * Split cards - fixed not working combo with
 suspend/delay abilities (#946, #6549);

---
 .../cards/abilities/keywords/SuspendTest.java | 131 ++++++++++++++++--
 .../abilities/keyword/SuspendAbility.java     |  73 +++++-----
 Mage/src/main/java/mage/cards/SplitCard.java  |  15 +-
 3 files changed, 166 insertions(+), 53 deletions(-)

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SuspendTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SuspendTest.java
index 8e6f75590f..5f699c9382 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SuspendTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SuspendTest.java
@@ -1,4 +1,3 @@
-
 package org.mage.test.cards.abilities.keywords;
 
 import mage.abilities.keyword.HasteAbility;
@@ -9,15 +8,13 @@ import org.junit.Test;
 import org.mage.test.serverside.base.CardTestPlayerBase;
 
 /**
- *
- * @author LevelX2
+ * @author LevelX2, JayDi85
  */
 public class SuspendTest extends CardTestPlayerBase {
 
     /**
      * Tests Epochrasite works (give suspend to a exiled card) When Epochrasite
      * dies, exile it with three time counters on it and it gains suspend.
-     *
      */
     @Test
     public void testEpochrasite() {
@@ -46,7 +43,6 @@ public class SuspendTest extends CardTestPlayerBase {
      * Tests Jhoira of the Ghitu works (give suspend to a exiled card) {2},
      * Exile a nonland card from your hand: Put four time counters on the exiled
      * card. If it doesn't have suspend, it gains suspend.
-     *
      */
     @Test
     public void testJhoiraOfTheGhitu() {
@@ -71,7 +67,6 @@ public class SuspendTest extends CardTestPlayerBase {
     /**
      * Tests that a spell countered with delay goes to exile with 3 time
      * counters and can be cast after the 3 counters are removed
-     *
      */
     @Test
     public void testDelay() {
@@ -141,7 +136,6 @@ public class SuspendTest extends CardTestPlayerBase {
     /**
      * Suppression Field incorrectly makes suspend cards cost 2 more to suspend.
      * It made my Rift Bolt cost 2R to suspend instead of R
-     *
      */
     @Test
     public void testCostManipulation() {
@@ -164,9 +158,8 @@ public class SuspendTest extends CardTestPlayerBase {
      * Cards cast from other zones that aren't the hand should not trigger
      * Knowledge Pool, as it states that only cards cast from the hand should be
      * exiled afterwards.
-     *
+     * <p>
      * Example: cards coming off suspend shouldn't trigger Knowledge Pool.
-     *
      */
     @Test
     public void testThatNotCastFromHand() {
@@ -199,4 +192,124 @@ public class SuspendTest extends CardTestPlayerBase {
         assertPermanentCount(playerA, "Silvercoat Lion", 0);
 
     }
+
+    /*
+    Delay {1}{U}
+
+    Counter target spell. If the spell is countered this way, exile it with three time counters on it instead of putting
+    it into its owner’s graveyard. If it doesn’t have suspend, it gains suspend. (At the beginning of its owner’s upkeep,
+    remove a time counter from that card. When the last is removed, the player plays it without paying its mana cost.
+    If it’s a creature, it has haste.)
+
+    Bug: Casting Delay on a fused Wear // Tear resulted in time counters never coming off it. It just sat there with
+    three counters every turn. See https://github.com/magefree/mage/issues/6549
+    */
+
+    @Test
+    public void test_Delay_SimpleSpell() {
+        //
+        addCard(Zone.HAND, playerA, "Delay", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
+        //
+        addCard(Zone.HAND, playerA, "Lightning Bolt", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
+
+        // cast spell and counter it with delay
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Delay", "Lightning Bolt", "Lightning Bolt");
+        //
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+        checkLife("after counter", 1, PhaseStep.PRECOMBAT_MAIN, playerB, 20);
+        checkExileCount("after counter", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 1);
+
+        // 3 time counters removes on upkeep (3, 5, 7) and cast again
+        setChoice(playerA, "Cast");
+        addTarget(playerA, playerB);
+        checkLife("after suspend", 7, PhaseStep.PRECOMBAT_MAIN, playerB, 20 - 3);
+        checkGraveyardCount("after suspend", 7, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 1);
+
+        setStrictChooseMode(true);
+        setStopAt(7, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+    }
+
+    @Test
+    public void test_Delay_SplitSingle() {
+        addCard(Zone.HAND, playerA, "Delay", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 5);
+        //
+        // Wear {1}{R} Destroy target artifact.
+        // Tear {W} Destroy target enchantment.
+        addCard(Zone.HAND, playerA, "Wear // Tear", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5);
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
+        //
+        addCard(Zone.BATTLEFIELD, playerB, "Bident of Thassa", 1); // Legendary Enchantment Artifact
+        addCard(Zone.BATTLEFIELD, playerB, "Bow of Nylea", 1); // Legendary Enchantment Artifact
+
+        // cast spell and counter it with delay
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wear", "Bident of Thassa");
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Delay", "Wear", "Wear");
+        //
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+        checkPermanentCount("after counter", 1, PhaseStep.PRECOMBAT_MAIN, playerB, "Bident of Thassa", 1);
+        checkPermanentCount("after counter", 1, PhaseStep.PRECOMBAT_MAIN, playerB, "Bow of Nylea", 1);
+        checkExileCount("after counter", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wear // Tear", 1);
+
+        // 3 time counters removes on upkeep (3, 5, 7) and cast again
+        setChoice(playerA, "Cast Wear");
+        addTarget(playerA, "Bident of Thassa");
+        checkPermanentCount("after suspend", 7, PhaseStep.PRECOMBAT_MAIN, playerB, "Bident of Thassa", 0);
+        checkPermanentCount("after suspend", 7, PhaseStep.PRECOMBAT_MAIN, playerB, "Bow of Nylea", 1);
+        checkGraveyardCount("after suspend", 7, PhaseStep.PRECOMBAT_MAIN, playerA, "Wear // Tear", 1);
+
+        setStrictChooseMode(true);
+        setStopAt(7, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+    }
+
+    @Test
+    public void test_Delay_SplitFused() {
+        /*
+        Bug: Casting Delay on a fused Wear // Tear resulted in time counters never coming off it. It just sat there with
+        three counters every turn. See https://github.com/magefree/mage/issues/6549
+        */
+
+        //
+        addCard(Zone.HAND, playerA, "Delay", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 5);
+        //
+        // Wear {1}{R} Destroy target artifact.
+        // Tear {W} Destroy target enchantment.
+        addCard(Zone.HAND, playerA, "Wear // Tear", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5);
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
+        //
+        addCard(Zone.BATTLEFIELD, playerB, "Bident of Thassa", 1); // Legendary Enchantment Artifact
+        addCard(Zone.BATTLEFIELD, playerB, "Bow of Nylea", 1); // Legendary Enchantment Artifact
+
+        // cast fused spell and counter it with delay
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "fused Wear // Tear");
+        addTarget(playerA, "Bident of Thassa");
+        addTarget(playerA, "Bow of Nylea");
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Delay", "Cast fused Wear // Tear", "Cast fused Wear // Tear");
+        //
+        checkPermanentCount("after counter", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Bident of Thassa", 1);
+        checkPermanentCount("after counter", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Bow of Nylea", 1);
+        checkExileCount("after counter", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Wear // Tear", 1);
+
+        // 3 time counters removes on upkeep (3, 5, 7) and cast again (fused cards can't be played from exile zone, so select split spell only)
+        setChoice(playerA, "Cast Wear");
+        addTarget(playerA, "Bident of Thassa");
+        checkPermanentCount("after suspend", 7, PhaseStep.PRECOMBAT_MAIN, playerB, "Bident of Thassa", 0);
+        checkPermanentCount("after suspend", 7, PhaseStep.PRECOMBAT_MAIN, playerB, "Bow of Nylea", 1);
+        checkGraveyardCount("after suspend", 7, PhaseStep.PRECOMBAT_MAIN, playerA, "Wear // Tear", 1);
+
+        setStrictChooseMode(true);
+        setStopAt(7, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+    }
 }
diff --git a/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java b/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java
index 2bca55f26d..3b066bf316 100644
--- a/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java
@@ -1,8 +1,5 @@
 package mage.abilities.keyword;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
 import mage.MageObject;
 import mage.MageObjectReference;
 import mage.abilities.Ability;
@@ -27,10 +24,13 @@ import mage.game.permanent.Permanent;
 import mage.players.Player;
 import mage.target.targetpointer.FixedTarget;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
 /**
- *
  * 502.59. Suspend
- *
+ * <p>
  * 502.59a Suspend is a keyword that represents three abilities. The first is a
  * static ability that functions while the card with suspend is in a player's
  * hand. The second and third are triggered abilities that function in the
@@ -42,13 +42,13 @@ import mage.target.targetpointer.FixedTarget;
  * card, if it's removed from the game, play it without paying its mana cost if
  * able. If you can't, it remains removed from the game. If you play it this way
  * and it's a creature, it gains haste until you lose control of it."
- *
+ * <p>
  * 502.59b A card is "suspended" if it's in the removed-from-the-game zone, has
  * suspend, and has a time counter on it.
- *
+ * <p>
  * 502.59c Playing a spell as an effect of its suspend ability follows the rules
  * for paying alternative costs in rules 409.1b and 409.1f-h.
- *
+ * <p>
  * The phrase "if you could play this card from your hand" checks only for
  * timing restrictions and permissions. This includes both what's inherent in
  * the card's type (for example, if the card with suspend is a creature, it must
@@ -57,38 +57,38 @@ import mage.target.targetpointer.FixedTarget;
  * actually follow all steps in playing the card is irrelevant. If the card is
  * impossible to play due to a lack of legal targets or an unpayable mana cost,
  * for example, it may still be removed from the game with suspend.
- *
+ * <p>
  * Removing a card from the game with its suspend ability is not playing that
  * card. This action doesn't use the stack and can't be responded to.
- *
+ * <p>
  * If a spell with suspend has targets, the targets are chosen when the spell is
  * played, not when it's removed from the game.
- *
+ * <p>
  * If the first triggered ability of suspend is countered, no time counter is
  * removed. The ability will trigger again during its owner's next upkeep.
- *
+ * <p>
  * When the last time counter is removed from a suspended card, the second
  * triggered ability of suspend will trigger. It doesn't matter why the time
  * counter was removed or whose effect removed it. (The _Time Spiral_ reminder
  * text is misleading on this point.)
- *
+ * <p>
  * If the second triggered ability of suspend is countered, the card can't be
  * played. It remains in the removed-from-the-game zone without any time
  * counters on it for the rest of the game, and it's no longer considered
  * suspended.
- *
+ * <p>
  * If the second triggered ability of suspend resolves, the card's owner must
  * play the spell if possible, even if that player doesn't want to. Normal
  * timing considerations for the spell are ignored (for example, if the
  * suspended card is a creature and this ability resolves during your upkeep,
  * you're able to play the card), but other play restrictions are not ignored.
- *
+ * <p>
  * If the second triggered ability of suspend resolves and the suspended card
  * can't be played due to a lack of legal targets or a play restriction, for
  * example, it remains in the removed-from-the-game zone without any time
  * counters on it for the rest of the game, and it's no longer considered
  * suspended.
- *
+ * <p>
  * As the second triggered ability of suspend resolves, if playing the suspended
  * card involves an additional cost, the card's owner must pay that cost if
  * able. If they can't, the card remains removed from the game. If the
@@ -99,14 +99,12 @@ import mage.target.targetpointer.FixedTarget;
  * cost, then they have a choice: The player may play the spell, produce mana,
  * and pay the cost. Or the player may choose to play no mana abilities, thus
  * making the card impossible to play because the additional mana can't be paid.
- *
+ * <p>
  * A creature played via suspend comes into play with haste. It still has haste
  * after the first turn it's in play as long as the same player controls it. As
  * soon as another player takes control of it, it loses haste.
  *
- *
  * @author LevelX2
- *
  */
 public class SuspendAbility extends SpecialAction {
 
@@ -117,9 +115,9 @@ public class SuspendAbility extends SpecialAction {
      * Gives the card the SuspendAbility
      *
      * @param suspend - amount of time counters, if Integer.MAX_VALUE is set
-     * there will be {X} costs and X counters added
-     * @param cost - null is used for temporary gained suspend ability
-     * @param card - card that has the suspend ability
+     *                there will be {X} costs and X counters added
+     * @param cost    - null is used for temporary gained suspend ability
+     * @param card    - card that has the suspend ability
      */
     public SuspendAbility(int suspend, ManaCost cost, Card card) {
         this(suspend, cost, card, false);
@@ -138,13 +136,13 @@ public class SuspendAbility extends SpecialAction {
         }
         StringBuilder sb = new StringBuilder("Suspend ");
         if (cost != null) {
-            sb.append(suspend == Integer.MAX_VALUE ? "X" : suspend).append("&mdash;").append(cost.getText()).append(suspend 
+            sb.append(suspend == Integer.MAX_VALUE ? "X" : suspend).append("&mdash;").append(cost.getText()).append(suspend
                     == Integer.MAX_VALUE ? ". X can't be 0" : "");
             if (!shortRule) {
                 sb.append(" <i>(Rather than cast this card from your hand, pay ")
                         .append(cost.getText())
                         .append(" and exile it with ")
-                        .append((suspend == 1 ? "a time counter" : (suspend == Integer.MAX_VALUE 
+                        .append((suspend == 1 ? "a time counter" : (suspend == Integer.MAX_VALUE
                                 ? "X time counters" : suspend + " time counters")))
                         .append(" on it.")
                         .append(" At the beginning of your upkeep, remove a time counter. "
@@ -176,7 +174,7 @@ public class SuspendAbility extends SpecialAction {
         ability.setControllerId(card.getOwnerId());
         game.getState().addOtherAbility(card, ability);
 
-        SuspendBeginningOfUpkeepInterveningIfTriggeredAbility ability1 = 
+        SuspendBeginningOfUpkeepInterveningIfTriggeredAbility ability1 =
                 new SuspendBeginningOfUpkeepInterveningIfTriggeredAbility();
         ability1.setSourceId(card.getId());
         ability1.setControllerId(card.getOwnerId());
@@ -214,8 +212,8 @@ public class SuspendAbility extends SpecialAction {
         MageObject object = game.getObject(sourceId);
         return new ActivationStatus(object.isInstant()
                 || object.hasAbility(FlashAbility.getInstance(), game)
-                || null != game.getContinuousEffects().asThough(sourceId, 
-                        AsThoughEffectType.CAST_AS_INSTANT, this, playerId, game)
+                || null != game.getContinuousEffects().asThough(sourceId,
+                AsThoughEffectType.CAST_AS_INSTANT, this, playerId, game)
                 || game.canPlaySorcery(playerId), null);
     }
 
@@ -241,7 +239,7 @@ class SuspendExileEffect extends OneShotEffect {
 
     public SuspendExileEffect(int suspend) {
         super(Outcome.PutCardInPlay);
-        this.staticText = new StringBuilder("Suspend ").append(suspend 
+        this.staticText = new StringBuilder("Suspend ").append(suspend
                 == Integer.MAX_VALUE ? "X" : suspend).toString();
         this.suspend = suspend;
     }
@@ -262,7 +260,7 @@ class SuspendExileEffect extends OneShotEffect {
         Player controller = game.getPlayer(source.getControllerId());
         if (card != null && controller != null) {
             UUID exileId = SuspendAbility.getSuspendExileId(controller.getId(), game);
-            if (controller.moveCardToExileWithInfo(card, exileId, "Suspended cards of " 
+            if (controller.moveCardToExileWithInfo(card, exileId, "Suspended cards of "
                     + controller.getName(), source.getSourceId(), game, Zone.HAND, true)) {
                 if (suspend == Integer.MAX_VALUE) {
                     suspend = source.getManaCostsToPay().getX();
@@ -298,11 +296,9 @@ class SuspendPlayCardAbility extends TriggeredAbilityImpl {
     public boolean checkTrigger(GameEvent event, Game game) {
         if (event.getTargetId().equals(getSourceId())) {
             Card card = game.getCard(getSourceId());
-            if (card != null
+            return card != null
                     && game.getState().getZone(card.getId()) == Zone.EXILED
-                    && card.getCounters(game).getCount(CounterType.TIME) == 0) {
-                return true;
-            }
+                    && card.getCounters(game).getCount(CounterType.TIME) == 0;
         }
         return false;
     }
@@ -340,8 +336,9 @@ class SuspendPlayCardEffect extends OneShotEffect {
         Card card = game.getCard(source.getSourceId());
         if (player != null && card != null) {
             // remove temporary suspend ability (used e.g. for Epochrasite)
+            // TODO: isGainedTemporary is not set or use in other places, so it can be deleted?!
             List<Ability> abilitiesToRemove = new ArrayList<>();
-            for (Ability ability : card.getAbilities()) {
+            for (Ability ability : card.getAbilities(game)) {
                 if (ability instanceof SuspendAbility) {
                     if (((SuspendAbility) ability).isGainedTemporary()) {
                         abilitiesToRemove.add(ability);
@@ -350,7 +347,7 @@ class SuspendPlayCardEffect extends OneShotEffect {
             }
             if (!abilitiesToRemove.isEmpty()) {
                 for (Ability ability : card.getAbilities()) {
-                    if (ability instanceof SuspendBeginningOfUpkeepInterveningIfTriggeredAbility 
+                    if (ability instanceof SuspendBeginningOfUpkeepInterveningIfTriggeredAbility
                             || ability instanceof SuspendPlayCardAbility) {
                         abilitiesToRemove.add(ability);
                     }
@@ -416,7 +413,7 @@ class GainHasteEffect extends ContinuousEffectImpl {
             }
             return true;
         }
-        if (game.getState().getZoneChangeCounter(((FixedTarget) getTargetPointer()).getTarget()) 
+        if (game.getState().getZoneChangeCounter(((FixedTarget) getTargetPointer()).getTarget())
                 >= ((FixedTarget) getTargetPointer()).getZoneChangeCounter()) {
             this.discard();
         }
@@ -428,8 +425,8 @@ class GainHasteEffect extends ContinuousEffectImpl {
 class SuspendBeginningOfUpkeepInterveningIfTriggeredAbility extends ConditionalInterveningIfTriggeredAbility {
 
     public SuspendBeginningOfUpkeepInterveningIfTriggeredAbility() {
-        super(new BeginningOfUpkeepTriggeredAbility(Zone.EXILED, new RemoveCounterSourceEffect(CounterType.TIME.createInstance()), 
-                TargetController.YOU, false),
+        super(new BeginningOfUpkeepTriggeredAbility(Zone.EXILED, new RemoveCounterSourceEffect(CounterType.TIME.createInstance()),
+                        TargetController.YOU, false),
                 SuspendedCondition.instance,
                 "At the beginning of your upkeep, if this card ({this}) is suspended, remove a time counter from it.");
         this.setRuleVisible(false);
diff --git a/Mage/src/main/java/mage/cards/SplitCard.java b/Mage/src/main/java/mage/cards/SplitCard.java
index 21c58c6c13..6845daa040 100644
--- a/Mage/src/main/java/mage/cards/SplitCard.java
+++ b/Mage/src/main/java/mage/cards/SplitCard.java
@@ -1,8 +1,5 @@
 package mage.cards;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
 import mage.MageObject;
 import mage.abilities.Abilities;
 import mage.abilities.AbilitiesImpl;
@@ -13,6 +10,10 @@ import mage.constants.SpellAbilityType;
 import mage.constants.Zone;
 import mage.game.Game;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
 /**
  * @author LevelX2
  */
@@ -135,11 +136,13 @@ public abstract class SplitCard extends CardImpl {
     public Abilities<Ability> getAbilities(Game game) {
         Abilities<Ability> allAbilites = new AbilitiesImpl<>();
         for (Ability ability : super.getAbilities(game)) {
+            // ignore split abilities TODO: why it here, for GUI's cleanup in card texts? Maybe it can be removed
             if (ability instanceof SpellAbility
-                    && ((SpellAbility) ability).getSpellAbilityType() != SpellAbilityType.SPLIT
-                    && ((SpellAbility) ability).getSpellAbilityType() != SpellAbilityType.SPLIT_AFTERMATH) {
-                allAbilites.add(ability);
+                    && (((SpellAbility) ability).getSpellAbilityType() == SpellAbilityType.SPLIT
+                    || ((SpellAbility) ability).getSpellAbilityType() == SpellAbilityType.SPLIT_AFTERMATH)) {
+                continue;
             }
+            allAbilites.add(ability);
         }
         allAbilites.addAll(leftHalfCard.getAbilities(game));
         allAbilites.addAll(rightHalfCard.getAbilities(game));

From 84daa0f8200f446ada05d88dc4c678c0d4b487cb Mon Sep 17 00:00:00 2001
From: johnm <johnm@WINDOWS-QR5QIIL.lan>
Date: Mon, 1 Jun 2020 10:29:10 +0100
Subject: [PATCH 079/586] return false when no choice made for archon ability

---
 Mage.Sets/src/mage/cards/a/ArchonOfValorsReach.java | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/Mage.Sets/src/mage/cards/a/ArchonOfValorsReach.java b/Mage.Sets/src/mage/cards/a/ArchonOfValorsReach.java
index fd4d1baa2a..782302bb39 100644
--- a/Mage.Sets/src/mage/cards/a/ArchonOfValorsReach.java
+++ b/Mage.Sets/src/mage/cards/a/ArchonOfValorsReach.java
@@ -169,6 +169,11 @@ class ArchonOfValorsReachReplacementEffect extends ContinuousRuleModifyingEffect
 
     @Override
     public boolean applies(GameEvent event, Ability source, Game game) {
+        
+        if ((String) game.getState().getValue(source.getSourceId().toString() + "_cardtype") == null){
+            return false;
+        }
+        
         CardType cardType = ArchonOfValorsReachChoice.getType((String) game.getState().getValue(source.getSourceId().toString() + "_cardtype"));
         // spell is not on the stack yet, so we have to check the card
         Card card = game.getCard(event.getSourceId());

From c40ef8b39ffb44e088d2080db8711f9dc9d57346 Mon Sep 17 00:00:00 2001
From: emerald000 <emerald000@hotmail.com>
Date: Mon, 1 Jun 2020 13:32:44 -0400
Subject: [PATCH 080/586] New Companion rule

"Once per game, any time you could cast a sorcery (during your main phase when the stack is empty), you can pay 3 generic mana to put your companion from your sideboard into your hand. This is a special action, not an activated ability."
---
 .../effects/keyword/CompanionEffect.java      | 27 +++++++++----------
 .../abilities/keyword/CompanionAbility.java   | 11 +++++---
 2 files changed, 21 insertions(+), 17 deletions(-)

diff --git a/Mage/src/main/java/mage/abilities/effects/keyword/CompanionEffect.java b/Mage/src/main/java/mage/abilities/effects/keyword/CompanionEffect.java
index 3b5421483e..a6c855e2d6 100644
--- a/Mage/src/main/java/mage/abilities/effects/keyword/CompanionEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/keyword/CompanionEffect.java
@@ -1,22 +1,21 @@
 package mage.abilities.effects.keyword;
 
 import mage.abilities.Ability;
-import mage.abilities.effects.AsThoughEffectImpl;
-import mage.constants.AsThoughEffectType;
-import mage.constants.Duration;
+import mage.abilities.effects.OneShotEffect;
+import mage.cards.Card;
 import mage.constants.Outcome;
+import mage.constants.Zone;
 import mage.game.Game;
-
-import java.util.UUID;
+import mage.players.Player;
 
 /*
  * @author emerald000
  */
-public class CompanionEffect extends AsThoughEffectImpl {
+public class CompanionEffect extends OneShotEffect {
 
     public CompanionEffect() {
-        super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit);
-        staticText = "Once during the game, you may cast your chosen companion from your sideboard";
+        super(Outcome.DrawCard);
+        staticText = "put {this} into your hand";
     }
 
     private CompanionEffect(final CompanionEffect effect) {
@@ -28,13 +27,13 @@ public class CompanionEffect extends AsThoughEffectImpl {
         return new CompanionEffect(this);
     }
 
-    @Override
-    public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
-        return objectId.equals(source.getSourceId()) && affectedControllerId.equals(source.getControllerId());
-    }
-
     @Override
     public boolean apply(Game game, Ability source) {
-        return true;
+        Player controller = game.getPlayer(source.getControllerId());
+        Card card = (Card) source.getSourceObjectIfItStillExists(game);
+        if (controller != null && card != null && game.getState().getZone(card.getId()) == Zone.OUTSIDE) {
+            return controller.moveCards(card, Zone.HAND, source, game);
+        }
+        return false;
     }
 }
diff --git a/Mage/src/main/java/mage/abilities/keyword/CompanionAbility.java b/Mage/src/main/java/mage/abilities/keyword/CompanionAbility.java
index cbf32664aa..bffb8ea944 100644
--- a/Mage/src/main/java/mage/abilities/keyword/CompanionAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/CompanionAbility.java
@@ -1,8 +1,10 @@
 package mage.abilities.keyword;
 
-import mage.abilities.StaticAbility;
+import mage.abilities.SpecialAction;
+import mage.abilities.costs.mana.GenericManaCost;
 import mage.abilities.effects.keyword.CompanionEffect;
 import mage.cards.Card;
+import mage.constants.TimingRule;
 import mage.constants.Zone;
 
 import java.util.Set;
@@ -10,13 +12,16 @@ import java.util.Set;
 /*
  * @author emerald000
  */
-public class CompanionAbility extends StaticAbility {
+public class CompanionAbility extends SpecialAction {
 
     private final CompanionCondition condition;
 
     public CompanionAbility(CompanionCondition condition) {
-        super(Zone.OUTSIDE, new CompanionEffect());
+        super(Zone.OUTSIDE);
         this.condition = condition;
+        this.addCost(new GenericManaCost(3));
+        this.addEffect(new CompanionEffect());
+        this.setTiming(TimingRule.SORCERY);
     }
 
     private CompanionAbility(final CompanionAbility ability) {

From aed44cd0cc38f6cac2c54acb0bafea9a306e8096 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 2 Jun 2020 11:11:28 -0400
Subject: [PATCH 081/586] updated standard ban list

---
 .../Mage.Deck.Constructed/src/mage/deck/Standard.java           | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Standard.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Standard.java
index 86cc46d2b2..8316e2de02 100644
--- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Standard.java
+++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Standard.java
@@ -17,7 +17,9 @@ public class Standard extends Constructed {
 
         setCodes.addAll(makeLegalSets());
 
+        banned.add("Agent of Treachery");
         banned.add("Field of the Dead");
+        banned.add("Fires of Invention");
         banned.add("Oko, Thief of Crowns");
         banned.add("Once Upon a Time");
         banned.add("Veil of Summer");

From 47af865bc315ace0bc09ca2ce191552aebac57f8 Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Tue, 2 Jun 2020 10:43:37 -0500
Subject: [PATCH 082/586] - Little fix Cradle of Vitality.  (null check)

---
 Mage.Sets/src/mage/cards/c/CradleOfVitality.java | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/Mage.Sets/src/mage/cards/c/CradleOfVitality.java b/Mage.Sets/src/mage/cards/c/CradleOfVitality.java
index 59d9d895ce..4640c6695a 100644
--- a/Mage.Sets/src/mage/cards/c/CradleOfVitality.java
+++ b/Mage.Sets/src/mage/cards/c/CradleOfVitality.java
@@ -61,7 +61,10 @@ class CradleOfVitalityEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Permanent permanent = game.getPermanent(source.getFirstTarget());
-        int lifeGained = (Integer) this.getValue("gainedLife");
+        int lifeGained = 0;
+        if (this.getValue("gainedLife") != null) {
+            lifeGained = (Integer) this.getValue("gainedLife");
+        }
         return permanent != null && lifeGained > 0
                 && permanent.addCounters(CounterType.P1P1.createInstance(lifeGained), source, game);
     }

From b94344341bf47968fcfa2018aa418547da25917e Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Wed, 3 Jun 2020 12:44:58 +0400
Subject: [PATCH 083/586] Improved playable abilities and split cards: * Now
 human player uses same code for playable abilities search as test framework
 (old version used different code, so it could not work in one of the modes);
 * Split cards - improves playable highlights; * Split cards - fixed that it
 doesn't work with dynamic added abilities like flashback (#6327, #6470,
 #6549);

---
 .../src/mage/player/human/HumanPlayer.java    |  11 +-
 .../cards/abilities/keywords/MorphTest.java   |  93 ++++++
 .../test/cards/mana/DoublingCubeTest.java     |   3 +-
 .../CastSplitCardsWithAsThoughManaTest.java   | 108 +++++++
 .../CastSplitCardsWithFlashbackTest.java      |  70 +++++
 .../java/org/mage/test/player/TestPlayer.java |   4 +-
 Mage/src/main/java/mage/cards/SplitCard.java  |   8 +-
 .../main/java/mage/players/PlayerImpl.java    | 288 +++++++-----------
 8 files changed, 399 insertions(+), 186 deletions(-)
 create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithAsThoughManaTest.java
 create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithFlashbackTest.java

diff --git a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java
index 18ca9e30a4..7455d09336 100644
--- a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java
+++ b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java
@@ -1047,8 +1047,9 @@ public class HumanPlayer extends PlayerImpl {
                     Zone zone = game.getState().getZone(object.getId());
                     if (zone != null) {
                         // look at card or try to cast/activate abilities
+                        LinkedHashMap<UUID, ActivatedAbility> useableAbilities = new LinkedHashMap<>();
+
                         Player actingPlayer = null;
-                        LinkedHashMap<UUID, ActivatedAbility> useableAbilities = null;
                         if (playerId.equals(game.getPriorityPlayerId())) {
                             actingPlayer = this;
                         } else if (getPlayersUnderYourControl().contains(game.getPriorityPlayerId())) {
@@ -1060,11 +1061,10 @@ public class HumanPlayer extends PlayerImpl {
 
                         if (object instanceof Card
                                 && ((Card) object).isFaceDown(game)
-                                && lookAtFaceDownCard((Card) object, game, useableAbilities == null ? 0 : useableAbilities.size())) {
+                                && lookAtFaceDownCard((Card) object, game, useableAbilities.size())) {
                             result = true;
                         } else {
-                            if (useableAbilities != null
-                                    && !useableAbilities.isEmpty()) {
+                            if (!useableAbilities.isEmpty()) {
                                 activateAbility(useableAbilities, object, game);
                                 result = true;
                             }
@@ -1347,8 +1347,7 @@ public class HumanPlayer extends PlayerImpl {
         Zone zone = game.getState().getZone(object.getId());
         if (zone != null) {
             LinkedHashMap<UUID, ActivatedManaAbilityImpl> useableAbilities = getUseableManaAbilities(object, zone, game);
-            if (useableAbilities != null
-                    && !useableAbilities.isEmpty()) {
+            if (!useableAbilities.isEmpty()) {
                 useableAbilities = ManaUtil.tryToAutoPay(unpaid, useableAbilities); // eliminates other abilities if one fits perfectly
                 currentlyUnpaidMana = unpaid;
                 activateAbility(useableAbilities, object, game);
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MorphTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MorphTest.java
index dacc8d11e2..253339652e 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MorphTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MorphTest.java
@@ -914,4 +914,97 @@ public class MorphTest extends CardTestPlayerBase {
         Permanent akroma = getPermanent("Akroma, Angel of Fury");
         Assert.assertTrue("Akroma has to be red", akroma.getColor(currentGame).isRed());
     }
+
+    @Test
+    public void test_LandWithMorph_PlayLand() {
+        // Morph {2}
+        addCard(Zone.HAND, playerA, "Zoetic Cavern");
+
+        checkPlayableAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Play Zoetic Cavern", true);
+        playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Zoetic Cavern");
+        setChoice(playerA, "No"); // no morph (canPay for generic/colored mana returns true all the time, so xmage ask about face down cast)
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        // 1 action must be here ("no" option is restores on failed morph call in playLand)
+        //assertAllCommandsUsed();
+        assertChoicesCount(playerA, 1);
+
+        assertPermanentCount(playerA, "Zoetic Cavern", 1);
+    }
+
+    @Test
+    public void test_LandWithMorph_Morph() {
+        // Morph {2}
+        addCard(Zone.HAND, playerA, "Zoetic Cavern");
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
+
+        checkPlayableAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Play Zoetic Cavern", true);
+        checkPlayableAbility("morph must be replaced by play ability", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Morph", false);
+        playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Zoetic Cavern");
+        setChoice(playerA, "Yes"); // morph
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerA, "Zoetic Cavern", 0);
+        assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
+    }
+
+    @Test
+    public void test_LandWithMorph_MorphAfterLand() {
+        removeAllCardsFromHand(playerA);
+
+        // Morph {2}
+        addCard(Zone.HAND, playerA, "Zoetic Cavern");
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
+        //
+        addCard(Zone.HAND, playerA, "Island", 1);
+
+        // play land first
+        playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Island");
+
+        // morph ability (play as face down) calls from playLand method, so it visible for play land command
+        checkPlayableAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Play Zoetic Cavern", true);
+        checkPlayableAbility("morph must be replaced by play ability", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Morph", false);
+        playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Zoetic Cavern");
+        setChoice(playerA, "Yes"); // morph
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerA, "Island", 1);
+        assertPermanentCount(playerA, "Zoetic Cavern", 0);
+        assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
+    }
+
+    @Test
+    public void test_LandWithMorph_MorphFromLibrary() {
+        removeAllCardsFromLibrary(playerA);
+
+        // You may play lands and cast spells from the top of your library.
+        addCard(Zone.BATTLEFIELD, playerA, "Future Sight");
+        //
+        // Morph {2}
+        addCard(Zone.LIBRARY, playerA, "Zoetic Cavern");
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
+
+        checkPlayableAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Play Zoetic Cavern", true);
+        checkPlayableAbility("morph must be replaced by play ability", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Morph", false);
+        playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Zoetic Cavern");
+        setChoice(playerA, "Yes"); // morph
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerA, "Zoetic Cavern", 0);
+        assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
+    }
 }
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/mana/DoublingCubeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/mana/DoublingCubeTest.java
index 7e2bb98b15..69744bf943 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/mana/DoublingCubeTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/mana/DoublingCubeTest.java
@@ -31,9 +31,10 @@ public class DoublingCubeTest extends CardTestPlayerBase {
 
         activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}, {T}:");
 
+        setStrictChooseMode(true);
         setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
         execute();
         assertManaPool(playerA, ManaType.COLORLESS, 4);
-
+        assertAllCommandsUsed();
     }
 }
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithAsThoughManaTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithAsThoughManaTest.java
new file mode 100644
index 0000000000..4330d9dd40
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithAsThoughManaTest.java
@@ -0,0 +1,108 @@
+package org.mage.test.cards.split;
+
+import mage.constants.PhaseStep;
+import mage.constants.Zone;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestPlayerBase;
+
+/**
+ * @author JayDi85
+ */
+public class CastSplitCardsWithAsThoughManaTest extends CardTestPlayerBase {
+
+    @Test
+    public void test_AsThoughMana_Simple() {
+        // {1}{R}
+        // When Dire Fleet Daredevil enters the battlefield, exile target instant or sorcery card from an opponent’s graveyard.
+        // You may cast that card this turn, and you may spend mana as though it were mana of any type to cast that spell.
+        // If that card would be put into a graveyard this turn, exile it instead.
+        addCard(Zone.HAND, playerA, "Dire Fleet Daredevil", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
+        //
+        addCard(Zone.GRAVEYARD, playerB, "Lightning Bolt", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 1);
+
+        // cast fleet
+        activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}", 2);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dire Fleet Daredevil");
+        addTarget(playerA, "Lightning Bolt");
+
+        // cast bolt with blue mana
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB);
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        assertLife(playerB, 20 - 3);
+    }
+
+    @Test
+    public void test_AsThoughMana_Split_WearTear() {
+        // {1}{R}
+        // When Dire Fleet Daredevil enters the battlefield, exile target instant or sorcery card from an opponent’s graveyard.
+        // You may cast that card this turn, and you may spend mana as though it were mana of any type to cast that spell.
+        // If that card would be put into a graveyard this turn, exile it instead.
+        addCard(Zone.HAND, playerA, "Dire Fleet Daredevil", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
+        //
+        // Wear {1}{R} Destroy target artifact.
+        // Tear {W} Destroy target enchantment.
+        addCard(Zone.GRAVEYARD, playerB, "Wear // Tear", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
+        addCard(Zone.BATTLEFIELD, playerB, "Bident of Thassa", 1); // Legendary Enchantment Artifact
+        addCard(Zone.BATTLEFIELD, playerB, "Bow of Nylea", 1); // Legendary Enchantment Artifact
+
+        // cast fleet
+        activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}", 2);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dire Fleet Daredevil");
+        addTarget(playerA, "Wear // Tear");
+
+        // cast Wear with black mana
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wear", "Bident of Thassa");
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        assertGraveyardCount(playerB, "Bident of Thassa", 1);
+        assertPermanentCount(playerB, "Bow of Nylea", 1);
+    }
+
+    @Test
+    public void test_AsThoughMana_Split_CatchRelease() {
+        // {1}{R}
+        // When Dire Fleet Daredevil enters the battlefield, exile target instant or sorcery card from an opponent’s graveyard.
+        // You may cast that card this turn, and you may spend mana as though it were mana of any type to cast that spell.
+        // If that card would be put into a graveyard this turn, exile it instead.
+        addCard(Zone.HAND, playerA, "Dire Fleet Daredevil", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
+        //
+        // Catch {1}{U}{R} Gain control of target permanent until end of turn. Untap it. It gains haste until end of turn.
+        // Release {4}{R}{W} Each player sacrifices an artifact, a creature, an enchantment, a land, and a planeswalker.
+        addCard(Zone.GRAVEYARD, playerB, "Catch // Release", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
+        addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears", 1);
+
+        // cast fleet
+        activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}", 2);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dire Fleet Daredevil");
+        addTarget(playerA, "Catch // Release");
+
+        // cast Catch with black mana
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Catch", "Balduvian Bears");
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerA, "Balduvian Bears", 1);
+        assertPermanentCount(playerB, "Balduvian Bears", 0);
+    }
+}
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithFlashbackTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithFlashbackTest.java
new file mode 100644
index 0000000000..d81189f6e0
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithFlashbackTest.java
@@ -0,0 +1,70 @@
+package org.mage.test.cards.split;
+
+import mage.constants.PhaseStep;
+import mage.constants.Zone;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestPlayerBase;
+
+/**
+ * @author JayDi85
+ */
+public class CastSplitCardsWithFlashbackTest extends CardTestPlayerBase {
+
+    @Test
+    public void test_Flashback_Simple() {
+        // {1}{U}
+        // When Snapcaster Mage enters the battlefield, target instant or sorcery card in your graveyard gains flashback until end of turn.
+        addCard(Zone.HAND, playerA, "Snapcaster Mage", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
+        //
+        addCard(Zone.GRAVEYARD, playerA, "Lightning Bolt", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
+
+        // add flashback
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Snapcaster Mage");
+        addTarget(playerA, "Lightning Bolt");
+
+        // cast as flashback
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Flashback", playerB);
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        assertLife(playerB, 20 - 3);
+    }
+
+    @Test
+    public void test_Flashback_Split() {
+        // {1}{U}
+        // When Snapcaster Mage enters the battlefield, target instant or sorcery card in your graveyard gains flashback until end of turn.
+        addCard(Zone.HAND, playerA, "Snapcaster Mage", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
+        //
+        // Wear {1}{R} Destroy target artifact.
+        // Tear {W} Destroy target enchantment.
+        addCard(Zone.GRAVEYARD, playerA, "Wear // Tear", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
+        addCard(Zone.BATTLEFIELD, playerB, "Bident of Thassa", 1); // Legendary Enchantment Artifact
+        addCard(Zone.BATTLEFIELD, playerB, "Bow of Nylea", 1); // Legendary Enchantment Artifact
+
+        // add flashback
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Snapcaster Mage");
+        addTarget(playerA, "Wear // Tear");
+
+        // cast as flashback
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Flashback {1}{R}", "Bident of Thassa");
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        assertGraveyardCount(playerB, "Bident of Thassa", 1);
+        assertPermanentCount(playerB, "Bow of Nylea", 1);
+    }
+}
diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
index fdb665dd5e..b0c2615874 100644
--- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
+++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
@@ -963,7 +963,7 @@ public class TestPlayer implements Player {
                 // you don't need to use stack command all the time, so some cast commands can be skiped to next check
                 if (game.getStack().isEmpty()) {
                     this.chooseStrictModeFailed("cast/activate", game,
-                            "Can't find available command - " + action.getAction() + " (use checkPlayableAbility for non castable checks)", true);
+                            "Can't find available command - " + action.getAction() + " (use checkPlayableAbility for \"non available\" checks)", true);
                 }
             } // turn/step
         }
@@ -1088,7 +1088,7 @@ public class TestPlayer implements Player {
                 .map(a -> (a.getZone() + " -> "
                         + a.getSourceObject(game).getIdName() + " -> "
                         + (a.toString().length() > 0
-                        ? a.toString().substring(0, Math.min(20, a.toString().length()) - 1)
+                        ? a.toString().substring(0, Math.min(20, a.toString().length()))
                         : a.getClass().getSimpleName())
                         + "..."))
                 .sorted()
diff --git a/Mage/src/main/java/mage/cards/SplitCard.java b/Mage/src/main/java/mage/cards/SplitCard.java
index 6845daa040..4205f05158 100644
--- a/Mage/src/main/java/mage/cards/SplitCard.java
+++ b/Mage/src/main/java/mage/cards/SplitCard.java
@@ -110,11 +110,13 @@ public abstract class SplitCard extends CardImpl {
     public Abilities<Ability> getAbilities() {
         Abilities<Ability> allAbilites = new AbilitiesImpl<>();
         for (Ability ability : super.getAbilities()) {
+            // ignore split abilities TODO: why it here, for GUI's cleanup in card texts? Maybe it can be removed
             if (ability instanceof SpellAbility
-                    && ((SpellAbility) ability).getSpellAbilityType() != SpellAbilityType.SPLIT
-                    && ((SpellAbility) ability).getSpellAbilityType() != SpellAbilityType.SPLIT_AFTERMATH) {
-                allAbilites.add(ability);
+                    && (((SpellAbility) ability).getSpellAbilityType() == SpellAbilityType.SPLIT
+                    || ((SpellAbility) ability).getSpellAbilityType() == SpellAbilityType.SPLIT_AFTERMATH)) {
+                continue;
             }
+            allAbilites.add(ability);
         }
         allAbilites.addAll(leftHalfCard.getAbilities());
         allAbilites.addAll(rightHalfCard.getAbilities());
diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java
index 5c8dd00ea5..9fa114ba30 100644
--- a/Mage/src/main/java/mage/players/PlayerImpl.java
+++ b/Mage/src/main/java/mage/players/PlayerImpl.java
@@ -246,7 +246,6 @@ public abstract class PlayerImpl implements Player, Serializable {
         this.storedBookmark = player.storedBookmark;
 
         this.topCardRevealed = player.topCardRevealed;
-        this.playersUnderYourControl.clear();
         this.playersUnderYourControl.addAll(player.playersUnderYourControl);
         this.usersAllowedToSeeHandCards.addAll(player.usersAllowedToSeeHandCards);
 
@@ -254,7 +253,6 @@ public abstract class PlayerImpl implements Player, Serializable {
         this.isGameUnderControl = player.isGameUnderControl;
 
         this.turnController = player.turnController;
-        this.turnControllers.clear();
         this.turnControllers.addAll(player.turnControllers);
 
         this.passed = player.passed;
@@ -1190,18 +1188,20 @@ public abstract class PlayerImpl implements Player, Serializable {
             return false;
         }
         ActivatedAbility playLandAbility = null;
-        boolean found = false;
+        boolean foundAlternative = false;
         for (Ability ability : card.getAbilities()) {
             // if cast for noMana no Alternative costs are allowed
             if ((ability instanceof AlternativeSourceCosts)
                     || (ability instanceof OptionalAdditionalSourceCosts)) {
-                found = true;
+                foundAlternative = true;
             }
             if (ability instanceof PlayLandAbility) {
                 playLandAbility = (ActivatedAbility) ability;
             }
         }
-        if (found) {
+
+        // try alternative cast (face down)
+        if (foundAlternative) {
             SpellAbility spellAbility = new SpellAbility(null, "",
                     game.getState().getZone(card.getId()), SpellAbilityType.FACE_DOWN_CREATURE);
             spellAbility.setControllerId(this.getId());
@@ -1210,9 +1210,11 @@ public abstract class PlayerImpl implements Player, Serializable {
                 return true;
             }
         }
+
         if (playLandAbility == null) {
             return false;
         }
+
         //20091005 - 114.2a
         ActivationStatus activationStatus = playLandAbility.canActivate(this.playerId, game);
         if (ignoreTiming) {
@@ -1496,110 +1498,29 @@ public abstract class PlayerImpl implements Player, Serializable {
         return useable;
     }
 
-    // Get the usable activated abilities for a *single card object*, that is, either a card or half of a split card.
-    // Also called on the whole split card but only passing the fuse ability and other whole-split-card shared abilities
-    // as candidates.
-    private void getUseableActivatedAbilitiesHalfImpl(MageObject object, Zone zone, Game game, Abilities<Ability> candidateAbilites,
-                                                      LinkedHashMap<UUID, ActivatedAbility> output) {
-        boolean canUse = !(object instanceof Permanent) || ((Permanent) object).canUseActivatedAbilities(game);
-        ManaOptions availableMana = null;
-        //        ManaOptions availableMana = getManaAvailable(game); // can only be activated if mana calculation works flawless otherwise player can't play spells they could play if calculation would work correctly
-        //        availableMana.addMana(manaPool.getMana());
-        for (Ability ability : candidateAbilites) {
-            if (canUse || ability.getAbilityType() == AbilityType.SPECIAL_ACTION) {
-                if (ability.getZone().match(zone)) {
-                    if (ability instanceof ActivatedAbility) {
-                        if (ability instanceof ActivatedManaAbilityImpl) {
-                            if (((ActivatedAbility) ability).canActivate(playerId, game).canActivate()) {
-                                output.put(ability.getId(), (ActivatedAbility) ability);
-                            }
-                        } else if (canPlay(((ActivatedAbility) ability), availableMana, object, game)) {
-                            output.put(ability.getId(), (ActivatedAbility) ability);
-                        }
-                    } else if (ability instanceof AlternativeSourceCosts) {
-                        if (object.isLand()) {
-                            for (Ability ability2 : object.getAbilities().copy()) {
-                                if (ability2 instanceof PlayLandAbility) {
-                                    output.put(ability2.getId(), (ActivatedAbility) ability2);
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        }
-        if (zone != Zone.HAND) {
-            if (Zone.GRAVEYARD == zone && canPlayCardsFromGraveyard()) {
-                for (ActivatedAbility ability : candidateAbilites.getPlayableAbilities(Zone.HAND)) {
-                    if (canUse
-                            || ability.getAbilityType() == AbilityType.SPECIAL_ACTION) {
-                        if (ability.getZone().equals(zone) || ability.getZone().equals(Zone.HAND)) {
-                            if (ability.canActivate(playerId, game).canActivate()) {
-                                output.put(ability.getId(), ability);
-                            }
-                        }
-                    }
-                }
-            }
-            if (zone != Zone.BATTLEFIELD) {
-                for (Ability ability : candidateAbilites) {
-                    if (ability.getZone().equals(zone) || ability.getZone().equals(Zone.HAND)) {
-                        if (game.getContinuousEffects().asThough(object.getId(),
-                                AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE,
-                                null,
-                                this.getId(),
-                                game)
-                                != null
-                                // if anyone sees an issue with this code, please report it.  Worked in my testing. !
-                                || game.getContinuousEffects().asThough(object.getId(),
-                                AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE,
-                                ability,
-                                this.getId(),
-                                game)
-                                != null) {
-                            if (canUse
-                                    || ability.getAbilityType() == AbilityType.SPECIAL_ACTION) {
-                                ability.setControllerId(this.getId());
-                                if (ability instanceof ActivatedAbility
-                                        && ability.getZone().match(Zone.HAND)
-                                        && ((ActivatedAbility) ability).canActivate(playerId, game).canActivate()) {
-                                    output.put(ability.getId(), (ActivatedAbility) ability);
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-
     @Override
     public LinkedHashMap<UUID, ActivatedAbility> getUseableActivatedAbilities(MageObject object, Zone zone, Game game) {
-        // TODO: replace with getPlayableFromNonHandCardAll (uses for all tests)
+        LinkedHashMap<UUID, ActivatedAbility> useable = new LinkedHashMap<>();
         boolean previousState = game.inCheckPlayableState();
         game.setCheckPlayableState(true);
-        LinkedHashMap<UUID, ActivatedAbility> useable = new LinkedHashMap<>();
-        if (object instanceof StackAbility) { // It may not be possible to activate abilities of stack abilities
-            return useable;
-        }
-        if (object instanceof SplitCard) { // TODO: use of getAbilities(game)
-            SplitCard splitCard = (SplitCard) object;
-            getUseableActivatedAbilitiesHalfImpl(splitCard.getLeftHalfCard(),
-                    zone, game, splitCard.getLeftHalfCard().getAbilities(game), useable);
-            getUseableActivatedAbilitiesHalfImpl(splitCard.getRightHalfCard(),
-                    zone, game, splitCard.getRightHalfCard().getAbilities(game), useable);
-            getUseableActivatedAbilitiesHalfImpl(splitCard,
-                    zone, game, splitCard.getSharedAbilities(game), useable);
-        } else if (object instanceof Card) {
-            getUseableActivatedAbilitiesHalfImpl(object,
-                    zone, game, ((Card) object).getAbilities(game), useable);
-        } else if (object != null) {
-            getUseableActivatedAbilitiesHalfImpl(object,
-                    zone, game, object.getAbilities(), useable);
-            getOtherUseableActivatedAbilities(object, zone, game, useable);
-        }
+        try {
+            // It may not be possible to activate abilities of stack abilities
+            if (object instanceof StackAbility) {
+                return useable;
+            }
 
-        game.setCheckPlayableState(previousState);
+            // collect and filter playable activated abilities
+            List<Ability> allPlayable = getPlayable(game, true, zone, false);
+            for (Ability ability : allPlayable) {
+                if (ability instanceof ActivatedAbility) {
+                    if (object.hasAbility(ability, game)) {
+                        useable.putIfAbsent(ability.getId(), (ActivatedAbility) ability);
+                    }
+                }
+            }
+        } finally {
+            game.setCheckPlayableState(previousState);
+        }
         return useable;
     }
 
@@ -3184,6 +3105,51 @@ public abstract class PlayerImpl implements Player, Serializable {
         return false;
     }
 
+    protected ActivatedAbility findActivatedAbilityFromPlayable(Card card, ManaOptions manaAvailable, Ability ability, Game game) {
+        // replace alternative abilities by real play abilities (e.g. morph/facedown static ability by play land)
+
+        if (ability instanceof ActivatedManaAbilityImpl) {
+            // mana ability
+            if (((ActivatedManaAbilityImpl) ability).canActivate(this.getId(), game).canActivate()) {
+                return (ActivatedManaAbilityImpl) ability;
+            }
+        } else if (ability instanceof AlternativeSourceCosts) {
+            // alternative cost must be replaced by real play ability
+            return findActivatedAbilityFromAlternativeSourceCost(card, manaAvailable, ability, game);
+        } else if (ability instanceof ActivatedAbility) {
+            // activated ability
+            if (canPlay((ActivatedAbility) ability, manaAvailable, card, game)) {
+                return (ActivatedAbility) ability;
+            }
+        }
+
+        // non playable abilities like static
+        return null;
+    }
+
+    protected ActivatedAbility findActivatedAbilityFromAlternativeSourceCost(Card card, ManaOptions manaAvailable, Ability ability, Game game) {
+        // alternative cost must be replaced by real play ability
+        if (ability instanceof AlternativeSourceCosts) {
+            AlternativeSourceCosts altAbility = (AlternativeSourceCosts) ability;
+            if (card.isLand()) {
+                // land
+                // morph ability is static, so it must be replaced with play land ability (playLand search and try to use face down first)
+                if (canLandPlayAlternateSourceCostsAbility(card, manaAvailable, ability, game)) { // e.g. Land with Morph
+                    Optional<Ability> landAbility = card.getAbilities(game).stream().filter(a -> a instanceof PlayLandAbility).findFirst();
+                    if (landAbility.isPresent()) {
+                        return (ActivatedAbility) landAbility.get();
+                    }
+                }
+            } else {
+                // creature and other
+                if (altAbility.isAvailable(card.getSpellAbility(), game)) {
+                    return card.getSpellAbility();
+                }
+            }
+        }
+        return null;
+    }
+
     protected boolean canLandPlayAlternateSourceCostsAbility(Card sourceObject, ManaOptions available, Ability ability, Game game) {
         if (!(sourceObject instanceof Permanent)) {
             Ability sourceAbility = sourceObject.getAbilities().stream()
@@ -3216,27 +3182,7 @@ public abstract class PlayerImpl implements Player, Serializable {
         return false;
     }
 
-    private void getPlayableFromGraveyardCard(Game game, Card card, Abilities<Ability> candidateAbilities, ManaOptions availableMana, List<Ability> output) {
-        MageObjectReference permittingObject = game.getContinuousEffects().asThough(card.getId(),
-                AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, card.getSpellAbility(), this.getId(), game);
-        for (ActivatedAbility ability : candidateAbilities.getActivatedAbilities(Zone.ALL)) {
-            boolean possible = false;
-            if (ability.getZone().match(Zone.GRAVEYARD)) {
-                possible = true;
-            } else if (ability.getZone().match(Zone.HAND)
-                    && (ability instanceof SpellAbility
-                    || ability instanceof PlayLandAbility)) {
-                if (permittingObject != null || canPlayCardsFromGraveyard()) {
-                    possible = true;
-                }
-            }
-            if (possible && canPlay(ability, availableMana, card, game)) {
-                output.add(ability);
-            }
-        }
-    }
-
-    private void getPlayableFromNonHandCardAll(Game game, Zone fromZone, Card card, ManaOptions availableMana, List<Ability> output) {
+    private void getPlayableFromCardAll(Game game, Zone fromZone, Card card, ManaOptions availableMana, List<Ability> output) {
         if (fromZone == null) {
             return;
         }
@@ -3244,16 +3190,16 @@ public abstract class PlayerImpl implements Player, Serializable {
         // BASIC abilities
         if (card instanceof SplitCard) {
             SplitCard splitCard = (SplitCard) card;
-            getPlayableFromNonHandCardSingle(game, fromZone, splitCard.getLeftHalfCard(), splitCard.getLeftHalfCard().getAbilities(), availableMana, output);
-            getPlayableFromNonHandCardSingle(game, fromZone, splitCard.getRightHalfCard(), splitCard.getRightHalfCard().getAbilities(), availableMana, output);
-            getPlayableFromNonHandCardSingle(game, fromZone, splitCard, splitCard.getSharedAbilities(game), availableMana, output);
+            getPlayableFromCardSingle(game, fromZone, splitCard.getLeftHalfCard(), splitCard.getLeftHalfCard().getAbilities(), availableMana, output);
+            getPlayableFromCardSingle(game, fromZone, splitCard.getRightHalfCard(), splitCard.getRightHalfCard().getAbilities(), availableMana, output);
+            getPlayableFromCardSingle(game, fromZone, splitCard, splitCard.getSharedAbilities(game), availableMana, output);
         } else if (card instanceof AdventureCard) {
             // adventure must use different card characteristics for different spells (main or adventure)
             AdventureCard adventureCard = (AdventureCard) card;
-            getPlayableFromNonHandCardSingle(game, fromZone, adventureCard.getSpellCard(), adventureCard.getSpellCard().getAbilities(), availableMana, output);
-            getPlayableFromNonHandCardSingle(game, fromZone, adventureCard, adventureCard.getSharedAbilities(game), availableMana, output);
+            getPlayableFromCardSingle(game, fromZone, adventureCard.getSpellCard(), adventureCard.getSpellCard().getAbilities(), availableMana, output);
+            getPlayableFromCardSingle(game, fromZone, adventureCard, adventureCard.getSharedAbilities(game), availableMana, output);
         } else {
-            getPlayableFromNonHandCardSingle(game, fromZone, card, card.getAbilities(), availableMana, output);
+            getPlayableFromCardSingle(game, fromZone, card, card.getAbilities(game), availableMana, output);
         }
 
         // DYNAMIC ADDED abilities
@@ -3278,7 +3224,7 @@ public abstract class PlayerImpl implements Player, Serializable {
         }
     }
 
-    private void getPlayableFromNonHandCardSingle(Game game, Zone fromZone, Card card, Abilities<Ability> candidateAbilities, ManaOptions availableMana, List<Ability> output) {
+    private void getPlayableFromCardSingle(Game game, Zone fromZone, Card card, Abilities<Ability> candidateAbilities, ManaOptions availableMana, List<Ability> output) {
         // check "can play" condition as affected controller (BUT play from not own hand zone must be checked as original controller)
         for (ActivatedAbility ability : candidateAbilities.getActivatedAbilities(Zone.ALL)) {
             boolean isPlaySpell = (ability instanceof SpellAbility);
@@ -3339,8 +3285,13 @@ public abstract class PlayerImpl implements Player, Serializable {
                     possibleToPlay = true;
                 }
 
-                if (possibleToPlay && canPlay(ability, availableMana, card, game)) {
-                    output.add(ability);
+                if (!possibleToPlay) {
+                    continue;
+                }
+
+                ActivatedAbility playAbility = findActivatedAbilityFromPlayable(card, availableMana, ability, game);
+                if (playAbility != null && !output.contains(playAbility)) {
+                    output.add(playAbility);
                 }
             } finally {
                 ability.setControllerId(savedControllerId);
@@ -3355,8 +3306,13 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     public List<Ability> getPlayable(Game game, boolean hidden, Zone fromZone, boolean hideDuplicatedAbilities) {
         List<Ability> playable = new ArrayList<>();
+        if (shouldSkipGettingPlayable(game)) {
+            return playable;
+        }
+
+        boolean previousState = game.inCheckPlayableState();
         game.setCheckPlayableState(true);
-        if (!shouldSkipGettingPlayable(game)) {
+        try {
             ManaOptions availableMana = getManaAvailable(game);
             availableMana.addMana(manaPool.getMana());
             for (ConditionalMana conditionalMana : manaPool.getConditionalMana()) {
@@ -3390,26 +3346,9 @@ public abstract class PlayerImpl implements Player, Serializable {
                                 continue;
                             }
 
-                            // if have alternative cost
-                            if (ability instanceof ActivatedAbility) {
-                                // normal ability
-                                if (canPlay((ActivatedAbility) ability, availableMana, card, game)) {
-                                    playable.add(ability);
-                                }
-                            } else if (ability instanceof AlternativeSourceCosts) {
-                                if (card.isLand()) {
-                                    if (canLandPlayAlternateSourceCostsAbility(card, availableMana, ability, game)) { // e.g. Land with Morph
-                                        playable.add(ability);
-                                    }
-                                } else if (card.isCreature()) { // e.g. makes a card available for play by Morph if the card may not be cast normally
-                                    if (!playable.contains(card.getSpellAbility())) {
-                                        if (((AlternativeSourceCosts) ability).isAvailable(card.getSpellAbility(), game)) {
-                                            playable.add(card.getSpellAbility());
-                                        }
-                                    }
-                                }
-                            } else {
-                                // unknown type
+                            ActivatedAbility playAbility = findActivatedAbilityFromPlayable(card, availableMana, ability, game);
+                            if (playAbility != null && !playable.contains(playAbility)) {
+                                playable.add(playAbility);
                             }
                         }
                     }
@@ -3418,14 +3357,14 @@ public abstract class PlayerImpl implements Player, Serializable {
 
             if (fromAll || fromZone == Zone.GRAVEYARD) {
                 for (Card card : graveyard.getCards(game)) {
-                    getPlayableFromNonHandCardAll(game, Zone.GRAVEYARD, card, availableMana, playable);
+                    getPlayableFromCardAll(game, Zone.GRAVEYARD, card, availableMana, playable);
                 }
             }
 
             if (fromAll || fromZone == Zone.EXILED) {
                 for (ExileZone exile : game.getExile().getExileZones()) {
                     for (Card card : exile.getCards(game)) {
-                        getPlayableFromNonHandCardAll(game, Zone.EXILED, card, availableMana, playable);
+                        getPlayableFromCardAll(game, Zone.EXILED, card, availableMana, playable);
                     }
                 }
             }
@@ -3435,7 +3374,7 @@ public abstract class PlayerImpl implements Player, Serializable {
                 for (Cards revealedCards : game.getState().getRevealed().values()) {
                     for (Card card : revealedCards.getCards(game)) {
                         // revealed cards can be from any zones
-                        getPlayableFromNonHandCardAll(game, game.getState().getZone(card.getId()), card, availableMana, playable);
+                        getPlayableFromCardAll(game, game.getState().getZone(card.getId()), card, availableMana, playable);
                     }
                 }
             }
@@ -3444,7 +3383,7 @@ public abstract class PlayerImpl implements Player, Serializable {
             if (fromAll || fromZone == Zone.OUTSIDE) {
                 for (Cards companionCards : game.getState().getCompanion().values()) {
                     for (Card card : companionCards.getCards(game)) {
-                        getPlayableFromNonHandCardAll(game, Zone.OUTSIDE, card, availableMana, playable);
+                        getPlayableFromCardAll(game, Zone.OUTSIDE, card, availableMana, playable);
                     }
                 }
             }
@@ -3453,12 +3392,10 @@ public abstract class PlayerImpl implements Player, Serializable {
             if (fromAll || fromZone == Zone.LIBRARY) {
                 for (UUID playerInRangeId : game.getState().getPlayersInRange(getId(), game)) {
                     Player player = game.getPlayer(playerInRangeId);
-                    if (player != null) {
-                        if (/*player.isTopCardRevealed() &&*/player.getLibrary().hasCards()) {
-                            Card card = player.getLibrary().getFromTop(game);
-                            if (card != null) {
-                                getPlayableFromNonHandCardAll(game, Zone.LIBRARY, card, availableMana, playable);
-                            }
+                    if (player != null && player.getLibrary().hasCards()) {
+                        Card card = player.getLibrary().getFromTop(game);
+                        if (card != null) {
+                            getPlayableFromCardAll(game, Zone.LIBRARY, card, availableMana, playable);
                         }
                     }
                 }
@@ -3471,10 +3408,13 @@ public abstract class PlayerImpl implements Player, Serializable {
             // activated abilities from battlefield objects
             if (fromAll || fromZone == Zone.BATTLEFIELD) {
                 for (Permanent permanent : game.getBattlefield().getAllActivePermanents(playerId)) {
-                    LinkedHashMap<UUID, ActivatedAbility> useableAbilities = getUseableActivatedAbilities(permanent, Zone.BATTLEFIELD, game);
-                    for (ActivatedAbility ability : useableAbilities.values()) {
-                        activatedUnique.putIfAbsent(ability.toString(), ability);
-                        activatedAll.add(ability);
+                    List<Ability> battlePlayable = new ArrayList<>();
+                    getPlayableFromCardAll(game, Zone.BATTLEFIELD, permanent, availableMana, battlePlayable);
+                    for (Ability ability : battlePlayable) {
+                        if (ability instanceof ActivatedAbility) {
+                            activatedUnique.putIfAbsent(ability.toString(), ability);
+                            activatedAll.add(ability);
+                        }
                     }
                 }
             }
@@ -3483,8 +3423,7 @@ public abstract class PlayerImpl implements Player, Serializable {
             if (fromAll || fromZone == Zone.STACK) {
                 for (StackObject stackObject : game.getState().getStack()) {
                     for (ActivatedAbility ability : stackObject.getAbilities().getActivatedAbilities(Zone.STACK)) {
-                        if (ability != null
-                                && canPlay(ability, availableMana, game.getObject(ability.getSourceId()), game)) {
+                        if (canPlay(ability, availableMana, game.getObject(ability.getSourceId()), game)) {
                             activatedUnique.put(ability.toString(), ability);
                             activatedAll.add(ability);
                         }
@@ -3496,8 +3435,7 @@ public abstract class PlayerImpl implements Player, Serializable {
             if (fromAll || fromZone == Zone.COMMAND) {
                 for (CommandObject commandObject : game.getState().getCommand()) {
                     for (ActivatedAbility ability : commandObject.getAbilities().getActivatedAbilities(Zone.COMMAND)) {
-                        if (ability.isControlledBy(getId())
-                                && canPlay(ability, availableMana, game.getObject(ability.getSourceId()), game)) {
+                        if (canPlay(ability, availableMana, game.getObject(ability.getSourceId()), game)) {
                             activatedUnique.put(ability.toString(), ability);
                             activatedAll.add(ability);
                         }
@@ -3510,8 +3448,10 @@ public abstract class PlayerImpl implements Player, Serializable {
             } else {
                 playable.addAll(activatedAll);
             }
+        } finally {
+            game.setCheckPlayableState(previousState);
         }
-        game.setCheckPlayableState(false);
+
         return playable;
     }
 

From f65f4a4344b1f193de8d2ec3ae015976c3fc1216 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Wed, 3 Jun 2020 15:06:12 +0200
Subject: [PATCH 084/586] * Fixed that calling the destroy method for an object
 of the class permanent did move the card in game but shouldn't (#6571).

---
 Mage/src/main/java/mage/game/permanent/PermanentImpl.java | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java
index e093335625..003d33da82 100644
--- a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java
+++ b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java
@@ -1112,14 +1112,18 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
 
     @Override
     public boolean destroy(UUID sourceId, Game game, boolean noRegen) {
+        // Only permanets on the battlefield can be destroyed
+        if (!game.getState().getZone(getId()).equals(Zone.BATTLEFIELD)) {
+            return false;
+        }
         //20091005 - 701.6
         if (abilities.containsKey(IndestructibleAbility.getInstance().getId())) {
             return false;
         }
 
         if (!game.replaceEvent(GameEvent.getEvent(EventType.DESTROY_PERMANENT, objectId, sourceId, controllerId, noRegen ? 1 : 0))) {
-            // this means destroy was successful, if object movement to graveyard will be replaced (e.g. commander to command zone) its still
-            // is handled as successful destroying (but not as sucessful "dies this way" for destroying).
+            // this means destroy was successful, if object movement to graveyard will be replaced (e.g. commander to command zone) it's still
+            // handled as successful destroying (but not as sucessful "dies this way" for destroying).
             if (moveToZone(Zone.GRAVEYARD, sourceId, game, false)) {
                 if (!game.isSimulation()) {
                     String logName;

From 07386cce8d75effba60f322198c80ade16d5cfcb Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Wed, 3 Jun 2020 15:07:52 +0200
Subject: [PATCH 085/586] * Shifting Shadows - Fixed not proper handling of
 gained triggered abilities during step resolution of Shifting Shadows effect
 (fixes #6571).

---
 .../src/mage/cards/m/MairsilThePretender.java |   6 +-
 .../src/mage/cards/s/ShiftingShadow.java      |   9 +-
 Mage.Tests/CommanderDuel_Mairisil_UBR.dck     |  90 ++++++++
 .../duel/MairsilThePretenderTest.java         | 203 ++++++++++++++++++
 .../base/impl/CardTestPlayerAPIImpl.java      |   9 +
 .../CommanderReplacementEffect.java           |   6 +-
 6 files changed, 316 insertions(+), 7 deletions(-)
 create mode 100644 Mage.Tests/CommanderDuel_Mairisil_UBR.dck
 create mode 100644 Mage.Tests/src/test/java/org/mage/test/commander/duel/MairsilThePretenderTest.java

diff --git a/Mage.Sets/src/mage/cards/m/MairsilThePretender.java b/Mage.Sets/src/mage/cards/m/MairsilThePretender.java
index 3a85125af0..3188b78a03 100644
--- a/Mage.Sets/src/mage/cards/m/MairsilThePretender.java
+++ b/Mage.Sets/src/mage/cards/m/MairsilThePretender.java
@@ -46,10 +46,12 @@ public final class MairsilThePretender extends CardImpl {
         this.power = new MageInt(4);
         this.toughness = new MageInt(4);
 
-        // When Mairsil, the Pretender enters the battlefield, you may exile an artifact or creature card from your hand or graveyard and put a cage counter on it.
+        // When Mairsil, the Pretender enters the battlefield, you may exile an artifact or creature card from your hand
+        // or graveyard and put a cage counter on it.
         this.addAbility(new EntersBattlefieldTriggeredAbility(new MairsilThePretenderExileEffect(), true));
 
-        // Mairsil, the Pretender has all activated abilities of all cards you own in exile with cage counters on them. You may activate each of those abilities only once each turn.
+        // Mairsil, the Pretender has all activated abilities of all cards you own in exile with cage counters on them. 
+        // You may activate each of those abilities only once each turn.
         Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new MairsilThePretenderGainAbilitiesEffect());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/s/ShiftingShadow.java b/Mage.Sets/src/mage/cards/s/ShiftingShadow.java
index e181f37a9d..5f9f1e72ba 100644
--- a/Mage.Sets/src/mage/cards/s/ShiftingShadow.java
+++ b/Mage.Sets/src/mage/cards/s/ShiftingShadow.java
@@ -71,12 +71,12 @@ public final class ShiftingShadow extends CardImpl {
 
 class ShiftingShadowEffect extends OneShotEffect {
 
-    private UUID auraId;
+    private final UUID auraId;
 
     public ShiftingShadowEffect(UUID auraId) {
         super(Outcome.PutCreatureInPlay);
         this.staticText = "destroy this creature. Reveal cards from the top of your library until you reveal a creature card. "
-                + "Put that card onto the battlefield and attach Shifting Shadow to it, then put all other cards revealed this way on the bottom of your library in a random order";
+                + "Put that card onto the battlefield and attach {this} to it, then put all other cards revealed this way on the bottom of your library in a random order";
         this.auraId = auraId;
     }
 
@@ -107,6 +107,11 @@ class ShiftingShadowEffect extends OneShotEffect {
             }
             if (aura != null) {
                 enchanted.destroy(source.getSourceId(), game, false);
+                // Because this effect has two steps, we have to call the processAction method here, so that triggered effects of the target going to graveyard go to the stack
+                // If we don't do it here, gained triggered effects to the target will be removed from the following moveCards method and the applyEffcts done there.
+                // Example: {@link org.mage.test.commander.duel.MairsilThePretenderTest#MairsilThePretenderTest Test}
+                game.getState().processAction(game);
+                
                 Cards revealed = new CardsImpl();
                 Cards otherCards = new CardsImpl();
                 for (Card card : controller.getLibrary().getCards(game)) {
diff --git a/Mage.Tests/CommanderDuel_Mairisil_UBR.dck b/Mage.Tests/CommanderDuel_Mairisil_UBR.dck
new file mode 100644
index 0000000000..e2368e5a05
--- /dev/null
+++ b/Mage.Tests/CommanderDuel_Mairisil_UBR.dck
@@ -0,0 +1,90 @@
+1 [DGM:11] Aetherling
+1 [JUD:77] Anger
+1 [10E:66] Arcanis the Omnipotent
+1 [SOM:28] Argent Sphinx
+1 [CHK:52] Azami, Lady of Scrolls
+1 [AVR:47] Deadeye Navigator
+1 [DRK:44] Eater of the Dead
+1 [EXO:33] Ertai, Wizard Adept
+1 [ALA:43] Fatestitcher
+1 [SOM:64] Geth, Lord of the Vault
+1 [EVE:55] Hateflayer
+1 [DKA:139] Havengul Lich
+1 [10E:87] Horseshoe Crab
+1 [ICE:136] Infernal Denizen
+1 [M12:59] Jace's Archivist
+1 [SHM:42] Knacksaw Clique
+1 [OGW:4] Kozilek, the Great Distortion
+1 [PLC:43] Magus of the Bazaar
+1 [ICE:150] Minion of Leshrac
+1 [SOM:72] Necrotic Ooze
+1 [SHM:258] Pili-Pala
+1 [MRD:47] Quicksilver Elemental
+1 [SOK:53] Sakashima the Impostor
+1 [MIR:142] Shauku, Endbringer
+1 [M15:231] Soul of New Phyrexia
+1 [PLC:110] Torchling
+1 [EMN:109] Tree of Perdition
+1 [M20:64] Leyline of Anticipation
+1 [9ED:152] Phyrexian Arena
+1 [PCY:45] Rhystic Study
+1 [RNA:245] Blood Crypt
+1 [KTK:230] Bloodstained Mire
+1 [WWK:132] Bojuka Bog
+1 [ELD:333] Command Tower
+1 [ALA:222] Crumbling Necropolis
+1 [RAV:276] Dimir Aqueduct
+1 [CON:142] Exotic Orchard
+1 [CHK:277] Hall of the Bandit Lord
+8 [IKO:263] Island
+1 [GPT:159] Izzet Boilerworks
+1 [CHK:279] Minamo, School at Water's Edge
+3 [IKO:269] Mountain
+1 [C20:298] Path of Ancestry
+1 [KTK:239] Polluted Delta
+1 [M19:254] Reliquary Tower
+1 [ONS:322] Riptide Laboratory
+1 [ORI:251] Shivan Reef
+1 [GRN:257] Steam Vents
+1 [10E:359] Sulfurous Springs
+1 [BFZ:249] Sunken Hollow
+4 [IKO:266] Swamp
+1 [10E:362] Underground River
+1 [GRN:259] Watery Grave
+1 [ODY:118] Buried Alive
+1 [3ED:105] Demonic Tutor
+1 [USG:188] Gamble
+1 [AVR:151] Reforge the Soul
+1 [EMA:108] Toxic Deluge
+1 [MB1:1603] Mana Crypt
+1 [5ED:388] Mana Vault
+1 [HOU:165] Mirage Mirror
+1 [5ED:391] Nevinyrral's Disk
+1 [CHK:268] Sensei's Divining Top
+1 [3ED:274] Sol Ring
+1 [5DN:156] Staff of Domination
+1 [LRW:263] Thousand-Year Elixir
+1 [UDS:139] Thran Dynamo
+1 [5DN:163] Vedalken Orrery
+1 [TMP:55] Capsize
+1 [C20:146] Chaos Warp
+1 [RTR:35] Cyclonic Rift
+1 [ODY:132] Entomb
+1 [INV:57] Fact or Fiction
+1 [TMP:70] Intuition
+1 [THS:65] Swan Song
+1 [RTR:111] Vandalblast
+1 [3ED:185] Wheel of Fortune
+1 [GTC:207] Whispering Madness
+1 [USG:111] Windfall
+1 [SHM:248] Cauldron of Souls
+1 [RAV:260] Dimir Signet
+1 [9ED:297] Fellwar Stone
+1 [DOM:215] Gilded Lotus
+1 [ULG:126] Grim Monolith
+1 [GTC:231] Illusionist's Bracers
+1 [GPT:152] Izzet Signet
+1 [MRD:199] Lightning Greaves
+SB: 1 [C17:41] Mairsil, the Pretender
+LAYOUT MAIN:(1,7)(CARD_TYPE,false,50)|([SHM:258],[M15:231])([SHM:248],[RAV:260],[9ED:297],[DOM:215],[ULG:126],[GTC:231],[GPT:152],[MRD:199],[MB1:1603],[5ED:388],[HOU:165],[5ED:391],[CHK:268],[3ED:274],[5DN:156],[LRW:263],[UDS:139],[5DN:163])([DGM:11],[JUD:77],[10E:66],[SOM:28],[CHK:52],[AVR:47],[DRK:44],[EXO:33],[ALA:43],[SOM:64],[EVE:55],[DKA:139],[10E:87],[ICE:136],[M12:59],[SHM:42],[OGW:4],[PLC:43],[ICE:150],[SOM:72],[MRD:47],[SOK:53],[MIR:142],[PLC:110],[EMN:109])([M20:64],[9ED:152],[PCY:45])([TMP:55],[C20:146],[RTR:35],[ODY:132],[INV:57],[TMP:70],[THS:65])([RNA:245],[KTK:230],[WWK:132],[ELD:333],[ALA:222],[RAV:276],[CON:142],[CHK:277],[IKO:263],[IKO:263],[IKO:263],[IKO:263],[IKO:263],[IKO:263],[IKO:263],[IKO:263],[GPT:159],[CHK:279],[IKO:269],[IKO:269],[IKO:269],[C20:298],[KTK:239],[M19:254],[ONS:322],[ORI:251],[GRN:257],[10E:359],[BFZ:249],[IKO:266],[IKO:266],[IKO:266],[IKO:266],[10E:362],[GRN:259])([ODY:118],[3ED:105],[USG:188],[AVR:151],[EMA:108],[RTR:111],[3ED:185],[GTC:207],[USG:111])
+LAYOUT SIDEBOARD:(1,1)(NONE,false,50)|([C17:41])
diff --git a/Mage.Tests/src/test/java/org/mage/test/commander/duel/MairsilThePretenderTest.java b/Mage.Tests/src/test/java/org/mage/test/commander/duel/MairsilThePretenderTest.java
new file mode 100644
index 0000000000..d9e5370775
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/commander/duel/MairsilThePretenderTest.java
@@ -0,0 +1,203 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.mage.test.commander.duel;
+
+import java.io.FileNotFoundException;
+import mage.constants.PhaseStep;
+import mage.constants.Zone;
+import mage.game.Game;
+import mage.game.GameException;
+import org.junit.Test;
+import static org.mage.test.player.TestPlayer.NO_TARGET;
+import org.mage.test.serverside.base.CardTestCommanderDuelBase;
+
+/**
+ *
+ * @author LevelX2
+ */
+public class MairsilThePretenderTest extends CardTestCommanderDuelBase {
+
+    @Override
+    protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
+        // When Mairsil, the Pretender enters the battlefield, you may exile an artifact or creature card from your hand
+        // or graveyard and put a cage counter on it.
+        // Mairsil, the Pretender has all activated abilities of all cards you own in exile with cage counters on them. 
+        // You may activate each of those abilities only once each turn.               
+        setDecknamePlayerA("CommanderDuel_Mairisil_UBR.dck"); // Commander = Mairsil, the Pretender {1}{U}{B}{R}
+        return super.createNewGameAndPlayers();
+    }
+
+    /**
+     * Basic Test
+     */
+    @Test
+    public void useShifitingShadowTest() {
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
+
+        // Enchant creature
+        // Enchanted creature has haste and “At the beginning of your upkeep, destroy this creature. 
+        // Reveal cards from the top of your library until you reveal a creature card.
+        // Put that card onto the battlefield and attach Shifting Shadow to it, 
+        // then put all other cards revealed this way on the bottom of your library in a random order.”
+        addCard(Zone.HAND, playerA, "Shifting Shadow", 1); // {2}{R}
+        
+        // Tap: Draw three cards.
+        // {2}{U}{U}: Return Arcanis the Omnipotent to its owner's hand.        
+        addCard(Zone.HAND, playerA, "Arcanis the Omnipotent", 1); // Creature {3}{U}{U}{U}
+
+        setChoice(playerA, "Yes");
+        
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mairsil, the Pretender");        
+        setChoice(playerA, "Yes"); // Exile a card
+        setChoice(playerA, "Arcanis the Omnipotent");
+        
+        
+        castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Shifting Shadow", "Mairsil, the Pretender");
+
+        setChoice(playerA, "Yes"); // Move commander to command zone
+        setStopAt(5, PhaseStep.PRECOMBAT_MAIN);
+
+        setStrictChooseMode(true);
+        execute();
+        assertAllCommandsUsed();
+        
+        assertLife(playerA, 40);
+        assertLife(playerB, 40);
+
+        assertExileCount("Arcanis the Omnipotent", 1);
+
+        assertCommandZoneCount(playerA, "Mairsil, the Pretender", 1);
+        
+        assertPermanentCount(playerA, "Shifting Shadow", 1);        
+    }
+    /**
+     * I tried playing it in Mairsil the Pretender commander deck and found 2
+     * cases where the creature is not properly destroyed:
+     *
+     * Using Arcanis the Omnipotent ability to return my commander to hand while
+     * trigger is on the stack I got Mairsil to hand then got asked whether I
+     * want to put him in gy so I answered yes assuming it cannot be destroyed
+     * while in my hand. He got put in graveyard straight from my hand.
+     * According to Oracle rules I should get a creature from top of the deck
+     * and still have my commander in hand. After giving my commander undying
+     * with shifting shadow trigger on the stack he got put in gy with undying
+     * not triggering.
+     *
+     * All this points to the card being hardcoded to put the creature into
+     * graveyard instead of simply destroying it
+     */
+    @Test
+    public void useShifitingShadowAndArcanisTest() {
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
+        addCard(Zone.LIBRARY, playerA, "Silvercoat Lion", 3);
+        skipInitShuffling();
+        // Enchant creature
+        // Enchanted creature has haste and “At the beginning of your upkeep, destroy this creature. 
+        // Reveal cards from the top of your library until you reveal a creature card.
+        // Put that card onto the battlefield and attach Shifting Shadow to it, 
+        // then put all other cards revealed this way on the bottom of your library in a random order.”
+        addCard(Zone.HAND, playerA, "Shifting Shadow", 1); // {2}{R}
+        
+        // Tap: Draw three cards.
+        // {2}{U}{U}: Return Arcanis the Omnipotent to its owner's hand.        
+        addCard(Zone.HAND, playerA, "Arcanis the Omnipotent", 1); // Creature {3}{U}{U}{U}
+
+        setChoice(playerA, "Yes");
+        
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mairsil, the Pretender");        
+        setChoice(playerA, "Yes"); // Exile a card
+        setChoice(playerA, "Arcanis the Omnipotent");
+        
+        
+        castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Shifting Shadow", "Mairsil, the Pretender");
+
+        activateAbility(5, PhaseStep.UPKEEP, playerA, "{2}{U}{U}: Return", NO_TARGET, "At the beginning of your upkeep");
+        setChoice(playerA, "No"); // Don't move commander to command zone because it goes to hand
+
+        setStopAt(5, PhaseStep.PRECOMBAT_MAIN);
+
+        setStrictChooseMode(true);
+        execute();
+        assertAllCommandsUsed();
+        
+        assertLife(playerA, 40);
+        assertLife(playerB, 40);
+
+        assertExileCount("Arcanis the Omnipotent", 1);
+
+        assertCommandZoneCount(playerA, "Mairsil, the Pretender", 0);
+        assertHandCount(playerA, "Mairsil, the Pretender", 1);
+       
+        
+        assertGraveyardCount(playerA, "Shifting Shadow", 1);  // Goes to graveyard because commander left battlefield to hand from Arcanis ability     
+        assertPermanentCount(playerA, "Silvercoat Lion", 1); 
+    }
+    
+    @Test
+    public void useShifitingShadowAndEndlingTest() {
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
+        addCard(Zone.LIBRARY, playerA, "Silvercoat Lion", 3);
+        skipInitShuffling();
+        // Enchant creature
+        // Enchanted creature has haste and “At the beginning of your upkeep, destroy this creature. 
+        // Reveal cards from the top of your library until you reveal a creature card.
+        // Put that card onto the battlefield and attach Shifting Shadow to it, 
+        // then put all other cards revealed this way on the bottom of your library in a random order.”
+        addCard(Zone.HAND, playerA, "Shifting Shadow", 1); // {2}{R}
+        // Tap: Draw three cards.
+        // {2}{U}{U}: Return Arcanis the Omnipotent to its owner's hand.        
+        addCard(Zone.HAND, playerA, "Arcanis the Omnipotent", 1); // Creature {3}{U}{U}{U}        
+        // {B}: Endling gains menace until end of turn.
+        // {B}: Endling gains deathtouch until end of turn.
+        // {B}: Endling gains undying until end of turn.
+        // {1}: Endling gets +1/-1 or -1/+1 until end of turn.   
+        addCard(Zone.HAND, playerA, "Endling", 1); // Creature {3}{U}{U}{U}
+
+        
+        
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mairsil, the Pretender");                
+        setChoice(playerA, "Yes"); // Exile a card
+        setChoice(playerA, "Yes"); // Exile from Hand
+        setChoice(playerA, "Endling");
+        
+        
+        castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Shifting Shadow", "Mairsil, the Pretender");
+
+        activateAbility(5, PhaseStep.UPKEEP, playerA, "{B}: {this} gains Undying", NO_TARGET, "At the beginning of your upkeep");
+        setChoice(playerA, "No"); // Don't move commander to command zone because can come back by Undying
+        
+        setChoice(playerA, "Yes"); // Exile a card (Mairsil comes back from Undying)
+        setChoice(playerA, "Yes"); // Exile from hand
+        setChoice(playerA, "Arcanis the Omnipotent");
+        
+        setStopAt(5, PhaseStep.PRECOMBAT_MAIN);
+
+        setStrictChooseMode(true);
+        execute();
+        assertAllCommandsUsed();
+        
+        assertLife(playerA, 40);
+        assertLife(playerB, 40);
+
+        assertExileCount("Endling", 1);
+        assertExileCount("Arcanis the Omnipotent", 1);
+
+        assertCommandZoneCount(playerA, "Mairsil, the Pretender", 0);
+        assertGraveyardCount(playerA, "Mairsil, the Pretender", 0);
+        assertPermanentCount(playerA, "Mairsil, the Pretender", 1); // Returns from Undying
+        assertPowerToughness(playerA, "Mairsil, the Pretender", 5, 5);
+        
+        assertPermanentCount(playerA, "Shifting Shadow", 1);  // Enchants Silvercoat Lion    
+        assertPermanentCount(playerA, "Silvercoat Lion", 1); 
+    }
+
+}
diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
index 6d5b96b9cf..6a7250afee 100644
--- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
+++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
@@ -1600,6 +1600,15 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
         player.addAction(turnNum, step, ACTIVATE_ABILITY + ability + "$target=" + String.join("^", targetNames));
     }
 
+    /**
+     * 
+     * @param turnNum
+     * @param step
+     * @param player
+     * @param ability
+     * @param targetName  use NO_TARGET if there is no target to set
+     * @param spellOnStack 
+     */
     public void activateAbility(int turnNum, PhaseStep step, TestPlayer player, String ability, String targetName, String spellOnStack) {
         // TODO: it's uses computerPlayer to execute, only ability target will work, but choices and targets commands aren't
         this.activateAbility(turnNum, step, player, ability, targetName, spellOnStack, StackClause.WHILE_ON_STACK);
diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/CommanderReplacementEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/CommanderReplacementEffect.java
index 399cd4c72a..e60ccca358 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/continuous/CommanderReplacementEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/CommanderReplacementEffect.java
@@ -91,9 +91,9 @@ public class CommanderReplacementEffect extends ReplacementEffectImpl {
     public boolean applies(GameEvent event, Ability source, Game game) {
         ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
 
-        if (!game.isSimulation() && commanderId.equals(zEvent.getTargetId())) {
-            //System.out.println("applies " + game.getTurnNum() + ": " + game.getObject(event.getTargetId()).getName() + ": " + zEvent.getFromZone() + " -> " + zEvent.getToZone() + "; " + game.getObject(zEvent.getSourceId()));
-        }
+//        if (!game.isSimulation() && commanderId.equals(zEvent.getTargetId())) {
+//            System.out.println("applies " + game.getTurnNum() + ": " + game.getObject(event.getTargetId()).getName() + ": " + zEvent.getFromZone() + " -> " + zEvent.getToZone() + "; " + game.getObject(zEvent.getSourceId()));
+//        }
 
         if (zEvent.getToZone().equals(Zone.HAND) && !alsoHand) {
             return false;

From 75d0926f4b2d1684909e2fc04ba8ba003f30e47b Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Wed, 3 Jun 2020 18:06:03 +0200
Subject: [PATCH 086/586] * Some minor changes.

---
 .../src/mage/cards/n/NacatlWarPride.java      |  2 +-
 .../cards/copy/VolrathsShapshifterTest.java   | 63 ++++++++++---------
 .../duel/MairsilThePretenderTest.java         |  4 +-
 3 files changed, 38 insertions(+), 31 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/n/NacatlWarPride.java b/Mage.Sets/src/mage/cards/n/NacatlWarPride.java
index 3bc52a35f6..514b256315 100644
--- a/Mage.Sets/src/mage/cards/n/NacatlWarPride.java
+++ b/Mage.Sets/src/mage/cards/n/NacatlWarPride.java
@@ -63,7 +63,7 @@ class NacatlWarPrideEffect extends OneShotEffect {
 
     public NacatlWarPrideEffect() {
         super(Outcome.Benefit);
-        this.staticText = "create X tokens that are copies of Nacatl War-Pride tapped and attacking, where X is the number of creatures defending player controls. Exile the tokens at the beginning of the next end step.";
+        this.staticText = "create X tokens that are copies of {this} tapped and attacking, where X is the number of creatures defending player controls. Exile the tokens at the beginning of the next end step";
     }
 
     public NacatlWarPrideEffect(final NacatlWarPrideEffect effect) {
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/VolrathsShapshifterTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/VolrathsShapshifterTest.java
index d8f4d44902..c985c48493 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/VolrathsShapshifterTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/VolrathsShapshifterTest.java
@@ -16,11 +16,11 @@ import org.mage.test.serverside.base.CardTestPlayerBase;
 
 /**
  * Volrath's Shapeshifter
- * 
- * As long as the top card of your graveyard is a creature card, 
- * Volrath's Shapeshifter has the full text of that card and has the 
- * text "2: Discard a card." (Volrath's Shapeshifter has that card's name,
- *  mana cost, color, types, abilities, power, and toughness.)
+ *
+ * As long as the top card of your graveyard is a creature card, Volrath's
+ * Shapeshifter has the full text of that card and has the text "2: Discard a
+ * card." (Volrath's Shapeshifter has that card's name, mana cost, color, types,
+ * abilities, power, and toughness.)
  *
  */
 public class VolrathsShapshifterTest extends CardTestPlayerBase {
@@ -36,9 +36,11 @@ public class VolrathsShapshifterTest extends CardTestPlayerBase {
         addCard(Zone.GRAVEYARD, playerA, "Assault Griffin", 1);
         addCard(Zone.LIBRARY, playerA, "Forest", 1);
         skipInitShuffling();
-        
+
         setStopAt(1, PhaseStep.END_TURN);
-        execute();
+        setStrictChooseMode(true);
+        execute();        
+        assertAllCommandsUsed();
 
         assertPermanentCount(playerA, "Assault Griffin", 1);
         assertPowerToughness(playerA, "Assault Griffin", 3, 2);
@@ -48,12 +50,13 @@ public class VolrathsShapshifterTest extends CardTestPlayerBase {
         Assert.assertTrue("Volrath's Shapeshifter must have flying", shapeshifter.getAbilities().contains(FlyingAbility.getInstance()));
         Assert.assertTrue("Volrath's Shapeshifter must have {2} : Discard a card", hasShapeshiftersOriginalAbility(shapeshifter));
     }
-    
+
     /**
-     * Tests turing back into Volrath's Shapeshifter after a new card is put on top that isn't a creature
+     * Tests turing back into Volrath's Shapeshifter after a new card is put on
+     * top that isn't a creature
      */
     @Test
-	public void testLosingCopy() {
+    public void testLosingCopy() {
         addCard(Zone.BATTLEFIELD, playerA, "Volrath's Shapeshifter", 1);
         // Codex Shredder - Artifact
         // {T}: Target player puts the top card of their library into their graveyard.
@@ -68,7 +71,9 @@ public class VolrathsShapshifterTest extends CardTestPlayerBase {
         activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player puts the top card of their library into their graveyard.", playerA);
 
         setStopAt(1, PhaseStep.END_TURN);
-        execute();
+        setStrictChooseMode(true);
+        execute();        
+        assertAllCommandsUsed();
 
         assertPermanentCount(playerA, "Volrath's Shapeshifter", 1);
         assertPowerToughness(playerA, "Volrath's Shapeshifter", 0, 1);
@@ -77,25 +82,27 @@ public class VolrathsShapshifterTest extends CardTestPlayerBase {
         Assert.assertTrue(shapeshifter.getSubtype(currentGame).contains(SubType.SHAPESHIFTER));
         Assert.assertTrue("Volrath's Shapeshifter must have {2} : Discard a card", hasShapeshiftersOriginalAbility(shapeshifter));
     }
-    
+
     @Test
     public void testControlChange() {
         addCard(Zone.BATTLEFIELD, playerA, "Island", 5);
         addCard(Zone.HAND, playerA, "Mind Control", 1);
-        
 
         addCard(Zone.LIBRARY, playerA, "Forest", 1);
         addCard(Zone.GRAVEYARD, playerA, "Dutiful Thrull", 1);
-        
+
         addCard(Zone.BATTLEFIELD, playerB, "Volrath's Shapeshifter", 1);
         addCard(Zone.GRAVEYARD, playerB, "Assault Griffin", 1);
-        
+
         skipInitShuffling();
-        
+
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mind Control", "Assault Griffin");
-        
+
         setStopAt(1, PhaseStep.END_TURN);
-        execute();
+        
+        setStrictChooseMode(true);
+        execute();        
+        assertAllCommandsUsed();
         
         assertPermanentCount(playerA, "Dutiful Thrull", 1);
         assertPowerToughness(playerA, "Dutiful Thrull", 1, 1);
@@ -103,20 +110,18 @@ public class VolrathsShapshifterTest extends CardTestPlayerBase {
         Permanent shapeshifter = getPermanent("Dutiful Thrull", playerA.getId());
         Assert.assertTrue(shapeshifter.getSubtype(currentGame).contains(SubType.THRULL));
         Assert.assertTrue("Volrath's Shapeshifter must have {2} : Discard a card", hasShapeshiftersOriginalAbility(shapeshifter));
-        
-        
     }
-    
+
     private boolean hasShapeshiftersOriginalAbility(Permanent shapeshifter) {
         for (Ability ability : shapeshifter.getAbilities()) {
-			if(ability instanceof SimpleActivatedAbility) {
-				SimpleActivatedAbility simpleActivatedAbility = (SimpleActivatedAbility)ability;
-				return simpleActivatedAbility.getZone() == Zone.BATTLEFIELD && simpleActivatedAbility.getEffects().size() == 1 &&
-					simpleActivatedAbility.getEffects().get(0) instanceof DiscardControllerEffect && simpleActivatedAbility.getManaCosts().size() == 1 
-							&& simpleActivatedAbility.getManaCosts().get(0) instanceof GenericManaCost && simpleActivatedAbility.getManaCosts().get(0).convertedManaCost() == 2;
-			}
-		}
-        
+            if (ability instanceof SimpleActivatedAbility) {
+                SimpleActivatedAbility simpleActivatedAbility = (SimpleActivatedAbility) ability;
+                return simpleActivatedAbility.getZone() == Zone.BATTLEFIELD && simpleActivatedAbility.getEffects().size() == 1
+                        && simpleActivatedAbility.getEffects().get(0) instanceof DiscardControllerEffect && simpleActivatedAbility.getManaCosts().size() == 1
+                        && simpleActivatedAbility.getManaCosts().get(0) instanceof GenericManaCost && simpleActivatedAbility.getManaCosts().get(0).convertedManaCost() == 2;
+            }
+        }
+
         return false;
     }
 }
diff --git a/Mage.Tests/src/test/java/org/mage/test/commander/duel/MairsilThePretenderTest.java b/Mage.Tests/src/test/java/org/mage/test/commander/duel/MairsilThePretenderTest.java
index d9e5370775..786be26818 100644
--- a/Mage.Tests/src/test/java/org/mage/test/commander/duel/MairsilThePretenderTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/commander/duel/MairsilThePretenderTest.java
@@ -38,7 +38,9 @@ public class MairsilThePretenderTest extends CardTestCommanderDuelBase {
         addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
         addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
         addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
-
+        addCard(Zone.LIBRARY, playerA, "Silvercoat Lion", 3);        
+        skipInitShuffling();
+        
         // Enchant creature
         // Enchanted creature has haste and “At the beginning of your upkeep, destroy this creature. 
         // Reveal cards from the top of your library until you reveal a creature card.

From 4061bebc40b1e963da2bdd5d896e8b2d5cb3746d Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Wed, 3 Jun 2020 19:00:48 +0200
Subject: [PATCH 087/586] * Nacatl-War Pride - minor rule text fix.

---
 Mage.Sets/src/mage/cards/n/NacatlWarPride.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Mage.Sets/src/mage/cards/n/NacatlWarPride.java b/Mage.Sets/src/mage/cards/n/NacatlWarPride.java
index 514b256315..cf29d0ba96 100644
--- a/Mage.Sets/src/mage/cards/n/NacatlWarPride.java
+++ b/Mage.Sets/src/mage/cards/n/NacatlWarPride.java
@@ -103,7 +103,7 @@ class NacatlWarPrideEffect extends OneShotEffect {
         if (!copies.isEmpty()) {
             FixedTargets fixedTargets = new FixedTargets(copies, game);
             ExileTargetEffect exileEffect = new ExileTargetEffect();
-            exileEffect.setTargetPointer(fixedTargets);
+            exileEffect.setTargetPointer(fixedTargets).setText("exile the tokens");
             game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect), source);
             return true;
         }

From cce467a5ec8d4679f6118bdcdb15343ae2863f54 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Thu, 4 Jun 2020 03:21:18 +0400
Subject: [PATCH 088/586] Additional fix and simplified for playable abilities
 (see comments b94344341bf47968fcfa2018aa418547da25917e)

---
 .../src/mage/player/ai/SimulatedPlayer2.java  |  13 +-
 .../src/mage/player/ai/MCTSPlayer.java        |   9 +-
 .../mage/player/ai/SimulatedPlayerMCTS.java   |   2 +-
 .../src/mage/player/ai/SimulatedPlayer.java   |   7 +-
 .../src/mage/player/human/HumanPlayer.java    |   2 +-
 .../java/mage/server/util/SystemUtil.java     |   6 +-
 .../CastSplitCardsWithFlashbackTest.java      |  34 +++-
 .../org/mage/test/player/RandomPlayer.java    |   6 +-
 .../java/org/mage/test/player/TestPlayer.java |  20 +--
 .../java/org/mage/test/stub/PlayerStub.java   |   4 +-
 Mage/src/main/java/mage/cards/CardImpl.java   |  16 ++
 Mage/src/main/java/mage/players/Player.java   |   4 +-
 .../main/java/mage/players/PlayerImpl.java    | 156 +++++++-----------
 13 files changed, 142 insertions(+), 137 deletions(-)

diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java
index 54ac89b928..771dc4afcd 100644
--- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java
+++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java
@@ -3,6 +3,7 @@ package mage.player.ai;
 import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.AbilityImpl;
+import mage.abilities.ActivatedAbility;
 import mage.abilities.TriggeredAbility;
 import mage.abilities.common.PassAbility;
 import mage.abilities.costs.mana.ManaCost;
@@ -94,9 +95,9 @@ public class SimulatedPlayer2 extends ComputerPlayer {
     }
 
     protected void simulateOptions(Game game) {
-        List<Ability> playables = game.getPlayer(playerId).getPlayable(game, isSimulatedPlayer);
+        List<ActivatedAbility> playables = game.getPlayer(playerId).getPlayable(game, isSimulatedPlayer);
         playables = filterAbilities(game, playables, suggested);
-        for (Ability ability : playables) {
+        for (ActivatedAbility ability : playables) {
             if (ability.getAbilityType() == AbilityType.MANA) {
                 continue;
             }
@@ -186,15 +187,15 @@ public class SimulatedPlayer2 extends ComputerPlayer {
      * @param suggested
      * @return
      */
-    protected List<Ability> filterAbilities(Game game, List<Ability> playables, List<String> suggested) {
+    protected List<ActivatedAbility> filterAbilities(Game game, List<ActivatedAbility> playables, List<String> suggested) {
         if (playables.isEmpty()) {
             return playables;
         }
         if (suggested == null || suggested.isEmpty()) {
             return playables;
         }
-        List<Ability> filtered = new ArrayList<>();
-        for (Ability ability : playables) {
+        List<ActivatedAbility> filtered = new ArrayList<>();
+        for (ActivatedAbility ability : playables) {
             Card card = game.getCard(ability.getSourceId());
             if (card != null) {
                 for (String s : suggested) {
@@ -212,7 +213,7 @@ public class SimulatedPlayer2 extends ComputerPlayer {
         return playables;
     }
 
-    protected List<Ability> filterOptions(Game game, List<Ability> options, Ability ability, List<String> suggested) {
+    protected List<Ability> filterOptions(Game game, List<Ability> options, ActivatedAbility ability, List<String> suggested) {
         if (options.isEmpty()) {
             return options;
         }
diff --git a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/MCTSPlayer.java b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/MCTSPlayer.java
index 5dedfe41d7..bdd731ffc4 100644
--- a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/MCTSPlayer.java
+++ b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/MCTSPlayer.java
@@ -2,6 +2,7 @@
 package mage.player.ai;
 
 import mage.abilities.Ability;
+import mage.abilities.ActivatedAbility;
 import mage.abilities.SpellAbility;
 import mage.abilities.common.PassAbility;
 import mage.abilities.costs.mana.GenericManaCost;
@@ -45,16 +46,16 @@ public class MCTSPlayer extends ComputerPlayer {
         return new MCTSPlayer(this);
     }
 
-    protected List<Ability> getPlayableAbilities(Game game) {
-        List<Ability> playables = getPlayable(game, true);
+    protected List<ActivatedAbility> getPlayableAbilities(Game game) {
+        List<ActivatedAbility> playables = getPlayable(game, true);
         playables.add(pass);
         return playables;
     }
 
     public List<Ability> getPlayableOptions(Game game) {
         List<Ability> all = new ArrayList<>();
-        List<Ability> playables = getPlayableAbilities(game);
-        for (Ability ability: playables) {
+        List<ActivatedAbility> playables = getPlayableAbilities(game);
+        for (ActivatedAbility ability: playables) {
             List<Ability> options = game.getPlayer(playerId).getPlayableOptions(ability, game);
             if (options.isEmpty()) {
                 if (!ability.getManaCosts().getVariableCosts().isEmpty()) {
diff --git a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java
index 0b3c44f268..957f43e5ac 100644
--- a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java
+++ b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java
@@ -73,7 +73,7 @@ public class SimulatedPlayerMCTS extends MCTSPlayer {
     }
 
     private Ability getAction(Game game) {
-        List<Ability> playables = getPlayableAbilities(game);
+        List<ActivatedAbility> playables = getPlayableAbilities(game);
         Ability ability;
         while (true) {
             if (playables.size() == 1) {
diff --git a/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/SimulatedPlayer.java b/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/SimulatedPlayer.java
index 3bc58353c7..4b141ec80a 100644
--- a/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/SimulatedPlayer.java
+++ b/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/SimulatedPlayer.java
@@ -3,6 +3,7 @@
 package mage.player.ai;
 
 import mage.abilities.Ability;
+import mage.abilities.ActivatedAbility;
 import mage.abilities.SpellAbility;
 import mage.abilities.TriggeredAbility;
 import mage.abilities.common.PassAbility;
@@ -59,10 +60,10 @@ public class SimulatedPlayer extends ComputerPlayer {
         return list;
     }
 
-    protected void simulateOptions(Game game, Ability previousActions) {
+    protected void simulateOptions(Game game, ActivatedAbility previousActions) {
         allActions.add(previousActions);
-        List<Ability> playables = game.getPlayer(playerId).getPlayable(game, isSimulatedPlayer);
-        for (Ability ability: playables) {
+        List<ActivatedAbility> playables = game.getPlayer(playerId).getPlayable(game, isSimulatedPlayer);
+        for (ActivatedAbility ability: playables) {
             List<Ability> options = game.getPlayer(playerId).getPlayableOptions(ability, game);
             if (options.isEmpty()) {
                 if (!ability.getManaCosts().getVariableCosts().isEmpty()) {
diff --git a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java
index 7455d09336..feb685d60c 100644
--- a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java
+++ b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java
@@ -1056,7 +1056,7 @@ public class HumanPlayer extends PlayerImpl {
                             actingPlayer = game.getPlayer(game.getPriorityPlayerId());
                         }
                         if (actingPlayer != null) {
-                            useableAbilities = actingPlayer.getUseableActivatedAbilities(object, zone, game);
+                            useableAbilities = actingPlayer.getPlayableActivatedAbilities(object, zone, game);
                         }
 
                         if (object instanceof Card
diff --git a/Mage.Server/src/main/java/mage/server/util/SystemUtil.java b/Mage.Server/src/main/java/mage/server/util/SystemUtil.java
index 58f8feb130..1d64adf871 100644
--- a/Mage.Server/src/main/java/mage/server/util/SystemUtil.java
+++ b/Mage.Server/src/main/java/mage/server/util/SystemUtil.java
@@ -425,7 +425,7 @@ public final class SystemUtil {
                                 game.firePriorityEvent(opponent.getId());
                             }
 
-                            List<Ability> abilities = opponent.getPlayable(game, true);
+                            List<ActivatedAbility> abilities = opponent.getPlayable(game, true);
                             Map<String, String> choices = new HashMap<>();
                             abilities.forEach(ability -> {
                                 MageObject object = ability.getSourceObject(game);
@@ -437,10 +437,10 @@ public final class SystemUtil {
                             choice.setKeyChoices(choices);
                             if (feedbackPlayer.choose(Outcome.Detriment, choice, game) && choice.getChoiceKey() != null) {
                                 String needId = choice.getChoiceKey();
-                                Optional<Ability> ability = abilities.stream().filter(a -> a.getId().toString().equals(needId)).findFirst();
+                                Optional<ActivatedAbility> ability = abilities.stream().filter(a -> a.getId().toString().equals(needId)).findFirst();
                                 if (ability.isPresent()) {
                                     // TODO: set priority for player?
-                                    ActivatedAbility activatedAbility = (ActivatedAbility) ability.get();
+                                    ActivatedAbility activatedAbility = ability.get();
                                     game.informPlayers(feedbackPlayer.getLogName() + " as another player " + opponent.getLogName()
                                             + " trying to force an activate ability: " + activatedAbility.getGameLogMessage(game));
                                     if (opponent.activateAbility(activatedAbility, game)) {
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithFlashbackTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithFlashbackTest.java
index d81189f6e0..9aa250eb7e 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithFlashbackTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithFlashbackTest.java
@@ -37,7 +37,7 @@ public class CastSplitCardsWithFlashbackTest extends CardTestPlayerBase {
     }
 
     @Test
-    public void test_Flashback_Split() {
+    public void test_Flashback_SplitLeft() {
         // {1}{U}
         // When Snapcaster Mage enters the battlefield, target instant or sorcery card in your graveyard gains flashback until end of turn.
         addCard(Zone.HAND, playerA, "Snapcaster Mage", 1);
@@ -67,4 +67,36 @@ public class CastSplitCardsWithFlashbackTest extends CardTestPlayerBase {
         assertGraveyardCount(playerB, "Bident of Thassa", 1);
         assertPermanentCount(playerB, "Bow of Nylea", 1);
     }
+
+    @Test
+    public void test_Flashback_SplitRight() {
+        // {1}{U}
+        // When Snapcaster Mage enters the battlefield, target instant or sorcery card in your graveyard gains flashback until end of turn.
+        addCard(Zone.HAND, playerA, "Snapcaster Mage", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
+        //
+        // Wear {1}{R} Destroy target artifact.
+        // Tear {W} Destroy target enchantment.
+        addCard(Zone.GRAVEYARD, playerA, "Wear // Tear", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
+        addCard(Zone.BATTLEFIELD, playerB, "Bident of Thassa", 1); // Legendary Enchantment Artifact
+        addCard(Zone.BATTLEFIELD, playerB, "Bow of Nylea", 1); // Legendary Enchantment Artifact
+
+        // add flashback
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Snapcaster Mage");
+        addTarget(playerA, "Wear // Tear");
+
+        // cast as flashback
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Flashback {W}", "Bident of Thassa");
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        assertGraveyardCount(playerB, "Bident of Thassa", 1);
+        assertPermanentCount(playerB, "Bow of Nylea", 1);
+    }
 }
diff --git a/Mage.Tests/src/test/java/org/mage/test/player/RandomPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/RandomPlayer.java
index 3a5fdc13bf..f2db711c38 100644
--- a/Mage.Tests/src/test/java/org/mage/test/player/RandomPlayer.java
+++ b/Mage.Tests/src/test/java/org/mage/test/player/RandomPlayer.java
@@ -73,7 +73,7 @@ public class RandomPlayer extends ComputerPlayer {
     }
 
     private Ability getAction(Game game) {
-        List<Ability> playables = getPlayableAbilities(game);
+        List<ActivatedAbility> playables = getPlayableAbilities(game);
         Ability ability;
         while (true) {
             if (playables.size() == 1) {
@@ -115,8 +115,8 @@ public class RandomPlayer extends ComputerPlayer {
         return ability;
     }
 
-    protected List<Ability> getPlayableAbilities(Game game) {
-        List<Ability> playables = getPlayable(game, true);
+    protected List<ActivatedAbility> getPlayableAbilities(Game game) {
+        List<ActivatedAbility> playables = getPlayable(game, true);
         playables.add(pass);
         return playables;
     }
diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
index b0c2615874..b43b200626 100644
--- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
+++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
@@ -587,14 +587,14 @@ public class TestPlayer implements Player {
                     if (groups.length > 2 && !checkExecuteCondition(groups, game)) {
                         break;
                     }
-                    for (Ability ability : computerPlayer.getPlayable(game, true)) { // add wrong action log?
+                    for (ActivatedAbility ability : computerPlayer.getPlayable(game, true)) { // add wrong action log?
                         if (isAbilityHaveTargetNameOrAlias(game, ability, groups[0])) {
                             int bookmark = game.bookmarkState();
-                            Ability newAbility = ability.copy();
+                            ActivatedAbility newAbility = ability.copy();
                             if (groups.length > 1 && !groups[1].equals("target=" + NO_TARGET)) {
                                 groupsForTargetHandling = groups;
                             }
-                            if (computerPlayer.activateAbility((ActivatedAbility) newAbility, game)) {
+                            if (computerPlayer.activateAbility(newAbility, game)) {
                                 actions.remove(action);
                                 groupsForTargetHandling = null;
                                 foundNoAction = 0; // Reset enless loop check because of no action
@@ -1078,7 +1078,7 @@ public class TestPlayer implements Player {
         });
     }
 
-    private void printAbilities(Game game, List<Ability> abilities) {
+    private void printAbilities(Game game, List<? extends Ability> abilities) {
         System.out.println("Total abilities: " + (abilities != null ? abilities.size() : 0));
         if (abilities == null) {
             return;
@@ -1714,6 +1714,8 @@ public class TestPlayer implements Player {
     private void chooseStrictModeFailed(String choiceType, Game game, String reason, boolean printAbilities) {
         if (strictChooseMode && !AICanChooseInStrictMode) {
             if (printAbilities) {
+                printStart("Available mana for " + computerPlayer.getName());
+                printMana(game, computerPlayer.getManaAvailable(game));
                 printStart("Available abilities for " + computerPlayer.getName());
                 printAbilities(game, computerPlayer.getPlayable(game, true));
                 printEnd();
@@ -2712,8 +2714,8 @@ public class TestPlayer implements Player {
     }
 
     @Override
-    public LinkedHashMap<UUID, ActivatedAbility> getUseableActivatedAbilities(MageObject object, Zone zone, Game game) {
-        return computerPlayer.getUseableActivatedAbilities(object, zone, game);
+    public LinkedHashMap<UUID, ActivatedAbility> getPlayableActivatedAbilities(MageObject object, Zone zone, Game game) {
+        return computerPlayer.getPlayableActivatedAbilities(object, zone, game);
     }
 
     @Override
@@ -3171,12 +3173,8 @@ public class TestPlayer implements Player {
         return computerPlayer.getManaAvailable(game);
     }
 
-    public List<Permanent> getAvailableManaProducersWithCost(Game game) {
-        return computerPlayer.getAvailableManaProducersWithCost(game);
-    }
-
     @Override
-    public List<Ability> getPlayable(Game game, boolean hidden) {
+    public List<ActivatedAbility> getPlayable(Game game, boolean hidden) {
         return computerPlayer.getPlayable(game, hidden);
     }
 
diff --git a/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java b/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java
index d4b5f701cb..7093c32259 100644
--- a/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java
+++ b/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java
@@ -1023,7 +1023,7 @@ public class PlayerStub implements Player {
     }
 
     @Override
-    public List<Ability> getPlayable(Game game, boolean hidden) {
+    public List<ActivatedAbility> getPlayable(Game game, boolean hidden) {
         return null;
     }
 
@@ -1038,7 +1038,7 @@ public class PlayerStub implements Player {
     }
 
     @Override
-    public LinkedHashMap<UUID, ActivatedAbility> getUseableActivatedAbilities(MageObject object, Zone zone, Game game) {
+    public LinkedHashMap<UUID, ActivatedAbility> getPlayableActivatedAbilities(MageObject object, Zone zone, Game game) {
         return null;
     }
 
diff --git a/Mage/src/main/java/mage/cards/CardImpl.java b/Mage/src/main/java/mage/cards/CardImpl.java
index f7d168227b..b968fbc342 100644
--- a/Mage/src/main/java/mage/cards/CardImpl.java
+++ b/Mage/src/main/java/mage/cards/CardImpl.java
@@ -14,6 +14,7 @@ import mage.ObjectColor;
 import mage.abilities.*;
 import mage.abilities.hint.Hint;
 import mage.abilities.hint.HintUtils;
+import mage.abilities.keyword.FlashbackAbility;
 import mage.abilities.mana.ActivatedManaAbilityImpl;
 import mage.cards.repository.PluginClassloaderRegistery;
 import mage.constants.*;
@@ -312,6 +313,21 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
         // dynamic
         all.addAll(cardState.getAbilities());
 
+        // workaround to add dynamic flashback ability from main card to all parts (example: Snapcaster Mage gives flashback to split card)
+        if (!this.getId().equals(this.getMainCard().getId())) {
+            CardState mainCardState = game.getState().getCardState(this.getMainCard().getId());
+            if (mainCardState != null
+                    && !mainCardState.hasLostAllAbilities()
+                    && mainCardState.getAbilities().containsClass(FlashbackAbility.class)) {
+                FlashbackAbility flash = new FlashbackAbility(this.getManaCost(), this.isInstant() ? TimingRule.INSTANT : TimingRule.SORCERY);
+                flash.setSourceId(this.getId());
+                flash.setControllerId(this.getOwnerId());
+                flash.setSpellAbilityType(this.getSpellAbility().getSpellAbilityType());
+                flash.setAbilityName(this.getName());
+                all.add(flash);
+            }
+        }
+
         return all;
     }
 
diff --git a/Mage/src/main/java/mage/players/Player.java b/Mage/src/main/java/mage/players/Player.java
index 8df2d477a3..61e04700c6 100644
--- a/Mage/src/main/java/mage/players/Player.java
+++ b/Mage/src/main/java/mage/players/Player.java
@@ -627,13 +627,13 @@ public interface Player extends MageItem, Copyable<Player> {
 
     ManaOptions getManaAvailable(Game game);
 
-    List<Ability> getPlayable(Game game, boolean hidden);
+    List<ActivatedAbility> getPlayable(Game game, boolean hidden);
 
     List<Ability> getPlayableOptions(Ability ability, Game game);
 
     Map<UUID, Integer> getPlayableObjects(Game game, Zone zone);
 
-    LinkedHashMap<UUID, ActivatedAbility> getUseableActivatedAbilities(MageObject object, Zone zone, Game game);
+    LinkedHashMap<UUID, ActivatedAbility> getPlayableActivatedAbilities(MageObject object, Zone zone, Game game);
 
     boolean addCounters(Counter counter, Game game);
 
diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java
index 9fa114ba30..8957b6905a 100644
--- a/Mage/src/main/java/mage/players/PlayerImpl.java
+++ b/Mage/src/main/java/mage/players/PlayerImpl.java
@@ -1499,7 +1499,7 @@ public abstract class PlayerImpl implements Player, Serializable {
     }
 
     @Override
-    public LinkedHashMap<UUID, ActivatedAbility> getUseableActivatedAbilities(MageObject object, Zone zone, Game game) {
+    public LinkedHashMap<UUID, ActivatedAbility> getPlayableActivatedAbilities(MageObject object, Zone zone, Game game) {
         LinkedHashMap<UUID, ActivatedAbility> useable = new LinkedHashMap<>();
         boolean previousState = game.inCheckPlayableState();
         game.setCheckPlayableState(true);
@@ -1510,12 +1510,33 @@ public abstract class PlayerImpl implements Player, Serializable {
             }
 
             // collect and filter playable activated abilities
-            List<Ability> allPlayable = getPlayable(game, true, zone, false);
-            for (Ability ability : allPlayable) {
-                if (ability instanceof ActivatedAbility) {
-                    if (object.hasAbility(ability, game)) {
-                        useable.putIfAbsent(ability.getId(), (ActivatedAbility) ability);
-                    }
+            // GUI: user clicks on card, but it must activate ability from any card's parts (main, left, right)
+            UUID needId1, needId2, needId3;
+            if (object instanceof SplitCard) {
+                needId1 = object.getId();
+                needId2 = ((SplitCard) object).getLeftHalfCard().getId();
+                needId3 = ((SplitCard) object).getRightHalfCard().getId();
+            } else if (object instanceof AdventureCard) {
+                needId1 = object.getId();
+                needId2 = ((AdventureCard) object).getMainCard().getId();
+                needId3 = ((AdventureCard) object).getSpellCard().getId();
+            } else if (object instanceof AdventureCardSpell) {
+                needId1 = object.getId();
+                needId2 = ((AdventureCardSpell) object).getParentCard().getId();
+                needId3 = object.getId();
+            } else {
+                needId1 = object.getId();
+                needId2 = object.getId();
+                needId3 = object.getId();
+            }
+
+            // workaround to find all abilities first and filter it for one object
+            List<ActivatedAbility> allPlayable = getPlayable(game, true, zone, false);
+            for (ActivatedAbility ability : allPlayable) {
+                if (Objects.equals(ability.getSourceId(), needId1)
+                        || Objects.equals(ability.getSourceId(), needId2)
+                        || Objects.equals(ability.getSourceId(), needId3)) {
+                    useable.putIfAbsent(ability.getId(), ability);
                 }
             }
         } finally {
@@ -1524,58 +1545,6 @@ public abstract class PlayerImpl implements Player, Serializable {
         return useable;
     }
 
-    // Adds special abilities that are given to non permanents by continuous effects
-    private void getOtherUseableActivatedAbilities(MageObject object, Zone zone, Game game, Map<UUID, ActivatedAbility> useable) {
-        Abilities<ActivatedAbility> otherAbilities = game.getState().getActivatedOtherAbilities(object.getId(), zone);
-        if (otherAbilities != null) {
-            boolean canUse = !(object instanceof Permanent)
-                    || ((Permanent) object).canUseActivatedAbilities(game);
-            for (ActivatedAbility ability : otherAbilities) {
-                if (canUse || ability.getAbilityType() == AbilityType.SPECIAL_ACTION) {
-                    Card card = game.getCard(ability.getSourceId());
-                    if (card != null) {
-                        if (card.isSplitCard() && ability instanceof FlashbackAbility) {
-                            FlashbackAbility flashbackAbility;
-                            // Left Half
-                            if (card.isInstant()) {
-                                flashbackAbility = new FlashbackAbility(((SplitCard) card).getLeftHalfCard().getManaCost(),
-                                        TimingRule.INSTANT);
-                            } else {
-                                flashbackAbility = new FlashbackAbility(((SplitCard) card).getLeftHalfCard().getManaCost(),
-                                        TimingRule.SORCERY);
-                            }
-                            flashbackAbility.setSourceId(card.getId());
-                            flashbackAbility.setControllerId(card.getOwnerId());
-                            flashbackAbility.setSpellAbilityType(SpellAbilityType.SPLIT_LEFT);
-                            flashbackAbility.setAbilityName(((SplitCard) card).getLeftHalfCard().getName());
-                            if (flashbackAbility.canActivate(playerId, game).canActivate()) {
-                                useable.put(flashbackAbility.getId(), flashbackAbility);
-                            }
-                            // Right Half
-                            if (card.isInstant()) {
-                                flashbackAbility = new FlashbackAbility(((SplitCard) card).getRightHalfCard().getManaCost(),
-                                        TimingRule.INSTANT);
-                            } else {
-                                flashbackAbility = new FlashbackAbility(((SplitCard) card).getRightHalfCard().getManaCost(),
-                                        TimingRule.SORCERY);
-                            }
-                            flashbackAbility.setSourceId(card.getId());
-                            flashbackAbility.setControllerId(card.getOwnerId());
-                            flashbackAbility.setSpellAbilityType(SpellAbilityType.SPLIT_RIGHT);
-                            flashbackAbility.setAbilityName(((SplitCard) card).getRightHalfCard().getName());
-                            if (flashbackAbility.canActivate(playerId, game).canActivate()) {
-                                useable.put(flashbackAbility.getId(), flashbackAbility);
-                            }
-
-                        } else {
-                            useable.put(ability.getId(), ability);
-                        }
-                    }
-                }
-            }
-        }
-    }
-
     protected LinkedHashMap<UUID, ActivatedManaAbilityImpl> getUseableManaAbilities(MageObject object, Zone zone, Game game) {
         LinkedHashMap<UUID, ActivatedManaAbilityImpl> useable = new LinkedHashMap<>();
         boolean canUse = !(object instanceof Permanent) || ((Permanent) object).canUseActivatedAbilities(game);
@@ -3182,7 +3151,16 @@ public abstract class PlayerImpl implements Player, Serializable {
         return false;
     }
 
-    private void getPlayableFromCardAll(Game game, Zone fromZone, Card card, ManaOptions availableMana, List<Ability> output) {
+    private Abilities<ActivatedAbility> getActivatedOnly(Abilities<Ability> list) {
+        Abilities<ActivatedAbility> res = new AbilitiesImpl<>();
+        list.stream()
+                .filter(a -> a instanceof ActivatedAbility)
+                .map(a -> (ActivatedAbility) a)
+                .forEach(res::add);
+        return res;
+    }
+
+    private void getPlayableFromCardAll(Game game, Zone fromZone, Card card, ManaOptions availableMana, List<ActivatedAbility> output) {
         if (fromZone == null) {
             return;
         }
@@ -3190,41 +3168,22 @@ public abstract class PlayerImpl implements Player, Serializable {
         // BASIC abilities
         if (card instanceof SplitCard) {
             SplitCard splitCard = (SplitCard) card;
-            getPlayableFromCardSingle(game, fromZone, splitCard.getLeftHalfCard(), splitCard.getLeftHalfCard().getAbilities(), availableMana, output);
-            getPlayableFromCardSingle(game, fromZone, splitCard.getRightHalfCard(), splitCard.getRightHalfCard().getAbilities(), availableMana, output);
-            getPlayableFromCardSingle(game, fromZone, splitCard, splitCard.getSharedAbilities(game), availableMana, output);
+            getPlayableFromCardSingle(game, fromZone, splitCard.getLeftHalfCard(), getActivatedOnly(splitCard.getLeftHalfCard().getAbilities(game)), availableMana, output);
+            getPlayableFromCardSingle(game, fromZone, splitCard.getRightHalfCard(), getActivatedOnly(splitCard.getRightHalfCard().getAbilities(game)), availableMana, output);
+            getPlayableFromCardSingle(game, fromZone, splitCard, getActivatedOnly(splitCard.getSharedAbilities(game)), availableMana, output);
         } else if (card instanceof AdventureCard) {
             // adventure must use different card characteristics for different spells (main or adventure)
             AdventureCard adventureCard = (AdventureCard) card;
-            getPlayableFromCardSingle(game, fromZone, adventureCard.getSpellCard(), adventureCard.getSpellCard().getAbilities(), availableMana, output);
-            getPlayableFromCardSingle(game, fromZone, adventureCard, adventureCard.getSharedAbilities(game), availableMana, output);
+            getPlayableFromCardSingle(game, fromZone, adventureCard.getSpellCard(), getActivatedOnly(adventureCard.getSpellCard().getAbilities(game)), availableMana, output);
+            getPlayableFromCardSingle(game, fromZone, adventureCard, getActivatedOnly(adventureCard.getSharedAbilities(game)), availableMana, output);
         } else {
-            getPlayableFromCardSingle(game, fromZone, card, card.getAbilities(game), availableMana, output);
+            getPlayableFromCardSingle(game, fromZone, card, getActivatedOnly(card.getAbilities(game)), availableMana, output);
         }
 
-        // DYNAMIC ADDED abilities
-        if (fromZone != Zone.ALL) { // TODO: test revealed cards with dynamic added abilities
-            // Other activated abilities (added dynamic by effects)
-            LinkedHashMap<UUID, ActivatedAbility> useable;
-            if (card instanceof AdventureCard) {
-                // adventure cards (contains two different cards: main and adventure spell)
-                useable = new LinkedHashMap<>();
-                getOtherUseableActivatedAbilities(((AdventureCard) card).getSpellCard(), fromZone, game, useable);
-                output.addAll(useable.values());
-
-                useable = new LinkedHashMap<>();
-                getOtherUseableActivatedAbilities(card, fromZone, game, useable);
-                output.addAll(useable.values());
-            } else {
-                // all other cards (TODO: check split cards with dynamic added abilities)
-                useable = new LinkedHashMap<>();
-                getOtherUseableActivatedAbilities(card, fromZone, game, useable);
-                output.addAll(useable.values());
-            }
-        }
+        // DYNAMIC ADDED abilities are adds in getAbilities(game)
     }
 
-    private void getPlayableFromCardSingle(Game game, Zone fromZone, Card card, Abilities<Ability> candidateAbilities, ManaOptions availableMana, List<Ability> output) {
+    private void getPlayableFromCardSingle(Game game, Zone fromZone, Card card, Abilities<ActivatedAbility> candidateAbilities, ManaOptions availableMana, List<ActivatedAbility> output) {
         // check "can play" condition as affected controller (BUT play from not own hand zone must be checked as original controller)
         for (ActivatedAbility ability : candidateAbilities.getActivatedAbilities(Zone.ALL)) {
             boolean isPlaySpell = (ability instanceof SpellAbility);
@@ -3300,12 +3259,12 @@ public abstract class PlayerImpl implements Player, Serializable {
     }
 
     @Override
-    public List<Ability> getPlayable(Game game, boolean hidden) {
+    public List<ActivatedAbility> getPlayable(Game game, boolean hidden) {
         return getPlayable(game, hidden, Zone.ALL, true);
     }
 
-    public List<Ability> getPlayable(Game game, boolean hidden, Zone fromZone, boolean hideDuplicatedAbilities) {
-        List<Ability> playable = new ArrayList<>();
+    public List<ActivatedAbility> getPlayable(Game game, boolean hidden, Zone fromZone, boolean hideDuplicatedAbilities) {
+        List<ActivatedAbility> playable = new ArrayList<>();
         if (shouldSkipGettingPlayable(game)) {
             return playable;
         }
@@ -3402,19 +3361,17 @@ public abstract class PlayerImpl implements Player, Serializable {
             }
 
             // eliminate duplicate activated abilities (uses for AI plays)
-            Map<String, Ability> activatedUnique = new HashMap<>();
-            List<Ability> activatedAll = new ArrayList<>();
+            Map<String, ActivatedAbility> activatedUnique = new HashMap<>();
+            List<ActivatedAbility> activatedAll = new ArrayList<>();
 
             // activated abilities from battlefield objects
             if (fromAll || fromZone == Zone.BATTLEFIELD) {
                 for (Permanent permanent : game.getBattlefield().getAllActivePermanents(playerId)) {
-                    List<Ability> battlePlayable = new ArrayList<>();
+                    List<ActivatedAbility> battlePlayable = new ArrayList<>();
                     getPlayableFromCardAll(game, Zone.BATTLEFIELD, permanent, availableMana, battlePlayable);
-                    for (Ability ability : battlePlayable) {
-                        if (ability instanceof ActivatedAbility) {
-                            activatedUnique.putIfAbsent(ability.toString(), ability);
-                            activatedAll.add(ability);
-                        }
+                    for (ActivatedAbility ability : battlePlayable) {
+                        activatedUnique.putIfAbsent(ability.toString(), ability);
+                        activatedAll.add(ability);
                     }
                 }
             }
@@ -3467,7 +3424,7 @@ public abstract class PlayerImpl implements Player, Serializable {
      */
     @Override
     public Map<UUID, Integer> getPlayableObjects(Game game, Zone zone) {
-        List<Ability> playableAbilities = getPlayable(game, true, zone, false); // do not hide duplicated abilities/cards
+        List<ActivatedAbility> playableAbilities = getPlayable(game, true, zone, false); // do not hide duplicated abilities/cards
         Map<UUID, Integer> playableObjects = new HashMap<>();
         for (Ability ability : playableAbilities) {
             if (ability.getSourceId() != null) {
@@ -3518,7 +3475,6 @@ public abstract class PlayerImpl implements Player, Serializable {
     @Override
     public List<Ability> getPlayableOptions(Ability ability, Game game) {
         List<Ability> options = new ArrayList<>();
-
         if (ability.isModal()) {
             addModeOptions(options, ability, game);
         } else if (!ability.getTargets().getUnchosen().isEmpty()) {

From 95075cf33edb095437b7e44a6ced7883c9cd9355 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Thu, 4 Jun 2020 11:24:37 +0400
Subject: [PATCH 089/586] Additional fix flashback and split cards (see
 cce467a5ec8d4679f6118bdcdb15343ae2863f54)

---
 .../cards/abilities/other/PastInFlamesTest.java   | 15 +++++----------
 .../split/CastSplitCardsWithFlashbackTest.java    |  3 +++
 .../mage/abilities/keyword/FlashbackAbility.java  |  7 +++++--
 3 files changed, 13 insertions(+), 12 deletions(-)

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/PastInFlamesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/PastInFlamesTest.java
index 8feb498747..03abb401a3 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/PastInFlamesTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/PastInFlamesTest.java
@@ -1,5 +1,3 @@
-
-
 package org.mage.test.cards.abilities.other;
 
 import mage.constants.PhaseStep;
@@ -8,19 +6,17 @@ import org.junit.Test;
 import org.mage.test.serverside.base.CardTestPlayerBase;
 
 /**
- *
  * @author BetaSteward
  */
 public class PastInFlamesTest extends CardTestPlayerBase {
 
-    /** 
+    /**
      * Past in Flames
      * Sorcery, 3R (4)
-     * Each instant and sorcery card in your graveyard gains flashback until end 
+     * Each instant and sorcery card in your graveyard gains flashback until end
      * of turn. The flashback cost is equal to its mana cost.
-     * Flashback {4}{R} (You may cast this card from your graveyard for its 
+     * Flashback {4}{R} (You may cast this card from your graveyard for its
      * flashback cost. Then exile it.)
-     *
      */
 
     @Test
@@ -31,7 +27,7 @@ public class PastInFlamesTest extends CardTestPlayerBase {
 
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Past in Flames");
         activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Flashback");
-        
+
         setStopAt(1, PhaseStep.END_TURN);
         execute();
 
@@ -40,7 +36,6 @@ public class PastInFlamesTest extends CardTestPlayerBase {
 
         assertExileCount("Lightning Bolt", 1);
         assertGraveyardCount(playerA, "Lightning Bolt", 0);
-
     }
-    
+
 }
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithFlashbackTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithFlashbackTest.java
index 9aa250eb7e..db85f76f92 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithFlashbackTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithFlashbackTest.java
@@ -34,6 +34,7 @@ public class CastSplitCardsWithFlashbackTest extends CardTestPlayerBase {
         assertAllCommandsUsed();
 
         assertLife(playerB, 20 - 3);
+        assertExileCount(playerA, "Lightning Bolt", 1);
     }
 
     @Test
@@ -66,6 +67,7 @@ public class CastSplitCardsWithFlashbackTest extends CardTestPlayerBase {
 
         assertGraveyardCount(playerB, "Bident of Thassa", 1);
         assertPermanentCount(playerB, "Bow of Nylea", 1);
+        assertExileCount(playerA, "Wear // Tear", 1);
     }
 
     @Test
@@ -98,5 +100,6 @@ public class CastSplitCardsWithFlashbackTest extends CardTestPlayerBase {
 
         assertGraveyardCount(playerB, "Bident of Thassa", 1);
         assertPermanentCount(playerB, "Bow of Nylea", 1);
+        assertExileCount(playerA, "Wear // Tear", 1);
     }
 }
diff --git a/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java b/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java
index 980716538d..91143c8978 100644
--- a/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java
@@ -14,6 +14,7 @@ import mage.game.events.GameEvent;
 import mage.game.events.ZoneChangeEvent;
 import mage.players.Player;
 import mage.target.targetpointer.FixedTarget;
+import mage.util.CardUtil;
 
 import java.util.UUID;
 
@@ -53,6 +54,7 @@ public class FlashbackAbility extends SpellAbility {
 
     @Override
     public ActivationStatus canActivate(UUID playerId, Game game) {
+        // flashback ability dynamicly added to all card's parts (split cards)
         if (super.canActivate(playerId, game).canActivate()) {
             Card card = game.getCard(getSourceId());
             if (card != null) {
@@ -210,11 +212,12 @@ class FlashbackReplacementEffect extends ReplacementEffectImpl {
 
     @Override
     public boolean applies(GameEvent event, Ability source, Game game) {
-        if (event.getTargetId().equals(source.getSourceId())
+        UUID cardId = CardUtil.getMainCardId(game, source.getSourceId()); // for split cards
+        if (cardId.equals(event.getTargetId())
                 && ((ZoneChangeEvent) event).getFromZone() == Zone.STACK
                 && ((ZoneChangeEvent) event).getToZone() != Zone.EXILED) {
 
-            int zcc = game.getState().getZoneChangeCounter(source.getSourceId());
+            int zcc = game.getState().getZoneChangeCounter(cardId);
             return ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1 == zcc;
 
         }

From d4dbf33d192d3ab7190d8bce4c633374dc707072 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Thu, 4 Jun 2020 10:57:36 +0200
Subject: [PATCH 090/586] * Some minor code changes.

---
 Mage.Sets/src/mage/cards/o/OrimsPrayer.java   |  3 +--
 .../test/cards/mana/SylvokExplorerTest.java   | 27 +++++--------------
 2 files changed, 8 insertions(+), 22 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/o/OrimsPrayer.java b/Mage.Sets/src/mage/cards/o/OrimsPrayer.java
index 53eb06d5f0..68fd83e320 100644
--- a/Mage.Sets/src/mage/cards/o/OrimsPrayer.java
+++ b/Mage.Sets/src/mage/cards/o/OrimsPrayer.java
@@ -38,8 +38,6 @@ public final class OrimsPrayer extends CardImpl {
 
 class OrimsPrayerTriggeredAbility extends TriggeredAbilityImpl {
 
-    int numberAttackingController = 0;
-
     public OrimsPrayerTriggeredAbility() {
         super(Zone.BATTLEFIELD, null);
     }
@@ -65,6 +63,7 @@ class OrimsPrayerTriggeredAbility extends TriggeredAbilityImpl {
         if (controller == null) {
             return false;
         }
+        int numberAttackingController = 0;
         for (UUID attackersId : game.getCombat().getAttackers()) {
             Permanent attackingCreature = game.getPermanent(attackersId);
             if (attackingCreature != null
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/mana/SylvokExplorerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/mana/SylvokExplorerTest.java
index 7351eba115..7c0d7cc6ff 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/mana/SylvokExplorerTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/mana/SylvokExplorerTest.java
@@ -4,7 +4,6 @@ import mage.abilities.mana.ManaOptions;
 import mage.constants.PhaseStep;
 import mage.constants.Zone;
 import org.junit.Assert;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.mage.test.serverside.base.CardTestPlayerBase;
 
@@ -14,25 +13,7 @@ import org.mage.test.serverside.base.CardTestPlayerBase;
  */
 public class SylvokExplorerTest extends CardTestPlayerBase {
 
-    /**
-     * java.lang.StackOverflowError at
-     * mage.filter.predicate.Predicates.and(Predicates.java:68) at
-     * mage.filter.FilterImpl.match(FilterImpl.java:62) at
-     * mage.filter.FilterPermanent.match(FilterPermanent.java:74) at
-     * mage.game.permanent.Battlefield.getActivePermanents(Battlefield.java:362)
-     * at
-     * mage.abilities.mana.AnyColorLandsProduceManaEffect.getManaTypes(AnyColorLandsProduceManaAbility.java:164)
-     * at
-     * mage.abilities.mana.AnyColorLandsProduceManaEffect.getNetMana(AnyColorLandsProduceManaAbility.java:181)
-     * at
-     * mage.abilities.mana.AnyColorLandsProduceManaAbility.getNetMana(AnyColorLandsProduceManaAbility.java:70)
-     * at
-     * mage.abilities.mana.AnyColorLandsProduceManaEffect.getManaTypes(AnyColorLandsProduceManaAbility.java:170)
-     * at
-     * mage.abilities.mana.AnyColorLandsProduceManaEffect.getNetMana(AnyColorLandsProduceManaAbility.java:181)
-     */
     @Test
-    @Ignore
     public void testOneInstance() {
         addCard(Zone.BATTLEFIELD, playerB, "Island", 1);
         addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1);
@@ -42,7 +23,9 @@ public class SylvokExplorerTest extends CardTestPlayerBase {
         addCard(Zone.BATTLEFIELD, playerA, "Plains", 1);
 
         setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
+        setStrictChooseMode(true);
         execute();
+        assertAllCommandsUsed();
 
         ManaOptions options = playerA.getAvailableManaTest(currentGame);
         Assert.assertEquals("Player should be able to create 1 red and 1 white mana", "{W}{R}", options.get(0).toString());
@@ -51,6 +34,7 @@ public class SylvokExplorerTest extends CardTestPlayerBase {
 
     @Test
     public void testTwoInstances() {
+        // {T}: Add one mana of any color that a land an opponent controls could produce.        
         addCard(Zone.BATTLEFIELD, playerB, "Exotic Orchard", 2);
 
         // {T}: Add one mana of any color that a land an opponent controls could produce.
@@ -58,8 +42,11 @@ public class SylvokExplorerTest extends CardTestPlayerBase {
         addCard(Zone.BATTLEFIELD, playerA, "Plains", 1);
 
         setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
+        
+        setStrictChooseMode(true);
         execute();
-
+        assertAllCommandsUsed();
+        
         ManaOptions options = playerA.getAvailableManaTest(currentGame);
         Assert.assertEquals("Player should be able to create 3 white mana", "{W}{W}{W}", options.get(0).toString());
     }

From 7594c4144cd86d186d83cae610f9a6d1f364b97d Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Thu, 4 Jun 2020 12:25:16 +0200
Subject: [PATCH 091/586] * Ignoring failing test again.

---
 .../test/java/org/mage/test/cards/mana/SylvokExplorerTest.java  | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/mana/SylvokExplorerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/mana/SylvokExplorerTest.java
index 7c0d7cc6ff..e48aeed312 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/mana/SylvokExplorerTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/mana/SylvokExplorerTest.java
@@ -4,6 +4,7 @@ import mage.abilities.mana.ManaOptions;
 import mage.constants.PhaseStep;
 import mage.constants.Zone;
 import org.junit.Assert;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.mage.test.serverside.base.CardTestPlayerBase;
 
@@ -14,6 +15,7 @@ import org.mage.test.serverside.base.CardTestPlayerBase;
 public class SylvokExplorerTest extends CardTestPlayerBase {
 
     @Test
+    @Ignore
     public void testOneInstance() {
         addCard(Zone.BATTLEFIELD, playerB, "Island", 1);
         addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1);

From 5dab330a9d6463a2f7ed7521502b8b35413f6ac7 Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Thu, 4 Jun 2020 11:08:32 -0500
Subject: [PATCH 092/586] - Fixed #6564

---
 Mage.Sets/src/mage/cards/h/HaldanAvidArcanist.java | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/h/HaldanAvidArcanist.java b/Mage.Sets/src/mage/cards/h/HaldanAvidArcanist.java
index 0922bcbf4e..4131e5765b 100644
--- a/Mage.Sets/src/mage/cards/h/HaldanAvidArcanist.java
+++ b/Mage.Sets/src/mage/cards/h/HaldanAvidArcanist.java
@@ -34,7 +34,8 @@ public final class HaldanAvidArcanist extends CardImpl {
         // Partner with Pako, Arcane Retriever
         this.addAbility(new PartnerWithAbility("Pako, Arcane Retriever"));
 
-        // You may play noncreature cards from exile with fetch counters on them if you exiled them, and you may spend mana as though it were mana of any color to cast those spells.
+        // You may play noncreature cards from exile with fetch counters on them if you 
+        // exiled them, and you may spend mana as though it were mana of any color to cast those spells.
         Ability ability = new SimpleStaticAbility(new HaldanAvidArcanistCastFromExileEffect());
         ability.addEffect(new HaldanAvidArcanistSpendAnyManaEffect());
         this.addAbility(ability);
@@ -56,7 +57,9 @@ public final class HaldanAvidArcanist extends CardImpl {
             return false;
         }
         Card card = game.getCard(sourceId);
-        return card != null && !card.isCreature() && card.getCounters(game).containsKey(CounterType.FETCH);
+        return card != null
+                && !card.isCreature()
+                && card.getCounters(game).containsKey(CounterType.FETCH);
     }
 }
 
@@ -110,11 +113,11 @@ class HaldanAvidArcanistSpendAnyManaEffect extends AsThoughEffectImpl implements
 
     @Override
     public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
-        return HaldanAvidArcanist.checkCard(objectId, source, affectedControllerId, game);
+        return true;
     }
 
     @Override
     public ManaType getAsThoughManaType(ManaType manaType, ManaPoolItem mana, UUID affectedControllerId, Ability source, Game game) {
         return mana.getFirstAvailable();
     }
-}
\ No newline at end of file
+}

From f010454cb2a05ff0cf495a2a73252a41074fdadd Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Thu, 4 Jun 2020 14:11:11 -0500
Subject: [PATCH 093/586] - Fixed #6569

---
 Mage.Sets/src/mage/cards/m/MeddlingMage.java | 19 +++++++++++++++----
 1 file changed, 15 insertions(+), 4 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/m/MeddlingMage.java b/Mage.Sets/src/mage/cards/m/MeddlingMage.java
index c2afa5dce5..ebf5f71d3a 100644
--- a/Mage.Sets/src/mage/cards/m/MeddlingMage.java
+++ b/Mage.Sets/src/mage/cards/m/MeddlingMage.java
@@ -8,6 +8,7 @@ import mage.abilities.common.AsEntersBattlefieldAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
 import mage.abilities.effects.common.ChooseACardNameEffect;
+import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
@@ -18,6 +19,7 @@ import mage.constants.Zone;
 import mage.game.Game;
 import mage.game.events.GameEvent;
 import mage.game.events.GameEvent.EventType;
+import mage.game.stack.Spell;
 
 /**
  *
@@ -82,14 +84,23 @@ class MeddlingMageReplacementEffect extends ContinuousRuleModifyingEffectImpl {
 
     @Override
     public boolean checksEventType(GameEvent event, Game game) {
-        return event.getType() == EventType.CAST_SPELL;
+        return event.getType() == EventType.CAST_SPELL_LATE;
     }
 
     @Override
     public boolean applies(GameEvent event, Ability source, Game game) {
+        // Check for Morph spell (no name)
+        Card card = game.getCard(event.getSourceId());
+        if (card != null) {
+            Spell spell = game.getState().getStack().getSpell(event.getSourceId());
+            if (spell != null && spell.isFaceDown(game)) {
+                return false; // Face Down cast spell (Morph creature) has no name
+            }
+        }
         MageObject object = game.getObject(event.getSourceId());
-        // fixes issue #1072
-        return object != null && !object.isCopy() && object.getName().equals(game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY));
-    }
+        return object != null
+                && !object.isCopy()
+                && object.getName().equals(game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY));
 
+    }
 }

From e95ae2675bbf9a906a8bec7959bfe83a753907aa Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Thu, 4 Jun 2020 23:47:50 +0400
Subject: [PATCH 094/586] Fixed different ZCC in split card's parts (flashback
 fix, see 95075cf33edb095437b7e44a6ced7883c9cd9355); Improve moveToZone code
 and fixed some cards with wrong commands queue (e.g. directly removes card
 from zone and then calls moveToZone again);

---
 Mage.Sets/src/mage/cards/c/ChandraAblaze.java |  1 -
 Mage.Sets/src/mage/cards/d/DarkRevenant.java  |  1 -
 .../src/mage/cards/d/DwellOnThePast.java      |  1 -
 Mage.Sets/src/mage/cards/g/GaeasBlessing.java |  1 -
 .../src/mage/cards/g/GraveyardShovel.java     |  1 -
 .../src/mage/cards/k/KrosanReclamation.java   |  1 -
 .../src/mage/cards/m/MemorysJourney.java      |  1 -
 .../src/mage/cards/s/SereneRemembrance.java   |  1 -
 .../src/mage/cards/s/SpellweaverVolute.java   |  2 +-
 .../mage/cards/s/StreamOfConsciousness.java   |  1 -
 Mage.Sets/src/mage/cards/u/UndyingBeast.java  |  1 -
 .../CastSplitCardsWithFlashbackTest.java      | 37 +++++++++++++++++++
 .../base/impl/CardTestPlayerAPIImpl.java      |  5 +--
 .../common/PutOnLibrarySourceEffect.java      |  1 -
 .../main/java/mage/cards/AdventureCard.java   | 22 ++++++++++-
 Mage/src/main/java/mage/cards/CardImpl.java   |  6 ++-
 Mage/src/main/java/mage/cards/Cards.java      |  2 +-
 Mage/src/main/java/mage/cards/CardsImpl.java  |  6 +--
 Mage/src/main/java/mage/cards/MeldCard.java   | 22 +++++++++--
 Mage/src/main/java/mage/cards/SplitCard.java  | 34 ++++++++++++-----
 .../main/java/mage/players/PlayerImpl.java    |  5 ++-
 21 files changed, 116 insertions(+), 36 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/c/ChandraAblaze.java b/Mage.Sets/src/mage/cards/c/ChandraAblaze.java
index 803758c703..4128d0687f 100644
--- a/Mage.Sets/src/mage/cards/c/ChandraAblaze.java
+++ b/Mage.Sets/src/mage/cards/c/ChandraAblaze.java
@@ -172,7 +172,6 @@ class ChandraAblazeEffect5 extends OneShotEffect {
                     Card card = game.getCard(target.getFirstTarget());
                     if (card != null) {
                         player.cast(card.getSpellAbility(), game, true, new MageObjectReference(source.getSourceObject(game), game));
-                        player.getGraveyard().remove(card);
                         cards.remove(card);
                     }
                 }
diff --git a/Mage.Sets/src/mage/cards/d/DarkRevenant.java b/Mage.Sets/src/mage/cards/d/DarkRevenant.java
index 6741b40a2b..8f42634f7e 100644
--- a/Mage.Sets/src/mage/cards/d/DarkRevenant.java
+++ b/Mage.Sets/src/mage/cards/d/DarkRevenant.java
@@ -69,7 +69,6 @@ class DarkRevenantEffect extends OneShotEffect {
         if (card != null && game.getState().getZone(source.getSourceId()) == Zone.GRAVEYARD) {
             Player owner = game.getPlayer(card.getOwnerId());
             if(owner != null) {
-                owner.getGraveyard().remove(card);
                 return card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true);
             }
         }
diff --git a/Mage.Sets/src/mage/cards/d/DwellOnThePast.java b/Mage.Sets/src/mage/cards/d/DwellOnThePast.java
index efa62fddb7..57164964d9 100644
--- a/Mage.Sets/src/mage/cards/d/DwellOnThePast.java
+++ b/Mage.Sets/src/mage/cards/d/DwellOnThePast.java
@@ -69,7 +69,6 @@ class DwellOnThePastEffect extends OneShotEffect {
                 Card card = game.getCard(targetId);
                 if (card != null) {
                     if (player.getGraveyard().contains(card.getId())) {
-                        player.getGraveyard().remove(card);
                         card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true);
                         shuffle = true;
                     }
diff --git a/Mage.Sets/src/mage/cards/g/GaeasBlessing.java b/Mage.Sets/src/mage/cards/g/GaeasBlessing.java
index 62b9516c83..8f617b4bc2 100644
--- a/Mage.Sets/src/mage/cards/g/GaeasBlessing.java
+++ b/Mage.Sets/src/mage/cards/g/GaeasBlessing.java
@@ -76,7 +76,6 @@ class GaeasBlessingEffect extends OneShotEffect {
                 Card card = game.getCard(targetId);
                 if (card != null) {
                     if (player.getGraveyard().contains(card.getId())) {
-                        player.getGraveyard().remove(card);
                         card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true);
                         shuffle = true;
                     }
diff --git a/Mage.Sets/src/mage/cards/g/GraveyardShovel.java b/Mage.Sets/src/mage/cards/g/GraveyardShovel.java
index b19130c100..9e348846c1 100644
--- a/Mage.Sets/src/mage/cards/g/GraveyardShovel.java
+++ b/Mage.Sets/src/mage/cards/g/GraveyardShovel.java
@@ -69,7 +69,6 @@ class GraveyardShovelEffect extends OneShotEffect {
             if (targetPlayer.chooseTarget(Outcome.Exile, target, source, game)) {
                 Card card = game.getCard(target.getFirstTarget());
                 if (card != null) {
-                    targetPlayer.getGraveyard().remove(card);
                     card.moveToExile(null, "", source.getSourceId(), game);
                     if (card.isCreature()) {
                         controller.gainLife(2, game, source);
diff --git a/Mage.Sets/src/mage/cards/k/KrosanReclamation.java b/Mage.Sets/src/mage/cards/k/KrosanReclamation.java
index 73e848177c..2ae901a246 100644
--- a/Mage.Sets/src/mage/cards/k/KrosanReclamation.java
+++ b/Mage.Sets/src/mage/cards/k/KrosanReclamation.java
@@ -74,7 +74,6 @@ class KrosanReclamationEffect extends OneShotEffect {
                 Card card = game.getCard(targetId);
                 if (card != null) {
                     if (player.getGraveyard().contains(card.getId())) {
-                        player.getGraveyard().remove(card);
                         card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true);
                         shuffle = true;
                     }
diff --git a/Mage.Sets/src/mage/cards/m/MemorysJourney.java b/Mage.Sets/src/mage/cards/m/MemorysJourney.java
index a54963a803..71b1f58fc3 100644
--- a/Mage.Sets/src/mage/cards/m/MemorysJourney.java
+++ b/Mage.Sets/src/mage/cards/m/MemorysJourney.java
@@ -73,7 +73,6 @@ class MemorysJourneyEffect extends OneShotEffect {
                 Card card = game.getCard(targetId);
                 if (card != null) {
                     if (player.getGraveyard().contains(card.getId())) {
-                        player.getGraveyard().remove(card);
                         card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true);
                         shuffle = true;
                     }
diff --git a/Mage.Sets/src/mage/cards/s/SereneRemembrance.java b/Mage.Sets/src/mage/cards/s/SereneRemembrance.java
index b805b2f3b5..441fd9d807 100644
--- a/Mage.Sets/src/mage/cards/s/SereneRemembrance.java
+++ b/Mage.Sets/src/mage/cards/s/SereneRemembrance.java
@@ -68,7 +68,6 @@ class SereneRemembranceEffect extends OneShotEffect {
                 for (Player player : game.getPlayers().values()) {
                     if (player.getGraveyard().contains(card.getId())) {
                         graveyardPlayer = player;
-                        player.getGraveyard().remove(card);
                         result |= card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true);
                     }
                 }
diff --git a/Mage.Sets/src/mage/cards/s/SpellweaverVolute.java b/Mage.Sets/src/mage/cards/s/SpellweaverVolute.java
index 971f423f3c..7f6b4fd2d0 100644
--- a/Mage.Sets/src/mage/cards/s/SpellweaverVolute.java
+++ b/Mage.Sets/src/mage/cards/s/SpellweaverVolute.java
@@ -90,7 +90,7 @@ class SpellweaverVoluteEffect extends OneShotEffect {
                             && controller.chooseUse(Outcome.Copy, "Create a copy of " + enchantedCard.getName() + '?', source, game)) {
                         Card copiedCard = game.copyCard(enchantedCard, source, source.getControllerId());
                         if (copiedCard != null) {
-                            ownerEnchanted.getGraveyard().add(copiedCard);
+                            controller.getGraveyard().add(copiedCard);
                             game.getState().setZone(copiedCard.getId(), Zone.GRAVEYARD);
                             if (controller.chooseUse(Outcome.PlayForFree, "Cast the copied card without paying mana cost?", source, game)) {
                                 if (copiedCard.getSpellAbility() != null) {
diff --git a/Mage.Sets/src/mage/cards/s/StreamOfConsciousness.java b/Mage.Sets/src/mage/cards/s/StreamOfConsciousness.java
index 24ac6e0dc4..ed7fd756ed 100644
--- a/Mage.Sets/src/mage/cards/s/StreamOfConsciousness.java
+++ b/Mage.Sets/src/mage/cards/s/StreamOfConsciousness.java
@@ -72,7 +72,6 @@ class StreamOfConsciousnessEffect extends OneShotEffect {
                 Card card = game.getCard(targetId);
                 if (card != null) {
                     if (player.getGraveyard().contains(card.getId())) {
-                        player.getGraveyard().remove(card);
                         card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true);
                         shuffle = true;
                     }
diff --git a/Mage.Sets/src/mage/cards/u/UndyingBeast.java b/Mage.Sets/src/mage/cards/u/UndyingBeast.java
index 20bf25c5a6..8dad549bf4 100644
--- a/Mage.Sets/src/mage/cards/u/UndyingBeast.java
+++ b/Mage.Sets/src/mage/cards/u/UndyingBeast.java
@@ -64,7 +64,6 @@ class UndyingBeastEffect extends OneShotEffect {
         if (card != null && game.getState().getZone(source.getSourceId()) == Zone.GRAVEYARD) {
             Player owner = game.getPlayer(card.getOwnerId());
             if(owner != null) {
-                owner.getGraveyard().remove(card);
                 return card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true);
             }
         }
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithFlashbackTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithFlashbackTest.java
index db85f76f92..9c68cd0f05 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithFlashbackTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithFlashbackTest.java
@@ -70,6 +70,43 @@ public class CastSplitCardsWithFlashbackTest extends CardTestPlayerBase {
         assertExileCount(playerA, "Wear // Tear", 1);
     }
 
+    @Test
+    public void test_Flashback_SplitLeft_ZCCChanged() {
+        // {1}{U}
+        // When Snapcaster Mage enters the battlefield, target instant or sorcery card in your graveyard gains flashback until end of turn.
+        addCard(Zone.HAND, playerA, "Snapcaster Mage", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
+        //
+        // Wear {1}{R} Destroy target artifact.
+        // Tear {W} Destroy target enchantment.
+        addCard(Zone.HAND, playerA, "Wear // Tear", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2 + 2); // for first Wear cast
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
+        addCard(Zone.BATTLEFIELD, playerB, "Bident of Thassa", 1); // Legendary Enchantment Artifact
+        addCard(Zone.BATTLEFIELD, playerB, "Bow of Nylea", 1); // Legendary Enchantment Artifact
+
+        // play card as normal to change ZCC for split cards (simulate GUI session - ZCC's was bugged and card's parts was able to have different ZCC)
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wear", "Bow of Nylea");
+
+        // add flashback
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Snapcaster Mage");
+        addTarget(playerA, "Wear // Tear");
+
+        // cast as flashback
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Flashback {1}{R}", "Bident of Thassa");
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        assertGraveyardCount(playerB, "Bident of Thassa", 1);
+        assertGraveyardCount(playerB, "Bow of Nylea", 1);
+        assertExileCount(playerA, "Wear // Tear", 1);
+    }
+
     @Test
     public void test_Flashback_SplitRight() {
         // {1}{U}
diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
index 6a7250afee..5cc971d404 100644
--- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
+++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
@@ -1601,13 +1601,12 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     }
 
     /**
-     * 
      * @param turnNum
      * @param step
      * @param player
      * @param ability
-     * @param targetName  use NO_TARGET if there is no target to set
-     * @param spellOnStack 
+     * @param targetName   use NO_TARGET if there is no target to set
+     * @param spellOnStack
      */
     public void activateAbility(int turnNum, PhaseStep step, TestPlayer player, String ability, String targetName, String spellOnStack) {
         // TODO: it's uses computerPlayer to execute, only ability target will work, but choices and targets commands aren't
diff --git a/Mage/src/main/java/mage/abilities/effects/common/PutOnLibrarySourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PutOnLibrarySourceEffect.java
index cd7f43bddb..28e5b10ba5 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/PutOnLibrarySourceEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/PutOnLibrarySourceEffect.java
@@ -54,7 +54,6 @@ public class PutOnLibrarySourceEffect extends OneShotEffect {
         } else if (sourceObject instanceof Card && game.getState().getZone(source.getSourceId()) == Zone.GRAVEYARD) {
             for (Player player : game.getPlayers().values()) {
                 if (player.getGraveyard().contains(sourceObject.getId())) {
-                    player.getGraveyard().remove(((Card) sourceObject));
                     ((Card) sourceObject).moveToZone(Zone.LIBRARY, source.getSourceId(), game, onTop);
                     return true;
                 }
diff --git a/Mage/src/main/java/mage/cards/AdventureCard.java b/Mage/src/main/java/mage/cards/AdventureCard.java
index 1cb62d410a..1fc792311e 100644
--- a/Mage/src/main/java/mage/cards/AdventureCard.java
+++ b/Mage/src/main/java/mage/cards/AdventureCard.java
@@ -1,7 +1,5 @@
 package mage.cards;
 
-import java.util.List;
-import java.util.UUID;
 import mage.abilities.Abilities;
 import mage.abilities.AbilitiesImpl;
 import mage.abilities.Ability;
@@ -9,6 +7,10 @@ import mage.abilities.SpellAbility;
 import mage.constants.CardType;
 import mage.constants.Zone;
 import mage.game.Game;
+import mage.game.events.ZoneChangeEvent;
+
+import java.util.List;
+import java.util.UUID;
 
 /**
  * @author TheElk801
@@ -64,6 +66,22 @@ public abstract class AdventureCard extends CardImpl {
         return false;
     }
 
+    @Override
+    public boolean removeFromZone(Game game, Zone fromZone, UUID sourceId) {
+        // zone contains only one main card
+        return super.removeFromZone(game, fromZone, sourceId);
+    }
+
+    @Override
+    public void updateZoneChangeCounter(Game game, ZoneChangeEvent event) {
+        if (isCopy()) { // same as meld cards
+            super.updateZoneChangeCounter(game, event);
+            return;
+        }
+        super.updateZoneChangeCounter(game, event);
+        getSpellCard().updateZoneChangeCounter(game, event);
+    }
+
     @Override
     public boolean cast(Game game, Zone fromZone, SpellAbility ability, UUID controllerId) {
         switch (ability.getSpellAbilityType()) {
diff --git a/Mage/src/main/java/mage/cards/CardImpl.java b/Mage/src/main/java/mage/cards/CardImpl.java
index b968fbc342..b9a1b37f0e 100644
--- a/Mage/src/main/java/mage/cards/CardImpl.java
+++ b/Mage/src/main/java/mage/cards/CardImpl.java
@@ -523,7 +523,6 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
             case EXILED:
                 if (game.getExile().getCard(getId(), game) != null) {
                     removed = game.getExile().removeCard(this, game);
-
                 }
                 break;
             case STACK:
@@ -533,15 +532,18 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
                 } else {
                     stackObject = game.getStack().getSpell(this.getId(), false);
                 }
+
                 if (stackObject == null && (this instanceof SplitCard)) { // handle if half of Split cast is on the stack
                     stackObject = game.getStack().getSpell(((SplitCard) this).getLeftHalfCard().getId(), false);
                     if (stackObject == null) {
                         stackObject = game.getStack().getSpell(((SplitCard) this).getRightHalfCard().getId(), false);
                     }
                 }
+
                 if (stackObject == null && (this instanceof AdventureCard)) {
                     stackObject = game.getStack().getSpell(((AdventureCard) this).getSpellCard().getId(), false);
                 }
+
                 if (stackObject == null) {
                     stackObject = game.getStack().getSpell(getId(), false);
                 }
@@ -589,6 +591,8 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
             }
         } else {
             logger.warn("Couldn't find card in fromZone, card=" + getIdName() + ", fromZone=" + fromZone);
+            // possible reason: you to remove card from wrong zone or card already removed,
+            // e.g. you added copy card to wrong graveyard (see owner) or removed card from graveyard before moveToZone call
         }
         return removed;
     }
diff --git a/Mage/src/main/java/mage/cards/Cards.java b/Mage/src/main/java/mage/cards/Cards.java
index 4ffe0c228f..a6c6a21efc 100644
--- a/Mage/src/main/java/mage/cards/Cards.java
+++ b/Mage/src/main/java/mage/cards/Cards.java
@@ -15,7 +15,7 @@ public interface Cards extends Set<UUID>, Serializable {
 
     Card get(UUID cardId, Game game);
 
-    void remove(Card card);
+    boolean remove(Card card);
 
     void setOwner(UUID ownerId, Game game);
 
diff --git a/Mage/src/main/java/mage/cards/CardsImpl.java b/Mage/src/main/java/mage/cards/CardsImpl.java
index 250bb3f3b8..ed69e8ea2f 100644
--- a/Mage/src/main/java/mage/cards/CardsImpl.java
+++ b/Mage/src/main/java/mage/cards/CardsImpl.java
@@ -64,11 +64,11 @@ public class CardsImpl extends LinkedHashSet<UUID> implements Cards, Serializabl
     }
 
     @Override
-    public void remove(Card card) {
+    public boolean remove(Card card) {
         if (card == null) {
-            return;
+            return false;
         }
-        this.remove(card.getId());
+        return this.remove(card.getId());
     }
 
     @Override
diff --git a/Mage/src/main/java/mage/cards/MeldCard.java b/Mage/src/main/java/mage/cards/MeldCard.java
index af29691a26..6112d24e0f 100644
--- a/Mage/src/main/java/mage/cards/MeldCard.java
+++ b/Mage/src/main/java/mage/cards/MeldCard.java
@@ -1,4 +1,3 @@
-
 package mage.cards;
 
 import mage.abilities.Ability;
@@ -13,7 +12,6 @@ import java.util.List;
 import java.util.UUID;
 
 /**
- *
  * @author emerald000
  */
 public abstract class MeldCard extends CardImpl {
@@ -142,6 +140,24 @@ public abstract class MeldCard extends CardImpl {
         return value;
     }
 
+    @Override
+    public boolean moveToZone(Zone toZone, UUID sourceId, Game game, boolean flag, List<UUID> appliedEffects) {
+        // TODO: missing override method for meld cards? See removeFromZone, updateZoneChangeCounter, etc
+        return super.moveToZone(toZone, sourceId, game, flag, appliedEffects);
+    }
+
+    @Override
+    public void setZone(Zone zone, Game game) {
+        // TODO: missing override method for meld cards? See removeFromZone, updateZoneChangeCounter, etc
+        super.setZone(zone, game);
+    }
+
+    @Override
+    public boolean moveToExile(UUID exileId, String name, UUID sourceId, Game game, List<UUID> appliedEffects) {
+        // TODO: missing override method for meld cards? See removeFromZone, updateZoneChangeCounter, etc
+        return super.moveToExile(exileId, name, sourceId, game, appliedEffects);
+    }
+
     @Override
     public boolean removeFromZone(Game game, Zone fromZone, UUID sourceId) {
         if (isCopy()) {
@@ -170,7 +186,7 @@ public abstract class MeldCard extends CardImpl {
             super.updateZoneChangeCounter(game, event);
             return;
         }
-        game.getState().updateZoneChangeCounter(objectId);
+        super.updateZoneChangeCounter(game, event);
         if (topLastZoneChangeCounter == topHalfCard.getZoneChangeCounter(game)
                 && halves.contains(topHalfCard.getId())) {
             topHalfCard.updateZoneChangeCounter(game, event);
diff --git a/Mage/src/main/java/mage/cards/SplitCard.java b/Mage/src/main/java/mage/cards/SplitCard.java
index 4205f05158..936339bf64 100644
--- a/Mage/src/main/java/mage/cards/SplitCard.java
+++ b/Mage/src/main/java/mage/cards/SplitCard.java
@@ -9,6 +9,7 @@ import mage.constants.CardType;
 import mage.constants.SpellAbilityType;
 import mage.constants.Zone;
 import mage.game.Game;
+import mage.game.events.ZoneChangeEvent;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -74,6 +75,13 @@ public abstract class SplitCard extends CardImpl {
         return false;
     }
 
+    @Override
+    public void setZone(Zone zone, Game game) {
+        super.setZone(zone, game);
+        game.setZone(getLeftHalfCard().getId(), zone);
+        game.setZone(getRightHalfCard().getId(), zone);
+    }
+
     @Override
     public boolean moveToExile(UUID exileId, String name, UUID sourceId, Game game, List<UUID> appliedEffects) {
         if (super.moveToExile(exileId, name, sourceId, game, appliedEffects)) {
@@ -85,6 +93,23 @@ public abstract class SplitCard extends CardImpl {
         return false;
     }
 
+    @Override
+    public boolean removeFromZone(Game game, Zone fromZone, UUID sourceId) {
+        // zone contains only one main card
+        return super.removeFromZone(game, fromZone, sourceId);
+    }
+
+    @Override
+    public void updateZoneChangeCounter(Game game, ZoneChangeEvent event) {
+        if (isCopy()) { // same as meld cards
+            super.updateZoneChangeCounter(game, event);
+            return;
+        }
+        super.updateZoneChangeCounter(game, event);
+        getLeftHalfCard().updateZoneChangeCounter(game, event);
+        getRightHalfCard().updateZoneChangeCounter(game, event);
+    }
+
     @Override
     public boolean cast(Game game, Zone fromZone, SpellAbility ability, UUID controllerId) {
         switch (ability.getSpellAbilityType()) {
@@ -99,13 +124,6 @@ public abstract class SplitCard extends CardImpl {
         }
     }
 
-    @Override
-    public void setZone(Zone zone, Game game) {
-        super.setZone(zone, game);
-        game.setZone(getLeftHalfCard().getId(), zone);
-        game.setZone(getRightHalfCard().getId(), zone);
-    }
-
     @Override
     public Abilities<Ability> getAbilities() {
         Abilities<Ability> allAbilites = new AbilitiesImpl<>();
@@ -168,7 +186,5 @@ public abstract class SplitCard extends CardImpl {
         leftHalfCard.setOwnerId(ownerId);
         rightHalfCard.getAbilities().setControllerId(ownerId);
         rightHalfCard.setOwnerId(ownerId);
-
     }
-
 }
diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java
index 8957b6905a..95d6c8b320 100644
--- a/Mage/src/main/java/mage/players/PlayerImpl.java
+++ b/Mage/src/main/java/mage/players/PlayerImpl.java
@@ -691,6 +691,8 @@ public abstract class PlayerImpl implements Player, Serializable {
             return false;
         }
         library.remove(card.getId(), game);
+        // must return true all the time (some cards can be removed directly from library, see getLibrary().removeFromTop)
+        // TODO: replace removeFromTop logic to normal with moveToZone
         return true;
     }
 
@@ -919,8 +921,7 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean removeFromGraveyard(Card card, Game game) {
-        this.graveyard.remove(card);
-        return true;
+        return this.graveyard.remove(card);
     }
 
     @Override

From 4b77cb0fa8d63e4f1964610460504af5e06ed146 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Fri, 5 Jun 2020 00:06:57 +0400
Subject: [PATCH 095/586] * Name card abilities - fixed that it can't find
 split cards or fused spells with entered name (part of #6603, #6549);

---
 Mage.Sets/src/mage/cards/b/BileBlight.java    |   2 +-
 Mage.Sets/src/mage/cards/b/BrainPry.java      |   2 +-
 .../src/mage/cards/c/CabalTherapist.java      |  13 +-
 Mage.Sets/src/mage/cards/c/CabalTherapy.java  |  13 +-
 .../src/mage/cards/c/ConundrumSphinx.java     |   2 +-
 Mage.Sets/src/mage/cards/c/CorrosiveOoze.java |   4 +-
 .../mage/cards/c/CouncilOfTheAbsolute.java    |   4 +-
 .../src/mage/cards/c/CrownOfEmpires.java      |   4 +-
 Mage.Sets/src/mage/cards/c/CursedScroll.java  |   2 +-
 .../src/mage/cards/d/DeathbellowWarCry.java   |   2 +-
 .../src/mage/cards/d/DementiaSliver.java      |   2 +-
 Mage.Sets/src/mage/cards/d/Denied.java        |   2 +-
 .../src/mage/cards/d/DetentionSphere.java     |   2 +-
 Mage.Sets/src/mage/cards/d/DiviningWitch.java |   2 +-
 Mage.Sets/src/mage/cards/f/Foreshadow.java    |   2 +-
 .../src/mage/cards/l/LammastideWeave.java     |   2 +-
 Mage.Sets/src/mage/cards/l/LiarsPendulum.java |   2 +-
 .../src/mage/cards/m/MagusOfTheScroll.java    |   2 +-
 Mage.Sets/src/mage/cards/p/PetraSphinx.java   |   2 +-
 Mage.Sets/src/mage/cards/p/Predict.java       |   2 +-
 Mage.Sets/src/mage/cards/r/ReflectorMage.java |   2 +-
 Mage.Sets/src/mage/cards/s/SearchTheCity.java |   2 +-
 .../src/mage/cards/s/SpoilsOfTheVault.java    |   2 +-
 .../src/mage/cards/t/ThoughtHemorrhage.java   |   2 +-
 Mage.Sets/src/mage/cards/t/TunnelVision.java  |   2 +-
 Mage.Sets/src/mage/cards/v/VexingArcanix.java |   2 +-
 ...astSplitCardsWithCostModificationTest.java | 201 ++++++++++++++++++
 .../org/mage/test/testapi/TestAliases.java    |  14 ++
 .../predicate/mageobject/NamePredicate.java   |   6 +-
 Mage/src/main/java/mage/util/CardUtil.java    |   9 +
 30 files changed, 257 insertions(+), 53 deletions(-)
 create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithCostModificationTest.java

diff --git a/Mage.Sets/src/mage/cards/b/BileBlight.java b/Mage.Sets/src/mage/cards/b/BileBlight.java
index 0b23756190..77b3e4f0fb 100644
--- a/Mage.Sets/src/mage/cards/b/BileBlight.java
+++ b/Mage.Sets/src/mage/cards/b/BileBlight.java
@@ -61,7 +61,7 @@ class BileBlightEffect extends BoostAllEffect {
                 } else {
                     String name = target.getName();
                     for (Permanent perm : game.getBattlefield().getActivePermanents(source.getControllerId(), game)) {
-                        if (CardUtil.haveSameNames(perm.getName(), name)) {
+                        if (CardUtil.haveSameNames(perm, name, game)) {
                             affectedObjectList.add(new MageObjectReference(perm, game));
                         }
                     }
diff --git a/Mage.Sets/src/mage/cards/b/BrainPry.java b/Mage.Sets/src/mage/cards/b/BrainPry.java
index 555d1368c1..8704271ed8 100644
--- a/Mage.Sets/src/mage/cards/b/BrainPry.java
+++ b/Mage.Sets/src/mage/cards/b/BrainPry.java
@@ -60,7 +60,7 @@ class BrainPryEffect extends OneShotEffect {
         if (targetPlayer != null && controller != null && sourceObject != null && cardName != null) {
             boolean hasDiscarded = false;
             for (Card card : targetPlayer.getHand().getCards(game)) {
-                if (CardUtil.haveSameNames(card.getName(), cardName)) {
+                if (CardUtil.haveSameNames(card, cardName, game)) {
                     targetPlayer.discard(card, source, game);
                     hasDiscarded = true;
                     break;
diff --git a/Mage.Sets/src/mage/cards/c/CabalTherapist.java b/Mage.Sets/src/mage/cards/c/CabalTherapist.java
index 53d044bd86..66ed076a80 100644
--- a/Mage.Sets/src/mage/cards/c/CabalTherapist.java
+++ b/Mage.Sets/src/mage/cards/c/CabalTherapist.java
@@ -90,18 +90,7 @@ class CabalTherapistDiscardEffect extends OneShotEffect {
             if (card == null) {
                 return true;
             }
-            if (card.isSplitCard()) {
-                SplitCard splitCard = (SplitCard) card;
-                if (CardUtil.haveSameNames(splitCard.getLeftHalfCard().getName(), cardName)) {
-                    return false;
-                } else if (CardUtil.haveSameNames(splitCard.getRightHalfCard().getName(), cardName)) {
-                    return false;
-                }
-            }
-            if (CardUtil.haveSameNames(card.getName(), cardName)) {
-                return false;
-            }
-            return true;
+            return !CardUtil.haveSameNames(card, cardName, game);
         });
         targetPlayer.discard(hand, source, game);
         return true;
diff --git a/Mage.Sets/src/mage/cards/c/CabalTherapy.java b/Mage.Sets/src/mage/cards/c/CabalTherapy.java
index 6aad9ff861..0a25b54b9c 100644
--- a/Mage.Sets/src/mage/cards/c/CabalTherapy.java
+++ b/Mage.Sets/src/mage/cards/c/CabalTherapy.java
@@ -75,18 +75,7 @@ class CabalTherapyEffect extends OneShotEffect {
             if (card == null) {
                 return true;
             }
-            if (card.isSplitCard()) {
-                SplitCard splitCard = (SplitCard) card;
-                if (CardUtil.haveSameNames(splitCard.getLeftHalfCard().getName(), cardName)) {
-                    return false;
-                } else if (CardUtil.haveSameNames(splitCard.getRightHalfCard().getName(), cardName)) {
-                    return false;
-                }
-            }
-            if (CardUtil.haveSameNames(card.getName(), cardName)) {
-                return false;
-            }
-            return true;
+            return !CardUtil.haveSameNames(card, cardName, game);
         });
         targetPlayer.discard(hand, source, game);
         return true;
diff --git a/Mage.Sets/src/mage/cards/c/ConundrumSphinx.java b/Mage.Sets/src/mage/cards/c/ConundrumSphinx.java
index 6baed0ceb0..4bddff8c30 100644
--- a/Mage.Sets/src/mage/cards/c/ConundrumSphinx.java
+++ b/Mage.Sets/src/mage/cards/c/ConundrumSphinx.java
@@ -85,7 +85,7 @@ class ConundrumSphinxEffect extends OneShotEffect {
                         if (card != null) {
                             Cards cards = new CardsImpl(card);
                             player.revealCards(source, player.getName(), cards, game);
-                            if (CardUtil.haveSameNames(card.getName(), cardName)) {
+                            if (CardUtil.haveSameNames(card, cardName, game)) {
                                 player.moveCards(cards, Zone.HAND, source, game);
                             } else {
                                 player.putCardsOnBottomOfLibrary(cards, game, source, false);
diff --git a/Mage.Sets/src/mage/cards/c/CorrosiveOoze.java b/Mage.Sets/src/mage/cards/c/CorrosiveOoze.java
index d6845d3243..543b50c756 100644
--- a/Mage.Sets/src/mage/cards/c/CorrosiveOoze.java
+++ b/Mage.Sets/src/mage/cards/c/CorrosiveOoze.java
@@ -133,7 +133,7 @@ class CorrosiveOozeCombatWatcher extends Watcher {
         if (event.getType() == GameEvent.EventType.BLOCKER_DECLARED) {
             Permanent attacker = game.getPermanent(event.getTargetId());
             Permanent blocker = game.getPermanent(event.getSourceId());
-            if (attacker != null && CardUtil.haveSameNames(attacker.getName(), "Corrosive Ooze")) { // To check for name is not working if Ooze is copied but name changed
+            if (attacker != null && CardUtil.haveSameNames(attacker, "Corrosive Ooze", game)) { // To check for name is not working if Ooze is copied but name changed
                 if (blocker != null && hasAttachedEquipment(game, blocker)) {
                     MageObjectReference oozeMor = new MageObjectReference(attacker, game);
                     Set<MageObjectReference> relatedCreatures = oozeBlocksOrBlocked.getOrDefault(oozeMor, new HashSet<>());
@@ -141,7 +141,7 @@ class CorrosiveOozeCombatWatcher extends Watcher {
                     oozeBlocksOrBlocked.put(oozeMor, relatedCreatures);
                 }
             }
-            if (blocker != null && CardUtil.haveSameNames(blocker.getName(), "Corrosive Ooze")) {
+            if (blocker != null && CardUtil.haveSameNames(blocker, "Corrosive Ooze", game)) {
                 if (attacker != null && hasAttachedEquipment(game, attacker)) {
                     MageObjectReference oozeMor = new MageObjectReference(blocker, game);
                     Set<MageObjectReference> relatedCreatures = oozeBlocksOrBlocked.getOrDefault(oozeMor, new HashSet<>());
diff --git a/Mage.Sets/src/mage/cards/c/CouncilOfTheAbsolute.java b/Mage.Sets/src/mage/cards/c/CouncilOfTheAbsolute.java
index a6e289d8f4..90fd017f89 100644
--- a/Mage.Sets/src/mage/cards/c/CouncilOfTheAbsolute.java
+++ b/Mage.Sets/src/mage/cards/c/CouncilOfTheAbsolute.java
@@ -93,7 +93,7 @@ class CouncilOfTheAbsoluteReplacementEffect extends ContinuousRuleModifyingEffec
         if (game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) {
             MageObject object = game.getObject(event.getSourceId());
             String needName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY);
-            return object != null && CardUtil.haveSameNames(object.getName(), needName);
+            return object != null && CardUtil.haveSameNames(object, needName, game);
         }
         return false;
     }
@@ -123,7 +123,7 @@ class CouncilOfTheAbsoluteCostReductionEffect extends CostModificationEffectImpl
             Card card = game.getCard(abilityToModify.getSourceId());
             if (card != null) {
                 String needName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY);
-                return CardUtil.haveSameNames(card.getName(), needName);
+                return CardUtil.haveSameNames(card, needName, game);
             }
         }
         return false;
diff --git a/Mage.Sets/src/mage/cards/c/CrownOfEmpires.java b/Mage.Sets/src/mage/cards/c/CrownOfEmpires.java
index f5bfddfe29..9089c535cf 100644
--- a/Mage.Sets/src/mage/cards/c/CrownOfEmpires.java
+++ b/Mage.Sets/src/mage/cards/c/CrownOfEmpires.java
@@ -61,9 +61,9 @@ class CrownOfEmpiresEffect extends OneShotEffect {
         boolean scepter = false;
         boolean throne = false;
         for (Permanent permanent : game.getBattlefield().getAllActivePermanents(source.getControllerId())) {
-            if (CardUtil.haveSameNames(permanent.getName(), "Scepter of Empires")) {
+            if (CardUtil.haveSameNames(permanent, "Scepter of Empires", game)) {
                 scepter = true;
-            } else if (CardUtil.haveSameNames(permanent.getName(), "Throne of Empires")) {
+            } else if (CardUtil.haveSameNames(permanent, "Throne of Empires", game)) {
                 throne = true;
             }
             if (scepter && throne) break;
diff --git a/Mage.Sets/src/mage/cards/c/CursedScroll.java b/Mage.Sets/src/mage/cards/c/CursedScroll.java
index 70d1e5f048..6ed0c99568 100644
--- a/Mage.Sets/src/mage/cards/c/CursedScroll.java
+++ b/Mage.Sets/src/mage/cards/c/CursedScroll.java
@@ -70,7 +70,7 @@ class CursedScrollEffect extends OneShotEffect {
                 }
                 revealed.add(card);
                 controller.revealCards(sourceObject.getIdName(), revealed, game);
-                if (CardUtil.haveSameNames(card.getName(), cardName)) {
+                if (CardUtil.haveSameNames(card, cardName, game)) {
                     Permanent creature = game.getPermanent(targetPointer.getFirst(game, source));
                     if (creature != null) {
                         creature.damage(2, source.getSourceId(), game, false, true);
diff --git a/Mage.Sets/src/mage/cards/d/DeathbellowWarCry.java b/Mage.Sets/src/mage/cards/d/DeathbellowWarCry.java
index 18824247a4..1655ec9108 100644
--- a/Mage.Sets/src/mage/cards/d/DeathbellowWarCry.java
+++ b/Mage.Sets/src/mage/cards/d/DeathbellowWarCry.java
@@ -72,6 +72,6 @@ class DeathbellowWarCryTarget extends TargetCardInLibrary {
                 .map(game::getCard)
                 .filter(Objects::nonNull)
                 .map(Card::getName)
-                .noneMatch(n -> CardUtil.haveSameNames(n, card.getName()));
+                .noneMatch(n -> CardUtil.haveSameNames(card, n, game));
     }
 }
diff --git a/Mage.Sets/src/mage/cards/d/DementiaSliver.java b/Mage.Sets/src/mage/cards/d/DementiaSliver.java
index 686a33a1ce..1610c825ae 100644
--- a/Mage.Sets/src/mage/cards/d/DementiaSliver.java
+++ b/Mage.Sets/src/mage/cards/d/DementiaSliver.java
@@ -86,7 +86,7 @@ class DementiaSliverEffect extends OneShotEffect {
                 if (card != null) {
                     revealed.add(card);
                     opponent.revealCards(sourceObject.getName(), revealed, game);
-                    if (CardUtil.haveSameNames(card.getName(), cardName)) {
+                    if (CardUtil.haveSameNames(card, cardName, game)) {
                         opponent.discard(card, source, game);
                     }
                 }
diff --git a/Mage.Sets/src/mage/cards/d/Denied.java b/Mage.Sets/src/mage/cards/d/Denied.java
index b5825c8b1e..596b96114f 100644
--- a/Mage.Sets/src/mage/cards/d/Denied.java
+++ b/Mage.Sets/src/mage/cards/d/Denied.java
@@ -63,7 +63,7 @@ class DeniedEffect extends OneShotEffect {
             player.revealCards("Denied!", player.getHand(), game, true);
             String namedCard = (String) object;
             for (Card card : player.getHand().getCards(game)) {
-                if (card != null && CardUtil.haveSameNames(card.getName(), namedCard)) {
+                if (card != null && CardUtil.haveSameNames(card, namedCard, game)) {
                     game.getStack().counter(targetSpell.getId(), source.getSourceId(), game);
                     break;
                 }
diff --git a/Mage.Sets/src/mage/cards/d/DetentionSphere.java b/Mage.Sets/src/mage/cards/d/DetentionSphere.java
index 64943101ae..c7a99d41a5 100644
--- a/Mage.Sets/src/mage/cards/d/DetentionSphere.java
+++ b/Mage.Sets/src/mage/cards/d/DetentionSphere.java
@@ -85,7 +85,7 @@ class DetentionSphereEntersEffect extends OneShotEffect {
             } else {
                 String name = targetPermanent.getName();
                 for (Permanent permanent : game.getBattlefield().getActivePermanents(source.getControllerId(), game)) {
-                    if (permanent != null && CardUtil.haveSameNames(permanent.getName(), name)) {
+                    if (permanent != null && CardUtil.haveSameNames(permanent, name, game)) {
                         controller.moveCardToExileWithInfo(permanent, exileId, sourceObject.getIdName(), source.getSourceId(), game, Zone.BATTLEFIELD, true);
                     }
                 }
diff --git a/Mage.Sets/src/mage/cards/d/DiviningWitch.java b/Mage.Sets/src/mage/cards/d/DiviningWitch.java
index 39e231aa20..a1a2f862bb 100644
--- a/Mage.Sets/src/mage/cards/d/DiviningWitch.java
+++ b/Mage.Sets/src/mage/cards/d/DiviningWitch.java
@@ -92,7 +92,7 @@ public final class DiviningWitch extends CardImpl {
                     if (card != null) {
                         cardsToReaveal.add(card);
                         // Put that card into your hand
-                        if (CardUtil.haveSameNames(card.getName(), name)) {
+                        if (CardUtil.haveSameNames(card, name, game)) {
                             cardToHand = card;
                             break;
                         }
diff --git a/Mage.Sets/src/mage/cards/f/Foreshadow.java b/Mage.Sets/src/mage/cards/f/Foreshadow.java
index bfa869d065..b3f778e9b3 100644
--- a/Mage.Sets/src/mage/cards/f/Foreshadow.java
+++ b/Mage.Sets/src/mage/cards/f/Foreshadow.java
@@ -72,7 +72,7 @@ class ForeshadowEffect extends OneShotEffect {
             Card card = targetPlayer.getLibrary().getFromTop(game);
             if (card != null) {
                 controller.moveCards(card, Zone.GRAVEYARD, source, game);
-                if (CardUtil.haveSameNames(card.getName(), cardName)) {
+                if (CardUtil.haveSameNames(card, cardName, game)) {
                     controller.drawCards(1, source.getSourceId(), game);
                 }
             }
diff --git a/Mage.Sets/src/mage/cards/l/LammastideWeave.java b/Mage.Sets/src/mage/cards/l/LammastideWeave.java
index 9cd195c43b..b8f979f472 100644
--- a/Mage.Sets/src/mage/cards/l/LammastideWeave.java
+++ b/Mage.Sets/src/mage/cards/l/LammastideWeave.java
@@ -71,7 +71,7 @@ class LammastideWeaveEffect extends OneShotEffect {
             Card card = targetPlayer.getLibrary().getFromTop(game);
             if (card != null) {
                 controller.moveCards(card, Zone.GRAVEYARD, source, game);
-                if (CardUtil.haveSameNames(card.getName(), cardName)) {
+                if (CardUtil.haveSameNames(card, cardName, game)) {
                     controller.gainLife(card.getConvertedManaCost(), game, source);
                 }
             }
diff --git a/Mage.Sets/src/mage/cards/l/LiarsPendulum.java b/Mage.Sets/src/mage/cards/l/LiarsPendulum.java
index 195878591b..e30258077d 100644
--- a/Mage.Sets/src/mage/cards/l/LiarsPendulum.java
+++ b/Mage.Sets/src/mage/cards/l/LiarsPendulum.java
@@ -93,7 +93,7 @@ class LiarsPendulumEffect extends OneShotEffect {
                         rightGuess = opponentGuess;
                     }
                 }
-                if (CardUtil.haveSameNames(card.getName(), cardName)) {
+                if (CardUtil.haveSameNames(card, cardName, game)) {
                     rightGuess = opponentGuess;
                 }
             }
diff --git a/Mage.Sets/src/mage/cards/m/MagusOfTheScroll.java b/Mage.Sets/src/mage/cards/m/MagusOfTheScroll.java
index 71f059afe6..3fd5ef2809 100644
--- a/Mage.Sets/src/mage/cards/m/MagusOfTheScroll.java
+++ b/Mage.Sets/src/mage/cards/m/MagusOfTheScroll.java
@@ -76,7 +76,7 @@ class MagusOfTheScrollEffect extends OneShotEffect {
                 }
                 revealed.add(card);
                 you.revealCards(sourceObject.getName(), revealed, game);
-                if (CardUtil.haveSameNames(card.getName(), cardName)) {
+                if (CardUtil.haveSameNames(card, cardName, game)) {
                     Permanent creature = game.getPermanent(targetPointer.getFirst(game, source));
                     if (creature != null) {
                         creature.damage(2, source.getSourceId(), game, false, true);
diff --git a/Mage.Sets/src/mage/cards/p/PetraSphinx.java b/Mage.Sets/src/mage/cards/p/PetraSphinx.java
index 253d4e77f0..c97a792299 100644
--- a/Mage.Sets/src/mage/cards/p/PetraSphinx.java
+++ b/Mage.Sets/src/mage/cards/p/PetraSphinx.java
@@ -78,7 +78,7 @@ class PetraSphinxEffect extends OneShotEffect {
                 if (card != null) {
                     Cards cards = new CardsImpl(card);
                     player.revealCards(source, cards, game);
-                    if (CardUtil.haveSameNames(card.getName(), cardName)) {
+                    if (CardUtil.haveSameNames(card, cardName, game)) {
                         player.moveCards(cards, Zone.HAND, source, game);
                     } else {
                         player.moveCards(cards, Zone.GRAVEYARD, source, game);
diff --git a/Mage.Sets/src/mage/cards/p/Predict.java b/Mage.Sets/src/mage/cards/p/Predict.java
index 1f9ec4f930..e4f98c5a95 100644
--- a/Mage.Sets/src/mage/cards/p/Predict.java
+++ b/Mage.Sets/src/mage/cards/p/Predict.java
@@ -67,7 +67,7 @@ class PredictEffect extends OneShotEffect {
             Card card = targetPlayer.getLibrary().getFromTop(game);
             if (card != null) {
                 controller.moveCards(card, Zone.GRAVEYARD, source, game);
-                if (CardUtil.haveSameNames(card.getName(), cardName)) {
+                if (CardUtil.haveSameNames(card, cardName, game)) {
                     amount = 2;
                 }
             }
diff --git a/Mage.Sets/src/mage/cards/r/ReflectorMage.java b/Mage.Sets/src/mage/cards/r/ReflectorMage.java
index faabe8b477..8de4580f25 100644
--- a/Mage.Sets/src/mage/cards/r/ReflectorMage.java
+++ b/Mage.Sets/src/mage/cards/r/ReflectorMage.java
@@ -119,7 +119,7 @@ class ExclusionRitualReplacementEffect extends ContinuousRuleModifyingEffectImpl
             if (spell != null && spell.isFaceDown(game)) {
                 return false; // Face Down cast spell (Morph creature) has no name
             }
-            return CardUtil.haveSameNames(card.getName(), creatureName) && Objects.equals(ownerId, card.getOwnerId());
+            return CardUtil.haveSameNames(card, creatureName, game) && Objects.equals(ownerId, card.getOwnerId());
         }
         return false;
     }
diff --git a/Mage.Sets/src/mage/cards/s/SearchTheCity.java b/Mage.Sets/src/mage/cards/s/SearchTheCity.java
index 57ad86e633..0522121de1 100644
--- a/Mage.Sets/src/mage/cards/s/SearchTheCity.java
+++ b/Mage.Sets/src/mage/cards/s/SearchTheCity.java
@@ -157,7 +157,7 @@ class SearchTheCityExiledCardToHandEffect extends OneShotEffect {
         ExileZone searchTheCityExileZone = game.getExile().getExileZone(source.getSourceId());
         if (cardName != null && searchTheCityExileZone != null) {
             for (Card card : searchTheCityExileZone.getCards(game)) {
-                if (CardUtil.haveSameNames(card.getName(), cardName)) {
+                if (CardUtil.haveSameNames(card, cardName, game)) {
                     if (card.moveToZone(Zone.HAND, source.getSourceId(), game, true)) {
                         game.informPlayers("Search the City: put " + card.getName() + " into owner's hand");
                     }
diff --git a/Mage.Sets/src/mage/cards/s/SpoilsOfTheVault.java b/Mage.Sets/src/mage/cards/s/SpoilsOfTheVault.java
index 153bc570f7..3466f128b5 100644
--- a/Mage.Sets/src/mage/cards/s/SpoilsOfTheVault.java
+++ b/Mage.Sets/src/mage/cards/s/SpoilsOfTheVault.java
@@ -68,7 +68,7 @@ class SpoilsOfTheVaultEffect extends OneShotEffect {
         for (Card card : controller.getLibrary().getCards(game)) {
             if (card != null) {
                 cardsToReveal.add(card);
-                if (CardUtil.haveSameNames(card.getName(), cardName)) {
+                if (CardUtil.haveSameNames(card, cardName, game)) {
                     controller.moveCards(card, Zone.HAND, source, game);
                     break;
                 } else {
diff --git a/Mage.Sets/src/mage/cards/t/ThoughtHemorrhage.java b/Mage.Sets/src/mage/cards/t/ThoughtHemorrhage.java
index a4b9bee2e9..7c10810ef0 100644
--- a/Mage.Sets/src/mage/cards/t/ThoughtHemorrhage.java
+++ b/Mage.Sets/src/mage/cards/t/ThoughtHemorrhage.java
@@ -86,7 +86,7 @@ class ThoughtHemorrhageEffect extends OneShotEffect {
                         + targetPlayer.getName(), targetPlayer.getHand(), game);
                 int cardsFound = 0;
                 for (Card card : targetPlayer.getHand().getCards(game)) {
-                    if (CardUtil.haveSameNames(card.getName(), cardName)) {
+                    if (CardUtil.haveSameNames(card, cardName, game)) {
                         cardsFound++;
                     }
                 }
diff --git a/Mage.Sets/src/mage/cards/t/TunnelVision.java b/Mage.Sets/src/mage/cards/t/TunnelVision.java
index 33b58c266c..ff1a97a640 100644
--- a/Mage.Sets/src/mage/cards/t/TunnelVision.java
+++ b/Mage.Sets/src/mage/cards/t/TunnelVision.java
@@ -73,7 +73,7 @@ class TunnelVisionEffect extends OneShotEffect {
 
         for (Card card : targetPlayer.getLibrary().getCards(game)) {
             cardsToReveal.add(card);
-            if (CardUtil.haveSameNames(card.getName(), cardName)) {
+            if (CardUtil.haveSameNames(card, cardName, game)) {
                 namedCard = card;
                 break;
             }
diff --git a/Mage.Sets/src/mage/cards/v/VexingArcanix.java b/Mage.Sets/src/mage/cards/v/VexingArcanix.java
index 7a77fc4238..742e51c56a 100644
--- a/Mage.Sets/src/mage/cards/v/VexingArcanix.java
+++ b/Mage.Sets/src/mage/cards/v/VexingArcanix.java
@@ -74,7 +74,7 @@ class VexingArcanixEffect extends OneShotEffect {
             if (card != null) {
                 Cards cards = new CardsImpl(card);
                 player.revealCards(sourceObject.getIdName(), cards, game);
-                if (CardUtil.haveSameNames(card.getName(), cardName)) {
+                if (CardUtil.haveSameNames(card, cardName, game)) {
                     player.moveCards(cards, Zone.HAND, source, game);
                 } else {
                     player.moveCards(cards, Zone.GRAVEYARD, source, game);
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithCostModificationTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithCostModificationTest.java
new file mode 100644
index 0000000000..ae6f1a97d6
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithCostModificationTest.java
@@ -0,0 +1,201 @@
+package org.mage.test.cards.split;
+
+import mage.constants.PhaseStep;
+import mage.constants.Zone;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestPlayerBase;
+
+/**
+ * @author JayDi85
+ */
+public class CastSplitCardsWithCostModificationTest extends CardTestPlayerBase {
+
+    @Test
+    public void test_CostReduction_Simple() {
+        // {2}{W}{U}
+        // As Council of the Absolute enters the battlefield, choose a noncreature, nonland card name.
+        // Your opponents can’t cast spells with the chosen name.
+        // Spells with the chosen name you cast cost {2} less to cast.
+        addCard(Zone.HAND, playerA, "Council of the Absolute", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 3);
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 1);
+        //
+        addCard(Zone.HAND, playerA, "Blastfire Bolt", 1); // {5}{R}, 5 damage
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6 - 2); // -2 for cost reduction
+        addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears", 1);
+
+        // cast Council
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Council of the Absolute");
+        setChoice(playerA, "Blastfire Bolt");
+
+        // cast bolt
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Blastfire Bolt", "Balduvian Bears");
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        assertGraveyardCount(playerB, "Balduvian Bears", 1);
+    }
+
+    @Test
+    public void test_CostReduction_SplitLeft() {
+        // {2}{W}{U}
+        // As Council of the Absolute enters the battlefield, choose a noncreature, nonland card name.
+        // Your opponents can’t cast spells with the chosen name.
+        // Spells with the chosen name you cast cost {2} less to cast.
+        addCard(Zone.HAND, playerA, "Council of the Absolute", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 3);
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 1);
+        //
+        // Armed {1}{R} Target creature gets +1/+1 and gains double strike until end of turn.
+        // Dangerous {3}{G} All creatures able to block target creature this turn do so.
+        addCard(Zone.HAND, playerA, "Armed // Dangerous", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2 - 1); // -1 from cost reduction
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 4 - 2); // check not working right cost reduction
+        addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1);
+
+        // cast Council
+        activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {W}", 3);
+        activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}", 1);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Council of the Absolute");
+        setChoice(playerA, "Armed");
+
+        // cast Armed
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+        checkPlayableAbility("after reduction", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Armed", true);
+        checkPlayableAbility("after reduction", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Dangerous", false);
+        checkPlayableAbility("after reduction", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast fused Armed // Dangerous", false);
+        showAvaileableAbilities("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Armed", "Balduvian Bears");
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        assertGraveyardCount(playerA, "Armed // Dangerous", 1);
+    }
+
+    @Test
+    public void test_CostReduction_SplitRight() {
+        // {2}{W}{U}
+        // As Council of the Absolute enters the battlefield, choose a noncreature, nonland card name.
+        // Your opponents can’t cast spells with the chosen name.
+        // Spells with the chosen name you cast cost {2} less to cast.
+        addCard(Zone.HAND, playerA, "Council of the Absolute", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 3);
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 1);
+        //
+        // Armed {1}{R} Target creature gets +1/+1 and gains double strike until end of turn.
+        // Dangerous {3}{G} All creatures able to block target creature this turn do so.
+        addCard(Zone.HAND, playerA, "Armed // Dangerous", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 4 - 2); // -2 from cost reduction
+        addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1);
+
+        // cast Council
+        activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {W}", 3);
+        activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}", 1);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Council of the Absolute");
+        setChoice(playerA, "Dangerous");
+
+        // cast Dangerous
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+        checkPlayableAbility("after reduction", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Armed", false);
+        checkPlayableAbility("after reduction", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Dangerous", true);
+        checkPlayableAbility("after reduction", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast fused Armed // Dangerous", false);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dangerous", "Balduvian Bears");
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        assertGraveyardCount(playerA, "Armed // Dangerous", 1);
+    }
+
+    @Test
+    @Ignore // TODO: cost modification don't work for fused spells, only for one of the part, see https://github.com/magefree/mage/issues/6603
+    public void test_CostReduction_SplitFused_ReduceRight() {
+        // {2}{W}{U}
+        // As Council of the Absolute enters the battlefield, choose a noncreature, nonland card name.
+        // Your opponents can’t cast spells with the chosen name.
+        // Spells with the chosen name you cast cost {2} less to cast.
+        addCard(Zone.HAND, playerA, "Council of the Absolute", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 3);
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 1);
+        //
+        // Armed {1}{R} Target creature gets +1/+1 and gains double strike until end of turn.
+        // Dangerous {3}{G} All creatures able to block target creature this turn do so.
+        addCard(Zone.HAND, playerA, "Armed // Dangerous", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); // no cost reduction
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 4 - 2); // -2 from cost reduction
+        addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1);
+
+        // cast Council
+        activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {W}", 3);
+        activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}", 1);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Council of the Absolute");
+        setChoice(playerA, "Dangerous");
+
+        // cast fused
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+        checkPlayableAbility("after reduction", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Armed", true);
+        checkPlayableAbility("after reduction", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Dangerous", true);
+        checkPlayableAbility("after reduction", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast fused Armed // Dangerous", true);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "fused Armed // Dangerous");
+        addTarget(playerA, "Balduvian Bears");
+        addTarget(playerA, "Balduvian Bears");
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        assertGraveyardCount(playerA, "Armed // Dangerous", 1);
+    }
+
+    @Test
+    @Ignore // TODO: cost modification don't work for fused spells, only for one of the part, see https://github.com/magefree/mage/issues/6603
+    public void test_CostReduction_SplitFused_ReduceLeft() {
+        // {2}{W}{U}
+        // As Council of the Absolute enters the battlefield, choose a noncreature, nonland card name.
+        // Your opponents can’t cast spells with the chosen name.
+        // Spells with the chosen name you cast cost {2} less to cast.
+        addCard(Zone.HAND, playerA, "Council of the Absolute", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 3);
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 1);
+        //
+        // Armed {1}{R} Target creature gets +1/+1 and gains double strike until end of turn.
+        // Dangerous {3}{G} All creatures able to block target creature this turn do so.
+        addCard(Zone.HAND, playerA, "Armed // Dangerous", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2 - 1); // -1 from cost reduction
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 4 - 1); // -1 from cost reduction ON FUSED
+        addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1);
+
+        // cast Council
+        activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {W}", 3);
+        activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}", 1);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Council of the Absolute");
+        setChoice(playerA, "Armed");
+
+        // cast fused
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+        checkPlayableAbility("after reduction", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Armed", true);
+        checkPlayableAbility("after reduction", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Dangerous", true);
+        checkPlayableAbility("after reduction", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast fused Armed // Dangerous", true);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "fused Armed // Dangerous");
+        addTarget(playerA, "Balduvian Bears");
+        addTarget(playerA, "Balduvian Bears");
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        assertGraveyardCount(playerA, "Armed // Dangerous", 1);
+    }
+}
diff --git a/Mage.Tests/src/test/java/org/mage/test/testapi/TestAliases.java b/Mage.Tests/src/test/java/org/mage/test/testapi/TestAliases.java
index 25c5a66b22..1702f2f90f 100644
--- a/Mage.Tests/src/test/java/org/mage/test/testapi/TestAliases.java
+++ b/Mage.Tests/src/test/java/org/mage/test/testapi/TestAliases.java
@@ -1,5 +1,7 @@
 package org.mage.test.testapi;
 
+import mage.cards.Card;
+import mage.cards.repository.CardRepository;
 import mage.constants.EmptyNames;
 import mage.constants.PhaseStep;
 import mage.constants.Zone;
@@ -44,6 +46,18 @@ public class TestAliases extends CardTestPlayerBase {
         Assert.assertFalse(CardUtil.haveSameNames("Name", "123", true));
         Assert.assertFalse(CardUtil.haveSameNames("Name", EmptyNames.FACE_DOWN_CREATURE.toString(), true));
         Assert.assertFalse(CardUtil.haveSameNames("Name1", "Name2", true));
+
+        // name with split card
+        Card splitCard1 = CardRepository.instance.findCard("Armed // Dangerous").getCard();
+        Card splitCard2 = CardRepository.instance.findCard("Alive // Well").getCard();
+        Assert.assertTrue(CardUtil.haveSameNames(splitCard1, "Armed", currentGame));
+        Assert.assertTrue(CardUtil.haveSameNames(splitCard1, "Dangerous", currentGame));
+        Assert.assertTrue(CardUtil.haveSameNames(splitCard1, "Armed // Dangerous", currentGame));
+        Assert.assertTrue(CardUtil.haveSameNames(splitCard1, splitCard1));
+        Assert.assertFalse(CardUtil.haveSameNames(splitCard1, "Other", currentGame));
+        Assert.assertFalse(CardUtil.haveSameNames(splitCard1, "Other // Dangerous", currentGame));
+        Assert.assertFalse(CardUtil.haveSameNames(splitCard1, "Armed // Other", currentGame));
+        Assert.assertFalse(CardUtil.haveSameNames(splitCard1, splitCard2));
     }
 
     @Test
diff --git a/Mage/src/main/java/mage/filter/predicate/mageobject/NamePredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/NamePredicate.java
index b80f7766a7..bc266d2718 100644
--- a/Mage/src/main/java/mage/filter/predicate/mageobject/NamePredicate.java
+++ b/Mage/src/main/java/mage/filter/predicate/mageobject/NamePredicate.java
@@ -31,11 +31,13 @@ public class NamePredicate implements Predicate<MageObject> {
         // A split card has the chosen name if one of its two names matches the chosen name.
         if (input instanceof SplitCard) {
             return CardUtil.haveSameNames(name, ((SplitCard) input).getLeftHalfCard().getName(), this.ignoreMtgRuleForEmptyNames) ||
-                    CardUtil.haveSameNames(name, ((SplitCard) input).getRightHalfCard().getName(), this.ignoreMtgRuleForEmptyNames);
+                    CardUtil.haveSameNames(name, ((SplitCard) input).getRightHalfCard().getName(), this.ignoreMtgRuleForEmptyNames) ||
+                    CardUtil.haveSameNames(name, input.getName(), this.ignoreMtgRuleForEmptyNames);
         } else if (input instanceof Spell && ((Spell) input).getSpellAbility().getSpellAbilityType() == SpellAbilityType.SPLIT_FUSED) {
             SplitCard card = (SplitCard) ((Spell) input).getCard();
             return CardUtil.haveSameNames(name, card.getLeftHalfCard().getName(), this.ignoreMtgRuleForEmptyNames) ||
-                    CardUtil.haveSameNames(name, card.getRightHalfCard().getName(), this.ignoreMtgRuleForEmptyNames);
+                    CardUtil.haveSameNames(name, card.getRightHalfCard().getName(), this.ignoreMtgRuleForEmptyNames) ||
+                    CardUtil.haveSameNames(name, card.getName(), this.ignoreMtgRuleForEmptyNames);
         } else {
             if (name.contains(" // ")) {
                 String leftName = name.substring(0, name.indexOf(" // "));
diff --git a/Mage/src/main/java/mage/util/CardUtil.java b/Mage/src/main/java/mage/util/CardUtil.java
index 99022d44ae..b418ad2fd8 100644
--- a/Mage/src/main/java/mage/util/CardUtil.java
+++ b/Mage/src/main/java/mage/util/CardUtil.java
@@ -11,6 +11,7 @@ import mage.constants.ColoredManaSymbol;
 import mage.constants.EmptyNames;
 import mage.constants.ManaType;
 import mage.filter.Filter;
+import mage.filter.predicate.mageobject.NamePredicate;
 import mage.game.CardState;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
@@ -657,6 +658,14 @@ public final class CardUtil {
         return object1 != null && object2 != null && haveSameNames(object1.getName(), object2.getName());
     }
 
+    public static boolean haveSameNames(MageObject object, String needName, Game game) {
+        return containsName(object, needName, game);
+    }
+
+    public static boolean containsName(MageObject object, String name, Game game) {
+        return new NamePredicate(name).apply(object, game);
+    }
+
     public static boolean haveEmptyName(String name) {
         return name == null || name.isEmpty() || name.equals(EmptyNames.FACE_DOWN_CREATURE.toString()) || name.equals(EmptyNames.FACE_DOWN_TOKEN.toString());
     }

From ecac9295ece2a91fdc061e13cdc9a00c8940f1bf Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Fri, 5 Jun 2020 01:13:17 +0400
Subject: [PATCH 096/586] Additional card fixed for named abilities support,
 also added face down spells support (see #6569);

---
 Mage.Sets/src/mage/cards/c/ConjurersBan.java  | 10 ++++---
 .../mage/cards/c/CouncilOfTheAbsolute.java    | 10 +++----
 Mage.Sets/src/mage/cards/d/Denied.java        |  7 +++--
 .../src/mage/cards/g/GideonsIntervention.java |  4 ++-
 .../mage/cards/i/IsperiaTheInscrutable.java   | 19 +++++++-------
 Mage.Sets/src/mage/cards/m/MeddlingMage.java  | 26 +++++--------------
 Mage.Sets/src/mage/cards/m/Mise.java          | 17 ++++++------
 Mage.Sets/src/mage/cards/n/Nevermore.java     | 10 +++----
 Mage.Sets/src/mage/cards/n/NullChamber.java   | 12 +++------
 .../src/mage/cards/p/PhyrexianRevoker.java    | 21 ++++++---------
 Mage.Sets/src/mage/cards/p/PithingNeedle.java | 21 ++++++++-------
 .../src/mage/cards/s/SorcerousSpyglass.java   | 21 ++++++---------
 .../src/mage/cards/v/VoidstoneGargoyle.java   | 26 +++++++------------
 .../org/mage/test/testapi/TestAliases.java    | 17 ++++++++++++
 .../predicate/mageobject/NamePredicate.java   |  7 +++--
 15 files changed, 110 insertions(+), 118 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/c/ConjurersBan.java b/Mage.Sets/src/mage/cards/c/ConjurersBan.java
index db7b0bd5a1..ee03ad7253 100644
--- a/Mage.Sets/src/mage/cards/c/ConjurersBan.java
+++ b/Mage.Sets/src/mage/cards/c/ConjurersBan.java
@@ -12,6 +12,7 @@ import mage.constants.Duration;
 import mage.constants.Outcome;
 import mage.game.Game;
 import mage.game.events.GameEvent;
+import mage.util.CardUtil;
 
 import java.util.UUID;
 
@@ -66,18 +67,19 @@ class ConjurersBanEffect extends ContinuousRuleModifyingEffectImpl {
     public boolean applies(GameEvent event, Ability source, Game game) {
         if (event.getType() == GameEvent.EventType.CAST_SPELL || event.getType() == GameEvent.EventType.PLAY_LAND) {
             MageObject object = game.getObject(event.getSourceId());
-            return object != null && object.getName().equals(game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY));
+            String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY);
+            return CardUtil.haveSameNames(object, cardName, game);
         }
         return false;
     }
 
     @Override
     public String getInfoMessage(Ability source, GameEvent event, Game game) {
-        String namedCard = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY);
+        String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY);
         String playerName = game.getPlayer(source.getControllerId()).getName();
-        if (namedCard == null || playerName == null || source.getSourceObject(game) == null) {
+        if (cardName == null || playerName == null || source.getSourceObject(game) == null) {
             return super.getInfoMessage(source, event, game);
         }
-        return "Until " + playerName + "'s next turn, spells named " + namedCard + " can't be cast and lands named " + namedCard + " can't be played (" + source.getSourceObject(game).getIdName() + ").";
+        return "Until " + playerName + "'s next turn, spells named " + cardName + " can't be cast and lands named " + cardName + " can't be played (" + source.getSourceObject(game).getIdName() + ").";
     }
 }
diff --git a/Mage.Sets/src/mage/cards/c/CouncilOfTheAbsolute.java b/Mage.Sets/src/mage/cards/c/CouncilOfTheAbsolute.java
index 90fd017f89..483f058a50 100644
--- a/Mage.Sets/src/mage/cards/c/CouncilOfTheAbsolute.java
+++ b/Mage.Sets/src/mage/cards/c/CouncilOfTheAbsolute.java
@@ -78,7 +78,7 @@ class CouncilOfTheAbsoluteReplacementEffect extends ContinuousRuleModifyingEffec
     public String getInfoMessage(Ability source, GameEvent event, Game game) {
         MageObject mageObject = game.getObject(source.getSourceId());
         if (mageObject != null) {
-            return "You can't cast a spell with that name (" + mageObject.getLogName() + " in play).";
+            return "You can't cast a spell with that name (" + mageObject.getName() + " in play).";
         }
         return null;
     }
@@ -92,8 +92,8 @@ class CouncilOfTheAbsoluteReplacementEffect extends ContinuousRuleModifyingEffec
     public boolean applies(GameEvent event, Ability source, Game game) {
         if (game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) {
             MageObject object = game.getObject(event.getSourceId());
-            String needName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY);
-            return object != null && CardUtil.haveSameNames(object, needName, game);
+            String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY);
+            return object != null && CardUtil.haveSameNames(object, cardName, game);
         }
         return false;
     }
@@ -122,8 +122,8 @@ class CouncilOfTheAbsoluteCostReductionEffect extends CostModificationEffectImpl
                 && abilityToModify.isControlledBy(source.getControllerId())) {
             Card card = game.getCard(abilityToModify.getSourceId());
             if (card != null) {
-                String needName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY);
-                return CardUtil.haveSameNames(card, needName, game);
+                String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY);
+                return CardUtil.haveSameNames(card, cardName, game);
             }
         }
         return false;
diff --git a/Mage.Sets/src/mage/cards/d/Denied.java b/Mage.Sets/src/mage/cards/d/Denied.java
index 596b96114f..b8d60aec7a 100644
--- a/Mage.Sets/src/mage/cards/d/Denied.java
+++ b/Mage.Sets/src/mage/cards/d/Denied.java
@@ -58,12 +58,11 @@ class DeniedEffect extends OneShotEffect {
             return true;
         }
         Player player = game.getPlayer(targetSpell.getControllerId());
-        Object object = game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY);
-        if (player != null && object instanceof String) {
+        String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY);
+        if (player != null && cardName != null) {
             player.revealCards("Denied!", player.getHand(), game, true);
-            String namedCard = (String) object;
             for (Card card : player.getHand().getCards(game)) {
-                if (card != null && CardUtil.haveSameNames(card, namedCard, game)) {
+                if (card != null && CardUtil.haveSameNames(card, cardName, game)) {
                     game.getStack().counter(targetSpell.getId(), source.getSourceId(), game);
                     break;
                 }
diff --git a/Mage.Sets/src/mage/cards/g/GideonsIntervention.java b/Mage.Sets/src/mage/cards/g/GideonsIntervention.java
index 9ab17cfb9b..1a679be278 100644
--- a/Mage.Sets/src/mage/cards/g/GideonsIntervention.java
+++ b/Mage.Sets/src/mage/cards/g/GideonsIntervention.java
@@ -20,6 +20,7 @@ import mage.game.events.GameEvent;
 import mage.game.events.GameEvent.EventType;
 import mage.game.events.PreventDamageEvent;
 import mage.game.permanent.Permanent;
+import mage.util.CardUtil;
 
 import java.util.UUID;
 
@@ -132,11 +133,12 @@ class GideonsInterventionPreventAllDamageEffect extends PreventionEffectImpl {
     public boolean applies(GameEvent event, Ability source, Game game) {
         MageObject object = game.getObject(event.getSourceId());
         Permanent targetPerm = game.getPermanent(event.getTargetId());
+        String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY);
 
         if (object != null && (event.getType() == GameEvent.EventType.DAMAGE_PLAYER
                 || targetPerm != null && (event.getType() == GameEvent.EventType.DAMAGE_CREATURE
                 || event.getType() == GameEvent.EventType.DAMAGE_PLANESWALKER))) {
-            if (object.getName().equals(game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY))
+            if (CardUtil.haveSameNames(object, cardName, game)
                     && (event.getTargetId().equals(source.getControllerId())
                     || targetPerm != null && targetPerm.isControlledBy(source.getControllerId()))) {
                 return super.applies(event, source, game);
diff --git a/Mage.Sets/src/mage/cards/i/IsperiaTheInscrutable.java b/Mage.Sets/src/mage/cards/i/IsperiaTheInscrutable.java
index 7a6a663c2d..ade3ddfe38 100644
--- a/Mage.Sets/src/mage/cards/i/IsperiaTheInscrutable.java
+++ b/Mage.Sets/src/mage/cards/i/IsperiaTheInscrutable.java
@@ -1,7 +1,5 @@
-
 package mage.cards.i;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
@@ -14,23 +12,25 @@ import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.SubType;
 import mage.constants.Outcome;
+import mage.constants.SubType;
 import mage.constants.SuperType;
 import mage.filter.FilterCard;
 import mage.filter.predicate.mageobject.AbilityPredicate;
 import mage.game.Game;
 import mage.players.Player;
 import mage.target.common.TargetCardInLibrary;
+import mage.util.CardUtil;
+
+import java.util.UUID;
 
 /**
- *
  * @author lunaskyrise
  */
 public final class IsperiaTheInscrutable extends CardImpl {
 
     public IsperiaTheInscrutable(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{W}{W}{U}{U}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{W}{U}{U}");
         this.addSuperType(SuperType.LEGENDARY);
         this.subtype.add(SubType.SPHINX);
         this.power = new MageInt(3);
@@ -38,7 +38,7 @@ public final class IsperiaTheInscrutable extends CardImpl {
 
         // Flying
         this.addAbility(FlyingAbility.getInstance());
-        
+
         // Whenever Isperia the Inscrutable deals combat damage to a player, name a card. That player reveals their hand. If they reveal the named card, search your library for a creature card with flying, reveal it, put it into your hand, then shuffle your library.
         Effect effect1 = new ChooseACardNameEffect(ChooseACardNameEffect.TypeOfName.ALL);
         Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(effect1, false, true);
@@ -78,12 +78,11 @@ class IsperiaTheInscrutableEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player player = game.getPlayer(getTargetPointer().getFirst(game, source));
-        Object object = game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY);
-        if (player != null && object instanceof String) {
+        String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY);
+        if (player != null && cardName != null) {
             player.revealCards(player.getLogName() + " hand", player.getHand(), game, true);
-            String namedCard = (String) object;
             for (Card card : player.getHand().getCards(game)) {
-                if (card != null && card.getName().equals(namedCard)) {
+                if (CardUtil.haveSameNames(card, cardName, game)) {
                     return new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter), true, true).apply(game, source);
                 }
             }
diff --git a/Mage.Sets/src/mage/cards/m/MeddlingMage.java b/Mage.Sets/src/mage/cards/m/MeddlingMage.java
index ebf5f71d3a..bb855380b1 100644
--- a/Mage.Sets/src/mage/cards/m/MeddlingMage.java
+++ b/Mage.Sets/src/mage/cards/m/MeddlingMage.java
@@ -1,6 +1,5 @@
 package mage.cards.m;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.MageObject;
 import mage.abilities.Ability;
@@ -8,21 +7,17 @@ import mage.abilities.common.AsEntersBattlefieldAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
 import mage.abilities.effects.common.ChooseACardNameEffect;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.CardType;
-import mage.constants.SubType;
-import mage.constants.Duration;
-import mage.constants.Outcome;
-import mage.constants.Zone;
+import mage.constants.*;
 import mage.game.Game;
 import mage.game.events.GameEvent;
 import mage.game.events.GameEvent.EventType;
-import mage.game.stack.Spell;
+import mage.util.CardUtil;
+
+import java.util.UUID;
 
 /**
- *
  * @author Plopman
  */
 public final class MeddlingMage extends CardImpl {
@@ -77,7 +72,7 @@ class MeddlingMageReplacementEffect extends ContinuousRuleModifyingEffectImpl {
     public String getInfoMessage(Ability source, GameEvent event, Game game) {
         MageObject mageObject = game.getObject(source.getSourceId());
         if (mageObject != null) {
-            return "You can't cast a spell with that name (" + mageObject.getLogName() + " in play).";
+            return "You can't cast a spell with that name (" + mageObject.getName() + " in play).";
         }
         return null;
     }
@@ -89,18 +84,11 @@ class MeddlingMageReplacementEffect extends ContinuousRuleModifyingEffectImpl {
 
     @Override
     public boolean applies(GameEvent event, Ability source, Game game) {
-        // Check for Morph spell (no name)
-        Card card = game.getCard(event.getSourceId());
-        if (card != null) {
-            Spell spell = game.getState().getStack().getSpell(event.getSourceId());
-            if (spell != null && spell.isFaceDown(game)) {
-                return false; // Face Down cast spell (Morph creature) has no name
-            }
-        }
         MageObject object = game.getObject(event.getSourceId());
+        String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY);
         return object != null
                 && !object.isCopy()
-                && object.getName().equals(game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY));
+                && CardUtil.haveSameNames(object, cardName, game);
 
     }
 }
diff --git a/Mage.Sets/src/mage/cards/m/Mise.java b/Mage.Sets/src/mage/cards/m/Mise.java
index 221affc8fd..b09b753876 100644
--- a/Mage.Sets/src/mage/cards/m/Mise.java
+++ b/Mage.Sets/src/mage/cards/m/Mise.java
@@ -1,7 +1,5 @@
-
 package mage.cards.m;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.ChooseACardNameEffect;
@@ -13,16 +11,18 @@ import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.game.Game;
 import mage.players.Player;
+import mage.util.CardUtil;
+
+import java.util.UUID;
 
 /**
- *
  * @author L_J
  */
 public final class Mise extends CardImpl {
 
     public Mise(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{U}");
-        
+        super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}");
+
         // Choose a nonland card name, then reveal the top card of your library. If that card has the chosen name, you draw three cards.
         this.getSpellAbility().addEffect(new ChooseACardNameEffect(ChooseACardNameEffect.TypeOfName.NON_LAND_NAME));
         this.getSpellAbility().addEffect(new MiseEffect());
@@ -52,14 +52,13 @@ class MiseEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player player = game.getPlayer(source.getControllerId());
-        Object object = game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY);
-        if (player != null && object instanceof String) {
+        String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY);
+        if (player != null && cardName != null) {
             Card card = player.getLibrary().getFromTop(game);
-            String namedCard = (String) object;
             CardsImpl cards = new CardsImpl(card);
             if (card != null) {
                 player.revealCards("Mise", cards, game, true);
-                if (card.getName().equals(namedCard)) {
+                if (CardUtil.haveSameNames(card, cardName, game)) {
                     player.drawCards(3, source.getSourceId(), game);
                 }
             }
diff --git a/Mage.Sets/src/mage/cards/n/Nevermore.java b/Mage.Sets/src/mage/cards/n/Nevermore.java
index 755bf786cd..36557baebe 100644
--- a/Mage.Sets/src/mage/cards/n/Nevermore.java
+++ b/Mage.Sets/src/mage/cards/n/Nevermore.java
@@ -1,6 +1,5 @@
 package mage.cards.n;
 
-import java.util.UUID;
 import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.common.AsEntersBattlefieldAbility;
@@ -16,9 +15,11 @@ import mage.constants.Zone;
 import mage.game.Game;
 import mage.game.events.GameEvent;
 import mage.game.events.GameEvent.EventType;
+import mage.util.CardUtil;
+
+import java.util.UUID;
 
 /**
- *
  * @author BetaSteward_at_googlemail.com
  */
 public final class Nevermore extends CardImpl {
@@ -70,9 +71,8 @@ class NevermoreEffect2 extends ContinuousRuleModifyingEffectImpl {
     public boolean applies(GameEvent event, Ability source, Game game) {
         if (event.getType() == EventType.CAST_SPELL) {
             MageObject object = game.getObject(event.getSourceId());
-            if (object != null && object.getName().equals(game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY))) {
-                return true;
-            }
+            String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY);
+            return CardUtil.haveSameNames(object, cardName, game);
         }
         return false;
     }
diff --git a/Mage.Sets/src/mage/cards/n/NullChamber.java b/Mage.Sets/src/mage/cards/n/NullChamber.java
index c3562465a3..99b0bf51ec 100644
--- a/Mage.Sets/src/mage/cards/n/NullChamber.java
+++ b/Mage.Sets/src/mage/cards/n/NullChamber.java
@@ -1,22 +1,17 @@
 package mage.cards.n;
 
-import java.util.UUID;
 import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.common.AsEntersBattlefieldAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
 import mage.abilities.effects.OneShotEffect;
-import mage.constants.SuperType;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.cards.repository.CardRepository;
 import mage.choices.Choice;
 import mage.choices.ChoiceImpl;
-import mage.constants.CardType;
-import mage.constants.Duration;
-import mage.constants.Outcome;
-import mage.constants.Zone;
+import mage.constants.*;
 import mage.game.Game;
 import mage.game.events.GameEvent;
 import mage.game.events.GameEvent.EventType;
@@ -25,8 +20,9 @@ import mage.players.Player;
 import mage.target.common.TargetOpponent;
 import mage.util.CardUtil;
 
+import java.util.UUID;
+
 /**
- *
  * @author jeffwadsworth
  */
 public final class NullChamber extends CardImpl {
@@ -141,7 +137,7 @@ class NullChamberReplacementEffect extends ContinuousRuleModifyingEffectImpl {
     public String getInfoMessage(Ability source, GameEvent event, Game game) {
         MageObject mageObject = game.getObject(source.getSourceId());
         if (mageObject != null) {
-            return "You can't cast a spell with that name (" + mageObject.getLogName() + " in play).";
+            return "You can't cast a spell with that name (" + mageObject.getName() + " in play).";
         }
         return null;
     }
diff --git a/Mage.Sets/src/mage/cards/p/PhyrexianRevoker.java b/Mage.Sets/src/mage/cards/p/PhyrexianRevoker.java
index a0880af364..b68174a304 100644
--- a/Mage.Sets/src/mage/cards/p/PhyrexianRevoker.java
+++ b/Mage.Sets/src/mage/cards/p/PhyrexianRevoker.java
@@ -1,7 +1,5 @@
-
 package mage.cards.p;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.MageObject;
 import mage.abilities.Ability;
@@ -11,23 +9,21 @@ import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
 import mage.abilities.effects.common.ChooseACardNameEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.CardType;
-import mage.constants.SubType;
-import mage.constants.Duration;
-import mage.constants.Outcome;
-import mage.constants.Zone;
+import mage.constants.*;
 import mage.game.Game;
 import mage.game.events.GameEvent;
 import mage.game.events.GameEvent.EventType;
+import mage.util.CardUtil;
+
+import java.util.UUID;
 
 /**
- *
  * @author BetaSteward_at_googlemail.com
  */
 public final class PhyrexianRevoker extends CardImpl {
 
     public PhyrexianRevoker(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{2}");
+        super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{2}");
         this.subtype.add(SubType.HORROR);
         this.power = new MageInt(2);
         this.toughness = new MageInt(1);
@@ -75,7 +71,7 @@ class PhyrexianRevokerEffect2 extends ContinuousRuleModifyingEffectImpl {
     public String getInfoMessage(Ability source, GameEvent event, Game game) {
         MageObject mageObject = game.getObject(source.getSourceId());
         if (mageObject != null) {
-            return "You can't activate abilities of sources with that name (" + mageObject.getLogName() + " in play).";
+            return "You can't activate abilities of sources with that name (" + mageObject.getName() + " in play).";
         }
         return null;
     }
@@ -84,9 +80,8 @@ class PhyrexianRevokerEffect2 extends ContinuousRuleModifyingEffectImpl {
     public boolean applies(GameEvent event, Ability source, Game game) {
         if (event.getType() == EventType.ACTIVATE_ABILITY) {
             MageObject object = game.getObject(event.getSourceId());
-            if (object != null && object.getName().equals(game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY))) {
-                return true;
-            }
+            String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY);
+            return CardUtil.haveSameNames(object, cardName, game);
         }
         return false;
     }
diff --git a/Mage.Sets/src/mage/cards/p/PithingNeedle.java b/Mage.Sets/src/mage/cards/p/PithingNeedle.java
index d8049e8ab0..2ec117dff4 100644
--- a/Mage.Sets/src/mage/cards/p/PithingNeedle.java
+++ b/Mage.Sets/src/mage/cards/p/PithingNeedle.java
@@ -1,7 +1,5 @@
 package mage.cards.p;
 
-import java.util.Optional;
-import java.util.UUID;
 import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.common.AsEntersBattlefieldAbility;
@@ -11,12 +9,18 @@ import mage.abilities.effects.common.ChooseACardNameEffect;
 import mage.abilities.mana.ActivatedManaAbilityImpl;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.*;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.Outcome;
+import mage.constants.Zone;
 import mage.game.Game;
 import mage.game.events.GameEvent;
+import mage.util.CardUtil;
+
+import java.util.Optional;
+import java.util.UUID;
 
 /**
- *
  * @author jeffwadsworth, nox
  */
 public final class PithingNeedle extends CardImpl {
@@ -70,14 +74,13 @@ class PithingNeedleEffect extends ContinuousRuleModifyingEffectImpl {
     @Override
     public boolean applies(GameEvent event, Ability source, Game game) {
         MageObject object = game.getObject(event.getSourceId());
+        String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY);
         Optional<Ability> ability = game.getAbility(event.getTargetId(), event.getSourceId());
-        if (ability.isPresent() 
+        if (ability.isPresent()
                 && object != null) {
-            if (game.getState().getPlayersInRange(source.getControllerId(), game).contains(event.getPlayerId()) // controller in range
+            return game.getState().getPlayersInRange(source.getControllerId(), game).contains(event.getPlayerId()) // controller in range
                     && !(ability.get() instanceof ActivatedManaAbilityImpl) // not an activated mana ability
-                    && object.getName().equals(game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY))) {
-                return true;
-            }
+                    && CardUtil.haveSameNames(object, cardName, game);
         }
         return false;
     }
diff --git a/Mage.Sets/src/mage/cards/s/SorcerousSpyglass.java b/Mage.Sets/src/mage/cards/s/SorcerousSpyglass.java
index 1e1735f9a2..6db68ce6e5 100644
--- a/Mage.Sets/src/mage/cards/s/SorcerousSpyglass.java
+++ b/Mage.Sets/src/mage/cards/s/SorcerousSpyglass.java
@@ -1,8 +1,5 @@
-
 package mage.cards.s;
 
-import java.util.Optional;
-import java.util.UUID;
 import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.common.AsEntersBattlefieldAbility;
@@ -11,18 +8,17 @@ import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
 import mage.abilities.effects.common.ChooseACardNameEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.AbilityType;
-import mage.constants.CardType;
-import mage.constants.Duration;
-import mage.constants.Outcome;
-import mage.constants.Zone;
+import mage.constants.*;
 import mage.game.Game;
 import mage.game.events.GameEvent;
 import mage.players.Player;
 import mage.target.common.TargetOpponent;
+import mage.util.CardUtil;
+
+import java.util.Optional;
+import java.util.UUID;
 
 /**
- *
  * @author TheElk801
  */
 public final class SorcerousSpyglass extends CardImpl {
@@ -110,13 +106,12 @@ class SorcerousSpyglassActivationEffect extends ContinuousRuleModifyingEffectImp
     @Override
     public boolean applies(GameEvent event, Ability source, Game game) {
         MageObject object = game.getObject(event.getSourceId());
+        String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY);
         Optional<Ability> ability = game.getAbility(event.getTargetId(), event.getSourceId());
         if (ability.isPresent() && object != null) {
-            if (game.getState().getPlayersInRange(source.getControllerId(), game).contains(event.getPlayerId()) // controller in range
+            return game.getState().getPlayersInRange(source.getControllerId(), game).contains(event.getPlayerId()) // controller in range
                     && ability.get().getAbilityType() != AbilityType.MANA
-                    && object.getName().equals(game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY))) {
-                return true;
-            }
+                    && CardUtil.haveSameNames(object, cardName, game);
         }
         return false;
     }
diff --git a/Mage.Sets/src/mage/cards/v/VoidstoneGargoyle.java b/Mage.Sets/src/mage/cards/v/VoidstoneGargoyle.java
index c2cdda7262..16294ad597 100644
--- a/Mage.Sets/src/mage/cards/v/VoidstoneGargoyle.java
+++ b/Mage.Sets/src/mage/cards/v/VoidstoneGargoyle.java
@@ -1,6 +1,5 @@
 package mage.cards.v;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.MageObject;
 import mage.abilities.Ability;
@@ -11,17 +10,15 @@ import mage.abilities.effects.common.ChooseACardNameEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.CardType;
-import mage.constants.SubType;
-import mage.constants.Duration;
-import mage.constants.Outcome;
-import mage.constants.Zone;
+import mage.constants.*;
 import mage.game.Game;
 import mage.game.events.GameEvent;
 import mage.game.events.GameEvent.EventType;
+import mage.util.CardUtil;
+
+import java.util.UUID;
 
 /**
- *
  * @author Plopman
  */
 public final class VoidstoneGargoyle extends CardImpl {
@@ -78,7 +75,7 @@ class VoidstoneGargoyleReplacementEffect1 extends ContinuousRuleModifyingEffectI
     public String getInfoMessage(Ability source, GameEvent event, Game game) {
         MageObject mageObject = game.getObject(source.getSourceId());
         if (mageObject != null) {
-            return "You can't cast a spell with that name (" + mageObject.getIdName() + ").";
+            return "You can't cast a spell with that name (" + mageObject.getName() + ").";
         }
         return null;
     }
@@ -87,10 +84,8 @@ class VoidstoneGargoyleReplacementEffect1 extends ContinuousRuleModifyingEffectI
     public boolean applies(GameEvent event, Ability source, Game game) {
         if (event.getType() == GameEvent.EventType.CAST_SPELL) {
             MageObject object = game.getObject(event.getSourceId());
-            if (object != null
-                    && object.getName().equals(game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY))) {
-                return true;
-            }
+            String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY);
+            return CardUtil.haveSameNames(object, cardName, game);
         }
         return false;
     }
@@ -122,7 +117,7 @@ class VoidstoneGargoyleRuleModifyingEffect2 extends ContinuousRuleModifyingEffec
     public String getInfoMessage(Ability source, GameEvent event, Game game) {
         MageObject mageObject = game.getObject(source.getSourceId());
         if (mageObject != null) {
-            return "You can't activate abilities of sources with that name (" + mageObject.getLogName() + " in play).";
+            return "You can't activate abilities of sources with that name (" + mageObject.getName() + " in play).";
         }
         return null;
     }
@@ -131,9 +126,8 @@ class VoidstoneGargoyleRuleModifyingEffect2 extends ContinuousRuleModifyingEffec
     public boolean applies(GameEvent event, Ability source, Game game) {
         if (event.getType() == EventType.ACTIVATE_ABILITY) {
             MageObject object = game.getObject(event.getSourceId());
-            if (object != null && object.getName().equals(game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY))) {
-                return true;
-            }
+            String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY);
+            return CardUtil.haveSameNames(object, cardName, game);
         }
         return false;
     }
diff --git a/Mage.Tests/src/test/java/org/mage/test/testapi/TestAliases.java b/Mage.Tests/src/test/java/org/mage/test/testapi/TestAliases.java
index 1702f2f90f..f5a72e3721 100644
--- a/Mage.Tests/src/test/java/org/mage/test/testapi/TestAliases.java
+++ b/Mage.Tests/src/test/java/org/mage/test/testapi/TestAliases.java
@@ -5,6 +5,7 @@ import mage.cards.repository.CardRepository;
 import mage.constants.EmptyNames;
 import mage.constants.PhaseStep;
 import mage.constants.Zone;
+import mage.game.stack.Spell;
 import mage.util.CardUtil;
 import org.junit.Assert;
 import org.junit.Test;
@@ -58,6 +59,22 @@ public class TestAliases extends CardTestPlayerBase {
         Assert.assertFalse(CardUtil.haveSameNames(splitCard1, "Other // Dangerous", currentGame));
         Assert.assertFalse(CardUtil.haveSameNames(splitCard1, "Armed // Other", currentGame));
         Assert.assertFalse(CardUtil.haveSameNames(splitCard1, splitCard2));
+
+        // name with face down spells: face down spells don't have names, see https://github.com/magefree/mage/issues/6569
+        Card bearCard = CardRepository.instance.findCard("Balduvian Bears").getCard();
+        Spell normalSpell = new Spell(bearCard, bearCard.getSpellAbility(), playerA.getId(), Zone.HAND);
+        Spell faceDownSpell = new Spell(bearCard, bearCard.getSpellAbility(), playerA.getId(), Zone.HAND);
+        faceDownSpell.setFaceDown(true, currentGame);
+        // normal spell
+        Assert.assertFalse(CardUtil.haveSameNames(normalSpell, "", currentGame));
+        Assert.assertFalse(CardUtil.haveSameNames(normalSpell, "Other", currentGame));
+        Assert.assertFalse(CardUtil.haveSameNames(normalSpell, EmptyNames.FACE_DOWN_CREATURE.toString(), currentGame));
+        Assert.assertTrue(CardUtil.haveSameNames(normalSpell, "Balduvian Bears", currentGame));
+        // face down spell
+        Assert.assertFalse(CardUtil.haveSameNames(faceDownSpell, "", currentGame));
+        Assert.assertFalse(CardUtil.haveSameNames(faceDownSpell, "Other", currentGame));
+        Assert.assertFalse(CardUtil.haveSameNames(faceDownSpell, EmptyNames.FACE_DOWN_CREATURE.toString(), currentGame));
+        Assert.assertFalse(CardUtil.haveSameNames(faceDownSpell, "Balduvian Bears", currentGame));
     }
 
     @Test
diff --git a/Mage/src/main/java/mage/filter/predicate/mageobject/NamePredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/NamePredicate.java
index bc266d2718..fbc763c73a 100644
--- a/Mage/src/main/java/mage/filter/predicate/mageobject/NamePredicate.java
+++ b/Mage/src/main/java/mage/filter/predicate/mageobject/NamePredicate.java
@@ -38,10 +38,13 @@ public class NamePredicate implements Predicate<MageObject> {
             return CardUtil.haveSameNames(name, card.getLeftHalfCard().getName(), this.ignoreMtgRuleForEmptyNames) ||
                     CardUtil.haveSameNames(name, card.getRightHalfCard().getName(), this.ignoreMtgRuleForEmptyNames) ||
                     CardUtil.haveSameNames(name, card.getName(), this.ignoreMtgRuleForEmptyNames);
+        } else if (input instanceof Spell && ((Spell) input).isFaceDown(game)) {
+            // face down spells don't have names, so it's not equal, see https://github.com/magefree/mage/issues/6569
+            return false;
         } else {
             if (name.contains(" // ")) {
                 String leftName = name.substring(0, name.indexOf(" // "));
-                String rightName = name.substring(name.indexOf(" // ") + 4, name.length());
+                String rightName = name.substring(name.indexOf(" // ") + 4);
                 return CardUtil.haveSameNames(leftName, input.getName(), this.ignoreMtgRuleForEmptyNames) ||
                         CardUtil.haveSameNames(rightName, input.getName(), this.ignoreMtgRuleForEmptyNames);
             } else {
@@ -52,6 +55,6 @@ public class NamePredicate implements Predicate<MageObject> {
 
     @Override
     public String toString() {
-        return "Name(" + name + ')';
+        return "Name (" + name + ')';
     }
 }

From d34b73bd789482e21db08e1ce6cb39a34bca99c8 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 4 Jun 2020 18:40:23 -0400
Subject: [PATCH 097/586] added M21 set file

---
 Mage.Sets/src/mage/sets/CoreSet2021.java | 36 ++++++++++++++++++++++++
 Utils/known-sets.txt                     |  1 +
 Utils/mtg-cards-data.txt                 |  2 ++
 Utils/mtg-sets-data.txt                  |  1 +
 4 files changed, 40 insertions(+)
 create mode 100644 Mage.Sets/src/mage/sets/CoreSet2021.java

diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
new file mode 100644
index 0000000000..7ce028c0be
--- /dev/null
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -0,0 +1,36 @@
+package mage.sets;
+
+import mage.cards.ExpansionSet;
+import mage.cards.repository.CardInfo;
+import mage.constants.Rarity;
+import mage.constants.SetType;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author TheElk801
+ */
+public final class CoreSet2021 extends ExpansionSet {
+
+    private static final CoreSet2021 instance = new CoreSet2021();
+
+    public static CoreSet2021 getInstance() {
+        return instance;
+    }
+
+    private final List<CardInfo> savedSpecialLand = new ArrayList<>();
+
+    private CoreSet2021() {
+        super("Core Set 2021", "M21", ExpansionSet.buildDate(2020, 7, 3), SetType.CORE);
+        this.hasBoosters = true;
+        this.hasBasicLands = false; // change when basics are available
+        this.numBoosterSpecial = 0;
+        this.numBoosterLands = 1;
+        this.numBoosterCommon = 10;
+        this.numBoosterUncommon = 3;
+        this.numBoosterRare = 1;
+        this.ratioBoosterMythic = 8;
+        this.maxCardNumberInBooster = 274;
+    }
+}
diff --git a/Utils/known-sets.txt b/Utils/known-sets.txt
index cc52013a19..2bcba03814 100644
--- a/Utils/known-sets.txt
+++ b/Utils/known-sets.txt
@@ -35,6 +35,7 @@ Conflux|Conflux|
 Conspiracy: Take the Crown|ConspiracyTakeTheCrown|
 Core Set 2019|CoreSet2019|
 Core Set 2020|CoreSet2020|
+Core Set 2021|CoreSet2021|
 Dark Ascension|DarkAscension|
 Darksteel|Darksteel|
 Dissension|Dissension|
diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt
index e9e4d9ceda..08865e4121 100644
--- a/Utils/mtg-cards-data.txt
+++ b/Utils/mtg-cards-data.txt
@@ -37438,3 +37438,5 @@ Gyruda, Doom of Depths|Ikoria: Lair of Behemoths|384|R|{4}{U/B}{U/B}|Legendary C
 Mysterious Egg|Ikoria: Lair of Behemoths|385|C|{1}|Creature - Egg|0|2|Whenever this creature mutates, put a +1/+1 counter on it.|
 Dirge Bat|Ikoria: Lair of Behemoths|386|R|{2}{B}{B}|Creature - Bat|3|3|Mutate {4}{B}{B}$Flash$Flying$Whenever this creature mutates, destroy target creature or planeswalker an opponent controls.|
 Crystalline Giant|Ikoria: Lair of Behemoths|387|R|{3}|Artifact Creature - Giant|3|3|At the beginning of combat on your turn, choose a kind of counter at random that Crystalline Giant doesn't have on it from among flying, first strike, deathtouch, hexproof, lifelink, menace, reach, trample, vigilance, or +1/+1. Put a counter of that kind on Crystalline Giant.|
+Mangara, the Diplomat|Core Set 2021|27|M|{3}{W}|Legendary Creature - Human Cleric|2|4|Lifelink$Whenever an opponent attacks with creatures, if two or more of those creatures are attacking you and/or a planeswalker you control, draw a card.$Whenever an opponent casts their second spell each turn, draw a card.|
+Double Vision|Core Set 2021|142|R|{3}{R}{R}|Enchantment|||Whenever you cast your first instant or sorcery spell each turn, copy that spell. You may choose new targets for the copy.|
diff --git a/Utils/mtg-sets-data.txt b/Utils/mtg-sets-data.txt
index 276354761c..0d8b90c562 100644
--- a/Utils/mtg-sets-data.txt
+++ b/Utils/mtg-sets-data.txt
@@ -131,6 +131,7 @@ Magic 2014|M14|
 Magic 2015|M15|
 Core Set 2019|M19|
 Core Set 2020|M20|
+Core Set 2021|M21|
 Masters 25|A25|
 Magic: The Gathering-Commander|CMD|
 Magic: The Gathering-Conspiracy|CNS|

From b8c1ff6e42be9aab9a5d219496658407be9302c4 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 4 Jun 2020 18:40:31 -0400
Subject: [PATCH 098/586] Implemented Mangara, the Diplomat

---
 .../src/mage/cards/m/MangaraTheDiplomat.java  | 131 ++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |   2 +
 2 files changed, 133 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/m/MangaraTheDiplomat.java

diff --git a/Mage.Sets/src/mage/cards/m/MangaraTheDiplomat.java b/Mage.Sets/src/mage/cards/m/MangaraTheDiplomat.java
new file mode 100644
index 0000000000..6414f41f75
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/m/MangaraTheDiplomat.java
@@ -0,0 +1,131 @@
+package mage.cards.m;
+
+import mage.MageInt;
+import mage.abilities.TriggeredAbilityImpl;
+import mage.abilities.effects.common.DrawCardSourceControllerEffect;
+import mage.abilities.keyword.LifelinkAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.SuperType;
+import mage.constants.Zone;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+import mage.players.Player;
+import mage.watchers.common.CastSpellLastTurnWatcher;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class MangaraTheDiplomat extends CardImpl {
+
+    public MangaraTheDiplomat(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}");
+
+        this.addSuperType(SuperType.LEGENDARY);
+        this.subtype.add(SubType.HUMAN);
+        this.subtype.add(SubType.CLERIC);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(4);
+
+        // Lifelink
+        this.addAbility(LifelinkAbility.getInstance());
+
+        // Whenever an opponent attacks with creatures, if two or more of those creatures are attacking you and/or a planeswalker you control, draw a card.
+        this.addAbility(new MangaraTheDiplomatAttackTriggeredAbility());
+
+        // Whenever an opponent casts their second spell each turn, draw a card.
+        this.addAbility(new MangaraTheDiplomatCastTriggeredAbility());
+    }
+
+    private MangaraTheDiplomat(final MangaraTheDiplomat card) {
+        super(card);
+    }
+
+    @Override
+    public MangaraTheDiplomat copy() {
+        return new MangaraTheDiplomat(this);
+    }
+}
+
+class MangaraTheDiplomatAttackTriggeredAbility extends TriggeredAbilityImpl {
+
+    MangaraTheDiplomatAttackTriggeredAbility() {
+        super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1));
+    }
+
+    private MangaraTheDiplomatAttackTriggeredAbility(final MangaraTheDiplomatAttackTriggeredAbility ability) {
+        super(ability);
+    }
+
+    @Override
+    public boolean checkEventType(GameEvent event, Game game) {
+        return event.getType() == GameEvent.EventType.DECLARED_ATTACKERS;
+    }
+
+    @Override
+    public boolean checkTrigger(GameEvent event, Game game) {
+        Player player = game.getPlayer(getControllerId());
+        return player != null
+                && player.hasOpponent(game.getActivePlayerId(), game)
+                && game
+                .getCombat()
+                .getAttackers()
+                .stream()
+                .map(uuid -> game.getCombat().getDefendingPlayerId(uuid, game))
+                .filter(getControllerId()::equals)
+                .count() >= 2;
+    }
+
+    @Override
+    public MangaraTheDiplomatAttackTriggeredAbility copy() {
+        return new MangaraTheDiplomatAttackTriggeredAbility(this);
+    }
+
+    @Override
+    public String getRule() {
+        return "Whenever an opponent attacks with creatures, " +
+                "if two or more of those creatures are attacking you " +
+                "and/or a planeswalker you control, draw a card.";
+    }
+}
+
+
+class MangaraTheDiplomatCastTriggeredAbility extends TriggeredAbilityImpl {
+
+    MangaraTheDiplomatCastTriggeredAbility() {
+        super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1));
+    }
+
+    private MangaraTheDiplomatCastTriggeredAbility(final MangaraTheDiplomatCastTriggeredAbility ability) {
+        super(ability);
+    }
+
+    @Override
+    public boolean checkEventType(GameEvent event, Game game) {
+        return event.getType() == GameEvent.EventType.SPELL_CAST;
+    }
+
+    @Override
+    public boolean checkTrigger(GameEvent event, Game game) {
+        Player player = game.getPlayer(getControllerId());
+        CastSpellLastTurnWatcher watcher = game.getState().getWatcher(CastSpellLastTurnWatcher.class);
+        return player != null
+                && watcher != null
+                && player.hasOpponent(event.getPlayerId(), game)
+                && watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(event.getPlayerId()) == 3;
+    }
+
+    @Override
+    public MangaraTheDiplomatCastTriggeredAbility copy() {
+        return new MangaraTheDiplomatCastTriggeredAbility(this);
+    }
+
+    @Override
+    public String getRule() {
+        return "Whenever an opponent casts their second spell each turn, draw a card.";
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 7ce028c0be..e168cdd015 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -32,5 +32,7 @@ public final class CoreSet2021 extends ExpansionSet {
         this.numBoosterRare = 1;
         this.ratioBoosterMythic = 8;
         this.maxCardNumberInBooster = 274;
+
+        cards.add(new SetCardInfo("Mangara, the Diplomat", 27, Rarity.MYTHIC, mage.cards.m.MangaraTheDiplomat.class));
     }
 }

From 3dcdcfc2ed81f06e15957578da53ca6af7a1ed8d Mon Sep 17 00:00:00 2001
From: htrajan <htrajan2002@yahoo.com>
Date: Thu, 4 Jun 2020 21:43:03 -0700
Subject: [PATCH 099/586] Implement DoubleVision

---
 Mage.Sets/src/mage/cards/d/DoubleVision.java | 86 ++++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java     |  1 +
 2 files changed, 87 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/d/DoubleVision.java

diff --git a/Mage.Sets/src/mage/cards/d/DoubleVision.java b/Mage.Sets/src/mage/cards/d/DoubleVision.java
new file mode 100644
index 0000000000..f71749174e
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/d/DoubleVision.java
@@ -0,0 +1,86 @@
+package mage.cards.d;
+
+import mage.abilities.common.SpellCastControllerTriggeredAbility;
+import mage.abilities.effects.common.CopyTargetSpellEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.filter.common.FilterInstantOrSorcerySpell;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+import mage.game.stack.Spell;
+import mage.target.targetpointer.FixedTarget;
+import mage.watchers.common.SpellsCastWatcher;
+
+import java.util.List;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+/**
+ * @author htrajan
+ */
+public final class DoubleVision extends CardImpl {
+
+    public DoubleVision(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{R}{R}");
+
+        // Whenever you cast your first instant or sorcery spell each turn, copy that spell. You may choose new targets for the copy.
+        this.addAbility(new DoubleVisionCopyTriggeredAbility(), new SpellsCastWatcher());
+    }
+
+    private DoubleVision(final DoubleVision card) {
+        super(card);
+    }
+
+    @Override
+    public DoubleVision copy() {
+        return new DoubleVision(this);
+    }
+}
+
+class DoubleVisionCopyTriggeredAbility extends SpellCastControllerTriggeredAbility {
+
+    DoubleVisionCopyTriggeredAbility() {
+        super(new CopyTargetSpellEffect(true), new FilterInstantOrSorcerySpell(), false);
+    }
+
+    DoubleVisionCopyTriggeredAbility(DoubleVisionCopyTriggeredAbility ability) {
+        super(ability);
+    }
+
+    @Override
+    public DoubleVisionCopyTriggeredAbility copy() {
+        return new DoubleVisionCopyTriggeredAbility(this);
+    }
+
+    @Override
+    public boolean checkTrigger(GameEvent event, Game game) {
+        if (super.checkTrigger(event, game)) {
+            Spell spell = game.getStack().getSpell(event.getTargetId());
+            if (isFirstInstantOrSorceryCastByPlayerOnTurn(spell, game)) {
+                this.getEffects().get(0).setTargetPointer(new FixedTarget(spell.getId()));
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean isFirstInstantOrSorceryCastByPlayerOnTurn(Spell spell, Game game) {
+        if (spell != null) {
+            SpellsCastWatcher watcher = game.getState().getWatcher(SpellsCastWatcher.class);
+            if (watcher != null) {
+                List<Spell> eligibleSpells = watcher.getSpellsCastThisTurn(this.getControllerId())
+                        .stream()
+                        .filter(s -> s.isInstant() || s.isSorcery())
+                        .collect(Collectors.toList());
+                return eligibleSpells.size() == 1 && eligibleSpells.get(0).getId().equals(spell.getId());
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public String getRule() {
+        return "Whenever you cast your first instant or sorcery spell each turn, copy that spell. You may choose new targets for the copy.";
+    }
+}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index e168cdd015..92bf871119 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -33,6 +33,7 @@ public final class CoreSet2021 extends ExpansionSet {
         this.ratioBoosterMythic = 8;
         this.maxCardNumberInBooster = 274;
 
+        cards.add(new SetCardInfo("Double Vision", 142, Rarity.RARE, mage.cards.d.DoubleVision.class));
         cards.add(new SetCardInfo("Mangara, the Diplomat", 27, Rarity.MYTHIC, mage.cards.m.MangaraTheDiplomat.class));
     }
 }

From b0ad0126ca14ef07743081797ca93c9a840781aa Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Fri, 5 Jun 2020 10:24:29 +0200
Subject: [PATCH 100/586] * Phase info added to the /Fix command. Some code
 cleanup.

---
 .../java/mage/server/game/GameController.java | 46 +++++++++----------
 Mage.Sets/src/mage/cards/t/ThassasOracle.java |  5 +-
 2 files changed, 27 insertions(+), 24 deletions(-)

diff --git a/Mage.Server/src/main/java/mage/server/game/GameController.java b/Mage.Server/src/main/java/mage/server/game/GameController.java
index a1963f587c..883df510da 100644
--- a/Mage.Server/src/main/java/mage/server/game/GameController.java
+++ b/Mage.Server/src/main/java/mage/server/game/GameController.java
@@ -1259,10 +1259,10 @@ public class GameController implements GameCallback {
                         .collect(Collectors.joining(", ")));
 
         StringBuilder sb = new StringBuilder();
-        sb.append("<font color='red'>FIX command called by " + user.getName() + "</font>");
+        sb.append("<font color='red'>FIX command called by ").append(user.getName()).append("</font>");
         sb.append("<font size='-2'>"); // font resize start for all next logs
-        sb.append("<br>Game ID: " + game.getId());
-
+        sb.append("<br>Game ID: ").append(game.getId());
+        sb.append("<br>Phase: ").append(game.getTurn().getPhaseType().toString()).append(" Step: ").append(game.getTurn().getStepType().toString());
         // pings info
         sb.append("<br>");
         sb.append(getPingsInfo());
@@ -1272,7 +1272,7 @@ public class GameController implements GameCallback {
 
         // fix active
         Player playerActive = game.getPlayer(state.getActivePlayerId());
-        sb.append("<br>Fixing active player: " + getName(playerActive));
+        sb.append("<br>Fixing active player: ").append(getName(playerActive));
         if (playerActive != null && !playerActive.canRespond()) {
             fixActions.add("active player fix");
 
@@ -1280,16 +1280,16 @@ public class GameController implements GameCallback {
             sb.append("<br>Try to concede...");
             playerActive.concede(game);
             playerActive.leave(); // abort any wait response actions
-            sb.append(" (" + asWarning("OK") + ", concede done)");
+            sb.append(" (").append(asWarning("OK")).append(", concede done)");
 
             sb.append("<br>Try to skip step...");
             Phase currentPhase = game.getPhase();
             if (currentPhase != null) {
                 currentPhase.getStep().skipStep(game, state.getActivePlayerId());
                 fixedAlready = true;
-                sb.append(" (" + asWarning("OK") + ", skip step done)");
+                sb.append(" (").append(asWarning("OK")).append(", skip step done)");
             } else {
-                sb.append(" (" + asBad("FAIL") + ", step is null)");
+                sb.append(" (").append(asBad("FAIL")).append(", step is null)");
             }
         } else {
             sb.append(playerActive != null ? " (" + asGood("OK") + ", can respond)" : " (" + asGood("OK") + ", no player)");
@@ -1297,7 +1297,7 @@ public class GameController implements GameCallback {
 
         // fix lost choosing dialog
         Player choosingPlayer = game.getPlayer(state.getChoosingPlayerId());
-        sb.append("<br>Fixing choosing player: " + getName(choosingPlayer));
+        sb.append("<br>Fixing choosing player: ").append(getName(choosingPlayer));
         if (choosingPlayer != null && !choosingPlayer.canRespond()) {
             fixActions.add("choosing player fix");
 
@@ -1305,7 +1305,7 @@ public class GameController implements GameCallback {
             sb.append("<br>Try to concede...");
             choosingPlayer.concede(game);
             choosingPlayer.leave(); // abort any wait response actions
-            sb.append(" (" + asWarning("OK") + ", concede done)");
+            sb.append(" (").append(asWarning("OK")).append(", concede done)");
 
             sb.append("<br>Try to skip step...");
             if (fixedAlready) {
@@ -1315,9 +1315,9 @@ public class GameController implements GameCallback {
                 if (currentPhase != null) {
                     currentPhase.getStep().skipStep(game, state.getActivePlayerId());
                     fixedAlready = true;
-                    sb.append(" (" + asWarning("OK") + ", skip step done)");
+                    sb.append(" (").append(asWarning("OK")).append(", skip step done)");
                 } else {
-                    sb.append(" (" + asBad("FAIL") + ", step is null)");
+                    sb.append(" (").append(asBad("FAIL")).append(", step is null)");
                 }
             }
         } else {
@@ -1326,7 +1326,7 @@ public class GameController implements GameCallback {
 
         // fix lost priority
         Player priorityPlayer = game.getPlayer(state.getPriorityPlayerId());
-        sb.append("<br>Fixing priority player: " + getName(priorityPlayer));
+        sb.append("<br>Fixing priority player: ").append(getName(priorityPlayer));
         if (priorityPlayer != null && !priorityPlayer.canRespond()) {
             fixActions.add("priority player fix");
 
@@ -1334,19 +1334,19 @@ public class GameController implements GameCallback {
             sb.append("<br>Try to concede...");
             priorityPlayer.concede(game);
             priorityPlayer.leave(); // abort any wait response actions
-            sb.append(" (" + asWarning("OK") + ", concede done)");
+            sb.append(" (").append(asWarning("OK")).append(", concede done)");
 
             sb.append("<br>Try to skip step...");
             if (fixedAlready) {
-                sb.append(" (" + asWarning("OK") + ", already skipped before)");
+                sb.append(" (").append(asWarning("OK")).append(", already skipped before)");
             } else {
                 Phase currentPhase = game.getPhase();
                 if (currentPhase != null) {
                     currentPhase.getStep().skipStep(game, state.getActivePlayerId());
                     fixedAlready = true;
-                    sb.append(" (" + asWarning("OK") + ", skip step done)");
+                    sb.append(" (").append(asWarning("OK")).append(", skip step done)");
                 } else {
-                    sb.append(" (" + asBad("FAIL") + ", step is null)");
+                    sb.append(" (").append(asBad("FAIL")).append(", step is null)");
                 }
             }
         } else {
@@ -1356,10 +1356,10 @@ public class GameController implements GameCallback {
         // fix timeout
         sb.append("<br>Fixing future timeout: ");
         if (futureTimeout != null) {
-            sb.append("cancelled?=" + futureTimeout.isCancelled());
-            sb.append("...done?=" + futureTimeout.isDone());
+            sb.append("cancelled?=").append(futureTimeout.isCancelled());
+            sb.append("...done?=").append(futureTimeout.isDone());
             int delay = (int) futureTimeout.getDelay(TimeUnit.SECONDS);
-            sb.append("...getDelay?=" + delay);
+            sb.append("...getDelay?=").append(delay);
             if (delay < 25) {
                 fixActions.add("future timeout fix");
 
@@ -1367,12 +1367,12 @@ public class GameController implements GameCallback {
                 sb.append("<br>Try to pass...");
                 PassAbility pass = new PassAbility();
                 game.endTurn(pass);
-                sb.append(" (" + asWarning("OK") + ", pass done)");
+                sb.append(" (").append(asWarning("OK")).append(", pass done)");
             } else {
-                sb.append(" (" + asGood("OK") + ", delay > 25)");
+                sb.append(" (").append(asGood("OK")).append(", delay > 25)");
             }
         } else {
-            sb.append(" (" + asGood("OK") + ", timeout is not using)");
+            sb.append(" (").append(asGood("OK")).append(", timeout is not using)");
         }
 
         // TODO: fix non started game (send game started event to user?)
@@ -1382,7 +1382,7 @@ public class GameController implements GameCallback {
             fixActions.add("none");
         }
         String appliedFixes = fixActions.stream().collect(Collectors.joining(", "));
-        sb.append("<br>Applied fixes: " + appliedFixes);
+        sb.append("<br>Applied fixes: ").append(appliedFixes);
         sb.append("</font>"); // font resize end
         sb.append("<br>");
 
diff --git a/Mage.Sets/src/mage/cards/t/ThassasOracle.java b/Mage.Sets/src/mage/cards/t/ThassasOracle.java
index a7ee33cc0e..49fca6d9b8 100644
--- a/Mage.Sets/src/mage/cards/t/ThassasOracle.java
+++ b/Mage.Sets/src/mage/cards/t/ThassasOracle.java
@@ -29,7 +29,10 @@ public final class ThassasOracle extends CardImpl {
         this.power = new MageInt(1);
         this.toughness = new MageInt(3);
 
-        // When Thassa's Oracle enters the battlefield, look at the top X cards of your library, where X is your devotion to blue. Put one of them on top of your library and the rest on the bottom of your library in a random order. If X is greater than or equal to the number of cards in your library, you win the game.
+        // When Thassa's Oracle enters the battlefield, look at the top X cards of your library, where X is your devotion to blue.
+        // Put up to one of them on top of your library and the rest on the bottom of your library in a random order. 
+        // If X is greater than or equal to the number of cards in your library, you win the game. 
+        // (Each Blue in the mana costs of permanents you control counts toward your devotion to blue.)
         this.addAbility(new EntersBattlefieldTriggeredAbility(new ThassasOracleEffect())
                 .addHint(DevotionCount.U.getHint()));
     }

From 659610eed40c494635a1e9dfe0d37aeb83e5219b Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Fri, 5 Jun 2020 07:42:41 -0400
Subject: [PATCH 101/586] fixed Mangara, the Diplomat spell count

---
 Mage.Sets/src/mage/cards/m/MangaraTheDiplomat.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Mage.Sets/src/mage/cards/m/MangaraTheDiplomat.java b/Mage.Sets/src/mage/cards/m/MangaraTheDiplomat.java
index 6414f41f75..6ecfbad160 100644
--- a/Mage.Sets/src/mage/cards/m/MangaraTheDiplomat.java
+++ b/Mage.Sets/src/mage/cards/m/MangaraTheDiplomat.java
@@ -116,7 +116,7 @@ class MangaraTheDiplomatCastTriggeredAbility extends TriggeredAbilityImpl {
         return player != null
                 && watcher != null
                 && player.hasOpponent(event.getPlayerId(), game)
-                && watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(event.getPlayerId()) == 3;
+                && watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(event.getPlayerId()) == 2;
     }
 
     @Override

From 67b4e8d668017fe7acc3600878e5556df4f57914 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Fri, 5 Jun 2020 15:12:42 +0200
Subject: [PATCH 102/586] * Marrow-Gnawer - Fixed casting cost order and rules
 text.

---
 Mage.Sets/src/mage/cards/a/AetherwindBasker.java         | 1 -
 Mage.Sets/src/mage/cards/a/ArmoredAscension.java         | 2 ++
 Mage.Sets/src/mage/cards/m/MarrowGnawer.java             | 9 +++++----
 .../common/PermanentsOnBattlefieldCount.java             | 5 +++++
 4 files changed, 12 insertions(+), 5 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/a/AetherwindBasker.java b/Mage.Sets/src/mage/cards/a/AetherwindBasker.java
index e2bc62492d..3ca50e8766 100644
--- a/Mage.Sets/src/mage/cards/a/AetherwindBasker.java
+++ b/Mage.Sets/src/mage/cards/a/AetherwindBasker.java
@@ -17,7 +17,6 @@ import mage.constants.Duration;
 import mage.constants.SubType;
 import mage.constants.Zone;
 import mage.filter.StaticFilters;
-import mage.filter.common.FilterControlledCreaturePermanent;
 
 /**
  *
diff --git a/Mage.Sets/src/mage/cards/a/ArmoredAscension.java b/Mage.Sets/src/mage/cards/a/ArmoredAscension.java
index ad9a117331..25b7a1516b 100644
--- a/Mage.Sets/src/mage/cards/a/ArmoredAscension.java
+++ b/Mage.Sets/src/mage/cards/a/ArmoredAscension.java
@@ -34,11 +34,13 @@ public final class ArmoredAscension extends CardImpl {
 
         this.subtype.add(SubType.AURA);
 
+        // Enchant creature
         TargetPermanent auraTarget = new TargetCreaturePermanent();
         this.getSpellAbility().addTarget(auraTarget);
         this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature));
         this.addAbility(new EnchantAbility(auraTarget.getTargetName()));
 
+        // Enchanted creature gets +1/+1 for each Plains you control and has flying.
         PermanentsOnBattlefieldCount amount = new PermanentsOnBattlefieldCount(filter, 1);
         SimpleStaticAbility ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(amount, amount, Duration.WhileOnBattlefield));
         ability.addEffect(new GainAbilityAttachedEffect(FlyingAbility.getInstance(), AttachmentType.AURA));
diff --git a/Mage.Sets/src/mage/cards/m/MarrowGnawer.java b/Mage.Sets/src/mage/cards/m/MarrowGnawer.java
index 3f136ef486..598c47ec00 100644
--- a/Mage.Sets/src/mage/cards/m/MarrowGnawer.java
+++ b/Mage.Sets/src/mage/cards/m/MarrowGnawer.java
@@ -51,10 +51,11 @@ public final class MarrowGnawer extends CardImpl {
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(FearAbility.getInstance(), Duration.WhileOnBattlefield, filterFear)));
 
         // {T}, Sacrifice a Rat: create X 1/1 black Rat creature tokens, where X is the number of Rats you control.
-        Ability ability;
-        String abilityText = "create X 1/1 black Rat creature tokens, where X is the number of Rats you control.";
-        ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new RatToken(),new PermanentsOnBattlefieldCount(filter3)).setText(abilityText), new SacrificeTargetCost(new TargetControlledPermanent(filterSacrifice)));
-        ability.addCost(new TapSourceCost());
+        Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, 
+                new CreateTokenEffect(new RatToken(), new PermanentsOnBattlefieldCount(filter3, null))
+                        /*.setText("create X 1/1 black Rat creature tokens, where X is the number of Rats you control")*/, 
+                new TapSourceCost());
+        ability.addCost( new SacrificeTargetCost(new TargetControlledPermanent(filterSacrifice)));
         this.addAbility(ability);
     }
 
diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/PermanentsOnBattlefieldCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/PermanentsOnBattlefieldCount.java
index 087f375dc0..a9a85c54a3 100644
--- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/PermanentsOnBattlefieldCount.java
+++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/PermanentsOnBattlefieldCount.java
@@ -24,6 +24,11 @@ public class PermanentsOnBattlefieldCount implements DynamicValue {
         this(filter, 1);
     }
 
+    /**
+     * 
+     * @param filter
+     * @param multiplier 
+     */
     public PermanentsOnBattlefieldCount(FilterPermanent filter, Integer multiplier) {
         this.filter = filter;
         this.multiplier = multiplier;

From dc54bb2ddf3d8d0ed8839d3c2201c5b8c9df2f41 Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Fri, 5 Jun 2020 16:45:20 -0500
Subject: [PATCH 103/586] - Fixed #6605

---
 Mage.Sets/src/mage/cards/d/DecoyGambit.java | 60 +++++++++++++++++----
 1 file changed, 51 insertions(+), 9 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/d/DecoyGambit.java b/Mage.Sets/src/mage/cards/d/DecoyGambit.java
index 99c29db62c..d238558379 100644
--- a/Mage.Sets/src/mage/cards/d/DecoyGambit.java
+++ b/Mage.Sets/src/mage/cards/d/DecoyGambit.java
@@ -6,7 +6,6 @@ import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.Outcome;
-import mage.constants.Zone;
 import mage.filter.FilterPermanent;
 import mage.filter.common.FilterCreaturePermanent;
 import mage.filter.predicate.permanent.ControllerIdPredicate;
@@ -16,12 +15,14 @@ import mage.players.Player;
 import mage.target.Target;
 import mage.target.TargetPermanent;
 import mage.target.targetadjustment.TargetAdjuster;
-
 import java.util.Collection;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
 import java.util.UUID;
 import java.util.stream.Collectors;
+import mage.abilities.condition.Condition;
+import mage.constants.Zone;
 
 /**
  * @author TheElk801
@@ -31,7 +32,8 @@ public final class DecoyGambit extends CardImpl {
     public DecoyGambit(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}");
 
-        // For each opponent, choose up to one target creature that player controls, then return that creature to its owner's hand unless its controller has you draw a card.
+        // For each opponent, choose up to one target creature that player controls, 
+        // then return that creature to its owner's hand unless its controller has you draw a card.
         this.getSpellAbility().addEffect(new DecoyGambitEffect());
         this.getSpellAbility().setTargetAdjuster(DecoyGambitAdjuster.instance);
     }
@@ -70,8 +72,8 @@ class DecoyGambitEffect extends OneShotEffect {
 
     DecoyGambitEffect() {
         super(Outcome.Benefit);
-        staticText = "For each opponent, choose up to one target creature that player controls, " +
-                "then return that creature to its owner's hand unless its controller has you draw a card.";
+        staticText = "For each opponent, choose up to one target creature that player controls, "
+                + "then return that creature to its owner's hand unless its controller has you draw a card.";
     }
 
     private DecoyGambitEffect(final DecoyGambitEffect effect) {
@@ -86,6 +88,8 @@ class DecoyGambitEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player controller = game.getPlayer(source.getControllerId());
+        HashSet<Permanent> permanentToHand = new HashSet();
+        int numberOfCardsToDraw = 0;
         if (controller == null) {
             return false;
         }
@@ -98,16 +102,54 @@ class DecoyGambitEffect extends OneShotEffect {
                 .filter(Objects::nonNull)
                 .collect(Collectors.toList());
         for (Permanent permanent : permanents) {
+            // If a creature targeted by Decoy Gambit changes controller, it’s no longer a legal target.
+            new DecoyGambitCondition(permanent).apply(game, source); // save current controller
             Player player = game.getPlayer(permanent.getControllerId());
             if (player == null) {
                 continue;
             }
-            if (player.chooseUse(outcome, "Have " + controller.getName() + " draw a card? If you don't, " +
-                    permanent.getName() + " will be returned to its owner's hand.", source, game)
-                    && controller.drawCards(1, source.getSourceId(), game) > 0) {
-                player.moveCards(permanent, Zone.HAND, source, game);
+            if (player.chooseUse(outcome, "Have " + controller.getName() + " draw a card? If you don't, "
+                    + permanent.getName() + " will be returned to its owner's hand.", source, game)) {
+                game.informPlayers(player.getName() + " chose to have " + controller.getName() + " draw a card.");
+                numberOfCardsToDraw += 1;
+            } else {
+                game.informPlayers(player.getName() + " chose to have their creature returned to their hand.");
+                permanentToHand.add(permanent);
+            }
+        }
+        /*
+        As the Decoy Gambit resolves, first the next opponent in turn order (or, if it’s an opponent’s 
+        turn, the opponent whose turn it is) chooses whether you’ll draw a card or return their creature 
+        that was targeted to its owner’s hand, then each other opponent in turn order does so knowing 
+        the choices made before them. After all choices are made, you draw the appropriate number of 
+        cards. After you’ve drawn, the appropriate creatures are all simultaneously returned to their owners’ hands.
+         */
+        controller.drawCards(numberOfCardsToDraw, source.getSourceId(), game);
+        for (Permanent creature : permanentToHand) {
+            if (creature != null
+                    && new DecoyGambitCondition(creature).apply(game, source)) { // same controller required
+                creature.moveToZone(Zone.HAND, source.getSourceId(), game, false);
             }
         }
         return true;
     }
 }
+
+class DecoyGambitCondition implements Condition {
+
+    private UUID controllerId;
+    private final Permanent permanent;
+
+    DecoyGambitCondition(Permanent permanent) {
+        this.permanent = permanent;
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        if (controllerId == null) { // is the original controller set
+            controllerId = permanent.getControllerId(); // original controller set
+        }
+        return (permanent != null
+                && Objects.equals(controllerId, permanent.getControllerId()));
+    }
+}

From 6980fb169055cd7cdc44e90c0e317abfc3a789fb Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Fri, 5 Jun 2020 20:11:50 -0400
Subject: [PATCH 104/586] updated M21 spoiler and reprints

---
 Mage.Sets/src/mage/sets/CoreSet2021.java | 14 ++++++-
 Utils/mtg-cards-data.txt                 | 47 ++++++++++++++++++++++++
 2 files changed, 60 insertions(+), 1 deletion(-)

diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index e168cdd015..be34ac3a06 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -24,7 +24,7 @@ public final class CoreSet2021 extends ExpansionSet {
     private CoreSet2021() {
         super("Core Set 2021", "M21", ExpansionSet.buildDate(2020, 7, 3), SetType.CORE);
         this.hasBoosters = true;
-        this.hasBasicLands = false; // change when basics are available
+        this.hasBasicLands = true;
         this.numBoosterSpecial = 0;
         this.numBoosterLands = 1;
         this.numBoosterCommon = 10;
@@ -33,6 +33,18 @@ public final class CoreSet2021 extends ExpansionSet {
         this.ratioBoosterMythic = 8;
         this.maxCardNumberInBooster = 274;
 
+        cards.add(new SetCardInfo("Azusa, Lost but Seeking", 173, Rarity.RARE, mage.cards.a.AzusaLostButSeeking.class));
+        cards.add(new SetCardInfo("Baneslayer Angel", 4, Rarity.MYTHIC, mage.cards.b.BaneslayerAngel.class));
+        cards.add(new SetCardInfo("Containment Priest", 314, Rarity.RARE, mage.cards.c.ContainmentPriest.class));
+        cards.add(new SetCardInfo("Fierce Empath", 181, Rarity.UNCOMMON, mage.cards.f.FierceEmpath.class));
+        cards.add(new SetCardInfo("Grim Tutor", 103, Rarity.MYTHIC, mage.cards.g.GrimTutor.class));
+        cards.add(new SetCardInfo("Island", 310, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Jeskai Elder", 53, Rarity.UNCOMMON, mage.cards.j.JeskaiElder.class));
         cards.add(new SetCardInfo("Mangara, the Diplomat", 27, Rarity.MYTHIC, mage.cards.m.MangaraTheDiplomat.class));
+        cards.add(new SetCardInfo("Quirion Dryad", 198, Rarity.UNCOMMON, mage.cards.q.QuirionDryad.class));
+        cards.add(new SetCardInfo("Rain of Revelation", 61, Rarity.UNCOMMON, mage.cards.r.RainOfRevelation.class));
+        cards.add(new SetCardInfo("Runed Halo", 32, Rarity.RARE, mage.cards.r.RunedHalo.class));
+        cards.add(new SetCardInfo("Tormod's Crypt", 241, Rarity.UNCOMMON, mage.cards.t.TormodsCrypt.class));
+        cards.add(new SetCardInfo("Ugin, the Spirit Dragon", 1, Rarity.MYTHIC, mage.cards.u.UginTheSpiritDragon.class));
     }
 }
diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt
index 08865e4121..79d88a93f9 100644
--- a/Utils/mtg-cards-data.txt
+++ b/Utils/mtg-cards-data.txt
@@ -37438,5 +37438,52 @@ Gyruda, Doom of Depths|Ikoria: Lair of Behemoths|384|R|{4}{U/B}{U/B}|Legendary C
 Mysterious Egg|Ikoria: Lair of Behemoths|385|C|{1}|Creature - Egg|0|2|Whenever this creature mutates, put a +1/+1 counter on it.|
 Dirge Bat|Ikoria: Lair of Behemoths|386|R|{2}{B}{B}|Creature - Bat|3|3|Mutate {4}{B}{B}$Flash$Flying$Whenever this creature mutates, destroy target creature or planeswalker an opponent controls.|
 Crystalline Giant|Ikoria: Lair of Behemoths|387|R|{3}|Artifact Creature - Giant|3|3|At the beginning of combat on your turn, choose a kind of counter at random that Crystalline Giant doesn't have on it from among flying, first strike, deathtouch, hexproof, lifelink, menace, reach, trample, vigilance, or +1/+1. Put a counter of that kind on Crystalline Giant.|
+Ugin, the Spirit Dragon|Core Set 2021|1|M|{8}|Legendary Planeswalker - Ugin|7|+2: Ugin, the Spirit Dragon deals 3 damage to any target.$−X: Exile each permanent with converted mana cost X or less that's one or more colors.$−10: You gain 7 life, draw seven cards, then put up to seven permanent cards from your hand onto the battlefield.|
+Baneslayer Angel|Core Set 2021|4|M|{3}{W}{W}|Creature - Angel|5|5|Flying, first strike, lifelink, protection from Demons and from Dragons|
 Mangara, the Diplomat|Core Set 2021|27|M|{3}{W}|Legendary Creature - Human Cleric|2|4|Lifelink$Whenever an opponent attacks with creatures, if two or more of those creatures are attacking you and/or a planeswalker you control, draw a card.$Whenever an opponent casts their second spell each turn, draw a card.|
+Pack Leader|Core Set 2021|29|R|{1}{W}|Creature - Dog|2|2|Other Dogs you control get +1/+1.$Whenever Pack Leader attacks, prevent all combat damage that would be dealt this turn to Dogs you control.|
+Runed Halo|Core Set 2021|32|R|{W}{W}|Enchantment|||As Runed Halo enters the battlefield, choose a card name.$You have protection from the chosen name.|
+Jeskai Elder|Core Set 2021|53|U|{1}{U}|Creature - Human Monk|1|2|Prowess$Whenever Jeskai Elder deals combat damage to a player, you may draw a card. If you do, discard a card.|
+Rain of Revelation|Core Set 2021|61|U|{3}{U}|Instant|||Draw three cards, then discard a card.|
+Shipwreck Dowser|Core Set 2021|71|U|{3}{U}{U}|Creature - Merfolk Wizard|3|3|Prowess$When Shipwreck Dowser enters the battlefield, return target instant or sorcery card from your graveyard to your hand.|
+Teferi, Master of Time|Core Set 2021|75|M|{2}{U}{U}|Legendary Planeswalker - Teferi|3|You may activate loyalty abilities of Teferi, Master of Time on any player's turn any time you could cast an instant.$+1: Draw a card, then discard a card.$−3: Target creature you don't control phases out.$−10: Take two extra turns after this one.|
+Teferi's Ageless Insight|Core Set 2021|76|R|{2}{U}{U}|Legendary Enchantment|||If you would draw a card except the first one you draw in each of your draw steps, draw two cards instead.|
+Teferi's Protege|Core Set 2021|77|C|{2}{U}|Creature - Human Wizard|2|3|{1}{U}, {T}: Draw a card, then discard a card.|
+Teferi's Tutelage|Core Set 2021|78|U|{2}{U}|Enchantment|||When Teferi's Tutelage enters the battlefield, draw a card, then discard a card.$Whenever you draw a card, target opponent mills two cards.|
+Grim Tutor|Core Set 2021|103|M|{1}{B}{B}|Sorcery|||Search your library for a card and put that card into your hand, then shuffle your library. You lose 3 life.|
+Liliana, Waker of the Dead|Core Set 2021|108|M|{2}{B}{B}|Legendary Planeswalker - Liliana|4|+1: Each player discards a card. Each opponent who can't loses 3 life.$−3: Target creature gets -X/-X until end of turn, where X is the number of cards in your graveyard.$−7: You get an emblem with "At the beginning of combat on your turn, put target creature card from a graveyard onto the battlefield under your control. It gains haste."|
+Peer into the Abyss|Core Set 2021|117|R|{4}{B}{B}{B}|Sorcery|||Target player draws cards equal to half the number of cards in their library and loses half their life. Round up each time.|
+Vito, Thorn of the Dusk Rose|Core Set 2021|127|R|{2}{B}|Legendary Creature - Vampire Cleric|1|3|Whenever you gain life, target opponent loses that much life.${3}{B}{B}: Creatures you control gain lifelink until end of turn.|
 Double Vision|Core Set 2021|142|R|{3}{R}{R}|Enchantment|||Whenever you cast your first instant or sorcery spell each turn, copy that spell. You may choose new targets for the copy.|
+Gadrak, the Crown-Scourge|Core Set 2021|146|R|{2}{R}|Legendary Creature - Dragon|5|4|Flying$Gadrak, the Crown-Scourge can't attack unless you control four or more artifacts.$At the beginning of your end step, create a Treasure token for each nontoken creature that died this turn.|
+Azusa, Lost but Seeking|Core Set 2021|173|R|{2}{G}|Legendary Creature - Human Monk|1|2|You may play two additional lands on each of your turns.|
+Fierce Empath|Core Set 2021|181|U|{2}{G}|Creature - Elf|1|1|When Fierce Empath enters the battlefield, you may search your library for a creature card with converted mana cost 6 or greater, reveal it, put it into your hand, then shuffle your library.|
+Primal Might|Core Set 2021|197|R|{X}{G}|Sorcery|||Target creature you control gets +X/+X until end of turn. Then it fights up to one target creature you don't control.|
+Quirion Dryad|Core Set 2021|198|U|{1}{G}|Creature - Dryad|1|1|Whenever you cast a white, blue, black, or red spell, put a +1/+1 counter on Quirion Dryad.|
+Indulging Patrician|Core Set 2021|219|U|{1}{W}{B}|Creature - Vampire Noble|1|4|Flying$Lifelink$At the beginning of your end step, if you gained 3 or more life this turn, each opponent loses 3 life.|
+Chromatic Orrery|Core Set 2021|228|M|{7}|Legendary Artifact|||You may spend mana as though it were mana of any color.${T}, Add {C}{C}{C}{C}{C}.${5}, {T}: Draw a card for each color among permanents you control.|
+Sparkhunter Masticore|Core Set 2021|240|R|{3}|Artifact Creature - Masticore|3|4|As an additional cost to cast this spell, discard a card.$Protection from planeswalkers${1}: Sparkhunter Masticore deals 1 damage to target planeswalker.${3}: Sparkhunter Masticore gains indestructible until end of turn.|
+Tormod's Crypt|Core Set 2021|241|U|{0}|Artifact|||{T}, Sacrifice Tormod's Crypt: Exile all cards from target player's graveyard.|
+Rin and Seri, Inseparable|Core Set 2021|278|M|{1}{R}{G}{W}|Legendary Creature - Dog Cat|4|4|Whenever you cast a Dog spell, create a 1/1 green Cat creature token.$Whenever you cast a Cat spell, create a 1/1 white Dog creature token.${R}{G}{W}: Rin and Seri, Inseparable deals damage to any target equal to the number of Dogs you control. You gain life equal to the number of Cats you control.|
+Island|Core Set 2021|310|C||Basic Land - Island|||({T}: Add {U}.)|
+Containment Priest|Core Set 2021|314|R|{1}{W}|Creature - Human Cleric|2|2|Flash$If a nontoken creature would enter the battlefield and it wasn't cast, exile it instead.|
+Basri, Devoted Paladin|Core Set 2021|320|M|{4}{W}{W}|Legendary Planeswalker - Basri|4|+1: Put a +1/+1 counter on up to one target creature. It gains vigilance until end of turn.$−1: Whenever a creature attacks this turn, put a +1/+1 counter on it.$−6: Creatures you control get +2/+2 and gain flying until end of turn.|
+Adherent of Hope|Core Set 2021|321|C|{1}{W}|Creature - Human Soldier|2|1|At the beginning of combat on your turn, if you control a Basri planeswalker, put a +1/+1 counter on Adherent of Hope.|
+Basri's Aegis|Core Set 2021|322|R|{2}{W}{W}|Sorcery|||Put a +1/+1 counter on each of up to two target creatures. You may search your library and/or graveyard for a card named Basri, Devoted Paladin, reveal it, and put it into your hand. If you search your library this way, shuffle it.|
+Sigiled Contender|Core Set 2021|323|U|{3}{W}|Creature - Human Warrior|3|3|Sigiled Contender has lifelink as long as it has a +1/+1 counter on it.|
+Teferi, Timeless Voyager|Core Set 2021|324|M|{4}{U}{U}|Legendary Planeswalker - Teferi|4|+1: Draw a card.$−3: Put target creature on top of its owner's library.$−8: Each creature target opponent controls phases out. Until the end of your next turn, they can't phase in.|
+Historian of Zhalfir|Core Set 2021|325|U|{2}{U}{U}|Creature - Human Wizard|3|3|Whenever Historian of Zhalfir attacks, if you control a Teferi planeswalker, draw a card.|
+Mystic Skyfish|Core Set 2021|326|C|{2}{U}|Creature - Fish|3|1|Whenever you draw your second card each turn, Mystic Skyfish gains flying until end of turn.|
+Teferi's Wavecaster|Core Set 2021|327|R|{3}{U}{U}|Creature - Merfolk Wizard|3|3|Flash$When Teferi's Wavecaster enters the battlefield, you may search your library and/or graveyard for a card named Teferi, Timeless Voyager, reveal it, and put it into your hand. If you search your library this way, shuffle it.|
+Liliana, Death Mage|Core Set 2021|328|M|{4}{B}{B}|Legendary Planeswalker - Liliana|4|+1: Return up to one target creature card from your graveyard to your hand.$−3: Destroy target creature. Its controller loses 2 life.$−7: Target opponent loses 2 life for each creature card in their graveyard.|
+Liliana's Scorn|Core Set 2021|329|R|{3}{B}{B}|Sorcery|||Destroy target creature. You may search your library and/or graveyard for a card named Liliana, Death Mage, reveal it, and put it into your hand. If you search your library this way, shuffle it.|
+Liliana's Scrounger|Core Set 2021|330|U|{2}{B}|Creature - Human Wizard|3|2|At the beginning of each end step, if a creature died this turn, you may put a loyalty counter on a Liliana planeswalker you control.|
+Spirit of Malevolence|Core Set 2021|331|C|{1}{B}|Creature - Spirit|2|1|When Spirit of Malevolence dies, each opponent loses 1 life and you gain 1 life.|
+Chandra, Flame's Catalyst|Core Set 2021|332|M|{4}{R}{R}|Legendary Planeswalker - Chandra|5|+1: Chandra, Flame's Catalyst deals 3 damage to each opponent.$−2: You may cast target red instant or sorcery card from your graveyard. If that spell would be put into your graveyard this turn, exile it instead.$−8: Discard your hand, then draw seven cards. Until end of turn, you may cast spells from your hand without paying their mana costs.|
+Chandra's Firemaw|Core Set 2021|333|R|{3}{R}{R}|Creature - Hellion|4|2|Haste$When Chandra's Firemaw enters the battlefield, you may search your library and/or graveyard for a card named Chandra, Flame's Catalyst, reveal it, and put it into your hand. If you search your library this way, shuffle it.|
+Keral Keep Disciples|Core Set 2021|334|U|{2}{R}{R}|Creature - Human Monk|4|3|Whenever you activate a loyalty ability of a Chandra planeswalker, Keral Keep Disciples deals 1 damage to each opponent.|
+Storm Caller|Core Set 2021|335|C|{2}{R}|Creature - Ogre Shaman|3|2|When Storm Caller enters the battlefield, it deals 2 damage to each opponent.|
+Garruk, Savage Herald|Core Set 2021|336|M|{4}{G}{G}|Legendary Planeswalker - Garruk|5|+1: Reveal the top card of your library. If it's a creature card, put it into your hand. Otherwise, put it on the bottom of your library.$−2: Target creature you control deals damage equal to its power to another target creature.$−7: Until end of turn, creatures you control gain "You may have this creature assign its combat damage as though it weren't blocked."|
+Garruk's Warsteed|Core Set 2021|337|R|{3}{G}{G}|Creature - Rhino|3|5|Vigilance$When Garruk's Warsteed enters the battlefield, you may search your library and/or graveyard for a card named Garruk, Savage Herald, reveal it, and put it into your hand. If you search your library this way, shuffle it.|
+Predatory Wurm|Core Set 2021|338|U|{3}{G}|Creature - Wurm|4|4|Vigilance$Predatory Wurm gets +2/+2 as long as you control a Garruk planeswalker.|
+Wildwood Patrol|Core Set 2021|339|C|{2}{G}|Creature - Centaur Scout|4|2|Trample|

From 74b6626020f3baed2b9880ca5aa372e921118aa3 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Fri, 5 Jun 2020 20:17:41 -0400
Subject: [PATCH 105/586] changed Hound to Dog

---
 .../src/mage/cards/a/AbzanBeastmaster.java    |  2 +-
 .../src/mage/cards/a/AerieBowmasters.java     |  2 +-
 .../src/mage/cards/a/AffaGuardHound.java      |  2 +-
 .../src/mage/cards/a/AinokArtillerist.java    |  2 +-
 Mage.Sets/src/mage/cards/a/AinokBondKin.java  |  2 +-
 Mage.Sets/src/mage/cards/a/AinokGuide.java    |  2 +-
 .../src/mage/cards/a/AinokSurvivalist.java    |  2 +-
 Mage.Sets/src/mage/cards/a/AinokTracker.java  |  2 +-
 Mage.Sets/src/mage/cards/a/AkroanMastiff.java |  2 +-
 Mage.Sets/src/mage/cards/a/AshmouthHound.java |  2 +-
 Mage.Sets/src/mage/cards/b/BINGO.java         |  2 +-
 Mage.Sets/src/mage/cards/b/Banehound.java     |  2 +-
 .../src/mage/cards/b/BlazingHellhound.java    |  2 +-
 Mage.Sets/src/mage/cards/b/BloodHound.java    |  2 +-
 Mage.Sets/src/mage/cards/b/BorosMastiff.java  |  2 +-
 .../src/mage/cards/c/CatharsCompanion.java    |  2 +-
 Mage.Sets/src/mage/cards/c/CausticHound.java  |  2 +-
 .../src/mage/cards/c/ChakramRetriever.java    |  2 +-
 .../src/mage/cards/c/ChampionOfArashin.java   |  2 +-
 Mage.Sets/src/mage/cards/c/CorpseCur.java     |  2 +-
 Mage.Sets/src/mage/cards/d/DrossRipper.java   |  2 +-
 .../src/mage/cards/e/ElvenWarhounds.java      |  2 +-
 .../src/mage/cards/e/EncampmentKeeper.java    |  2 +-
 .../src/mage/cards/f/FieryHellhound.java      |  2 +-
 Mage.Sets/src/mage/cards/f/FilthyCur.java     |  2 +-
 Mage.Sets/src/mage/cards/g/GateHound.java     |  2 +-
 Mage.Sets/src/mage/cards/g/GhostHounds.java   |  2 +-
 Mage.Sets/src/mage/cards/g/GoreVassal.java    |  2 +-
 .../src/mage/cards/g/GreaterMossdog.java      |  2 +-
 Mage.Sets/src/mage/cards/g/GuardDogs.java     |  2 +-
 .../src/mage/cards/h/HellfireMongrel.java     |  2 +-
 Mage.Sets/src/mage/cards/h/HollowDogs.java    |  2 +-
 .../src/mage/cards/h/HollowbornBarghest.java  |  2 +-
 .../src/mage/cards/h/HoundOfGriselbrand.java  |  2 +-
 .../src/mage/cards/h/HoundOfTheFarbogs.java   |  2 +-
 Mage.Sets/src/mage/cards/h/Hydradoodle.java   |  2 +-
 .../src/mage/cards/i/ImmolatingSouleater.java |  2 +-
 .../src/mage/cards/i/IsamaruHoundOfKonda.java |  2 +-
 .../src/mage/cards/k/KarplusanHound.java      |  2 +-
 .../mage/cards/k/KunorosHoundOfAthreos.java   |  2 +-
 Mage.Sets/src/mage/cards/l/LavaHounds.java    |  2 +-
 Mage.Sets/src/mage/cards/l/Leashling.java     |  2 +-
 .../src/mage/cards/l/LightningHounds.java     |  2 +-
 Mage.Sets/src/mage/cards/l/LongshotSquad.java |  2 +-
 .../src/mage/cards/l/LurkingJackals.java      |  2 +-
 Mage.Sets/src/mage/cards/m/MadDog.java        |  2 +-
 .../src/mage/cards/m/MogissWarhound.java      |  2 +-
 Mage.Sets/src/mage/cards/m/MongrelPack.java   | 41 ++++---------------
 .../src/mage/cards/m/MonstrousHound.java      |  2 +-
 Mage.Sets/src/mage/cards/m/MoonEatingDog.java |  2 +-
 Mage.Sets/src/mage/cards/m/MortisDogs.java    |  2 +-
 Mage.Sets/src/mage/cards/m/Mossdog.java       |  2 +-
 .../src/mage/cards/m/MowuLoyalCompanion.java  |  2 +-
 Mage.Sets/src/mage/cards/p/PackMastiff.java   |  2 +-
 .../src/mage/cards/p/PakoArcaneRetriever.java |  2 +-
 Mage.Sets/src/mage/cards/p/PatrolHound.java   |  2 +-
 Mage.Sets/src/mage/cards/p/PhantomWhelp.java  |  2 +-
 Mage.Sets/src/mage/cards/p/PlagueDogs.java    |  2 +-
 Mage.Sets/src/mage/cards/p/PyreHound.java     |  2 +-
 .../src/mage/cards/r/RakdosRagemutt.java      |  2 +-
 .../src/mage/cards/r/ResoluteWatchdog.java    |  2 +-
 .../src/mage/cards/s/SaltRoadAmbushers.java   |  2 +-
 .../src/mage/cards/s/SandsteppeScavenger.java |  2 +-
 .../src/mage/cards/s/ScrapyardMongrel.java    |  2 +-
 Mage.Sets/src/mage/cards/s/SnowHound.java     |  2 +-
 .../src/mage/cards/t/ThrabenFoulbloods.java   |  2 +-
 .../src/mage/cards/t/ThrabenPurebloods.java   |  2 +-
 .../src/mage/cards/t/ThrashingMossdog.java    |  2 +-
 .../src/mage/cards/t/TwoHeadedCerberus.java   |  2 +-
 .../src/mage/cards/u/UnderworldCerberus.java  |  2 +-
 .../src/mage/cards/u/UnderworldRageHound.java |  2 +-
 Mage.Sets/src/mage/cards/v/VampireHounds.java |  2 +-
 .../src/mage/cards/w/WarclampMastiff.java     |  2 +-
 Mage.Sets/src/mage/cards/w/Watchdog.java      |  2 +-
 Mage.Sets/src/mage/cards/w/WildDogs.java      |  2 +-
 Mage.Sets/src/mage/cards/w/WildMongrel.java   |  2 +-
 .../src/mage/cards/w/WildfireCerberus.java    |  2 +-
 Mage.Sets/src/mage/cards/z/ZodiacDog.java     |  2 +-
 .../java/mage/verify/VerifyCardDataTest.java  |  3 ++
 .../src/main/java/mage/constants/SubType.java |  2 +-
 .../token/{HoundToken.java => DogToken.java}  | 20 ++++-----
 .../mage/game/permanent/token/MowuToken.java  |  5 +--
 82 files changed, 101 insertions(+), 124 deletions(-)
 rename Mage/src/main/java/mage/game/permanent/token/{HoundToken.java => DogToken.java} (53%)

diff --git a/Mage.Sets/src/mage/cards/a/AbzanBeastmaster.java b/Mage.Sets/src/mage/cards/a/AbzanBeastmaster.java
index e050309518..76707204e8 100644
--- a/Mage.Sets/src/mage/cards/a/AbzanBeastmaster.java
+++ b/Mage.Sets/src/mage/cards/a/AbzanBeastmaster.java
@@ -21,7 +21,7 @@ public final class AbzanBeastmaster extends CardImpl {
 
     public AbzanBeastmaster(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{G}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.subtype.add(SubType.SHAMAN);
         this.power = new MageInt(2);
         this.toughness = new MageInt(1);
diff --git a/Mage.Sets/src/mage/cards/a/AerieBowmasters.java b/Mage.Sets/src/mage/cards/a/AerieBowmasters.java
index 8d4f11ce50..baac41ba67 100644
--- a/Mage.Sets/src/mage/cards/a/AerieBowmasters.java
+++ b/Mage.Sets/src/mage/cards/a/AerieBowmasters.java
@@ -19,7 +19,7 @@ public final class AerieBowmasters extends CardImpl {
 
     public AerieBowmasters(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{G}{G}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.subtype.add(SubType.ARCHER);
         this.power = new MageInt(3);
         this.toughness = new MageInt(4);
diff --git a/Mage.Sets/src/mage/cards/a/AffaGuardHound.java b/Mage.Sets/src/mage/cards/a/AffaGuardHound.java
index a5415096b0..419bf0c33e 100644
--- a/Mage.Sets/src/mage/cards/a/AffaGuardHound.java
+++ b/Mage.Sets/src/mage/cards/a/AffaGuardHound.java
@@ -23,7 +23,7 @@ public final class AffaGuardHound extends CardImpl {
 
     public AffaGuardHound (UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
 
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
diff --git a/Mage.Sets/src/mage/cards/a/AinokArtillerist.java b/Mage.Sets/src/mage/cards/a/AinokArtillerist.java
index 589990f025..ffb5401177 100644
--- a/Mage.Sets/src/mage/cards/a/AinokArtillerist.java
+++ b/Mage.Sets/src/mage/cards/a/AinokArtillerist.java
@@ -23,7 +23,7 @@ public final class AinokArtillerist extends CardImpl {
 
     public AinokArtillerist(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{G}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.subtype.add(SubType.ARCHER);
         this.power = new MageInt(4);
         this.toughness = new MageInt(1);
diff --git a/Mage.Sets/src/mage/cards/a/AinokBondKin.java b/Mage.Sets/src/mage/cards/a/AinokBondKin.java
index ccec3c06fc..5c0aea14cf 100644
--- a/Mage.Sets/src/mage/cards/a/AinokBondKin.java
+++ b/Mage.Sets/src/mage/cards/a/AinokBondKin.java
@@ -33,7 +33,7 @@ public final class AinokBondKin extends CardImpl {
 
     public AinokBondKin(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{W}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.subtype.add(SubType.SOLDIER);
 
         this.power = new MageInt(2);
diff --git a/Mage.Sets/src/mage/cards/a/AinokGuide.java b/Mage.Sets/src/mage/cards/a/AinokGuide.java
index 2136d4f976..b59cd42b1b 100644
--- a/Mage.Sets/src/mage/cards/a/AinokGuide.java
+++ b/Mage.Sets/src/mage/cards/a/AinokGuide.java
@@ -25,7 +25,7 @@ public final class AinokGuide extends CardImpl {
 
     public AinokGuide(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{G}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.subtype.add(SubType.SCOUT);
         this.power = new MageInt(1);
         this.toughness = new MageInt(1);
diff --git a/Mage.Sets/src/mage/cards/a/AinokSurvivalist.java b/Mage.Sets/src/mage/cards/a/AinokSurvivalist.java
index 0a406fc343..aa812a835d 100644
--- a/Mage.Sets/src/mage/cards/a/AinokSurvivalist.java
+++ b/Mage.Sets/src/mage/cards/a/AinokSurvivalist.java
@@ -31,7 +31,7 @@ public final class AinokSurvivalist extends CardImpl {
 
     public AinokSurvivalist(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{G}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.subtype.add(SubType.SHAMAN);
         this.power = new MageInt(2);
         this.toughness = new MageInt(1);
diff --git a/Mage.Sets/src/mage/cards/a/AinokTracker.java b/Mage.Sets/src/mage/cards/a/AinokTracker.java
index 1c3d8b1475..c4909ef07e 100644
--- a/Mage.Sets/src/mage/cards/a/AinokTracker.java
+++ b/Mage.Sets/src/mage/cards/a/AinokTracker.java
@@ -19,7 +19,7 @@ public final class AinokTracker extends CardImpl {
 
     public AinokTracker(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{5}{R}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.subtype.add(SubType.SCOUT);
 
         this.power = new MageInt(3);
diff --git a/Mage.Sets/src/mage/cards/a/AkroanMastiff.java b/Mage.Sets/src/mage/cards/a/AkroanMastiff.java
index f1e385cde9..7b61b400b2 100644
--- a/Mage.Sets/src/mage/cards/a/AkroanMastiff.java
+++ b/Mage.Sets/src/mage/cards/a/AkroanMastiff.java
@@ -23,7 +23,7 @@ public final class AkroanMastiff extends CardImpl {
 
     public AkroanMastiff(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{W}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
 
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
diff --git a/Mage.Sets/src/mage/cards/a/AshmouthHound.java b/Mage.Sets/src/mage/cards/a/AshmouthHound.java
index 15e10029c7..dda0221e81 100644
--- a/Mage.Sets/src/mage/cards/a/AshmouthHound.java
+++ b/Mage.Sets/src/mage/cards/a/AshmouthHound.java
@@ -19,7 +19,7 @@ public final class AshmouthHound extends CardImpl {
     public AshmouthHound(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}");
         this.subtype.add(SubType.ELEMENTAL);
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
 
         this.power = new MageInt(2);
         this.toughness = new MageInt(1);
diff --git a/Mage.Sets/src/mage/cards/b/BINGO.java b/Mage.Sets/src/mage/cards/b/BINGO.java
index 185acbd19d..d6d4b004a2 100644
--- a/Mage.Sets/src/mage/cards/b/BINGO.java
+++ b/Mage.Sets/src/mage/cards/b/BINGO.java
@@ -39,7 +39,7 @@ public final class BINGO extends CardImpl {
 
     public BINGO(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{G}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.power = new MageInt(1);
         this.toughness = new MageInt(1);
 
diff --git a/Mage.Sets/src/mage/cards/b/Banehound.java b/Mage.Sets/src/mage/cards/b/Banehound.java
index 493e33caa6..20f196c29a 100644
--- a/Mage.Sets/src/mage/cards/b/Banehound.java
+++ b/Mage.Sets/src/mage/cards/b/Banehound.java
@@ -19,7 +19,7 @@ public final class Banehound extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}");
 
         this.subtype.add(SubType.NIGHTMARE);
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.power = new MageInt(1);
         this.toughness = new MageInt(1);
 
diff --git a/Mage.Sets/src/mage/cards/b/BlazingHellhound.java b/Mage.Sets/src/mage/cards/b/BlazingHellhound.java
index 65ed6aee7c..61d0e0f67e 100644
--- a/Mage.Sets/src/mage/cards/b/BlazingHellhound.java
+++ b/Mage.Sets/src/mage/cards/b/BlazingHellhound.java
@@ -25,7 +25,7 @@ public final class BlazingHellhound extends CardImpl {
 
     public BlazingHellhound(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{R}");
-        this.subtype.add(SubType.ELEMENTAL, SubType.HOUND);
+        this.subtype.add(SubType.ELEMENTAL, SubType.DOG);
         this.power = new MageInt(4);
         this.toughness = new MageInt(3);
 
diff --git a/Mage.Sets/src/mage/cards/b/BloodHound.java b/Mage.Sets/src/mage/cards/b/BloodHound.java
index f182a4b5e3..98e7ef6b12 100644
--- a/Mage.Sets/src/mage/cards/b/BloodHound.java
+++ b/Mage.Sets/src/mage/cards/b/BloodHound.java
@@ -26,7 +26,7 @@ public final class BloodHound extends CardImpl {
     public BloodHound(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}");
 
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.power = new MageInt(1);
         this.toughness = new MageInt(1);
 
diff --git a/Mage.Sets/src/mage/cards/b/BorosMastiff.java b/Mage.Sets/src/mage/cards/b/BorosMastiff.java
index 9d2e118ed8..35811ff779 100644
--- a/Mage.Sets/src/mage/cards/b/BorosMastiff.java
+++ b/Mage.Sets/src/mage/cards/b/BorosMastiff.java
@@ -23,7 +23,7 @@ public final class BorosMastiff extends CardImpl {
 
     public BorosMastiff (UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{W}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
 
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
diff --git a/Mage.Sets/src/mage/cards/c/CatharsCompanion.java b/Mage.Sets/src/mage/cards/c/CatharsCompanion.java
index d26614f24f..f42b71fd1e 100644
--- a/Mage.Sets/src/mage/cards/c/CatharsCompanion.java
+++ b/Mage.Sets/src/mage/cards/c/CatharsCompanion.java
@@ -27,7 +27,7 @@ public final class CatharsCompanion extends CardImpl {
 
     public CatharsCompanion(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.power = new MageInt(3);
         this.toughness = new MageInt(1);
 
diff --git a/Mage.Sets/src/mage/cards/c/CausticHound.java b/Mage.Sets/src/mage/cards/c/CausticHound.java
index 021f30af70..266d106cec 100644
--- a/Mage.Sets/src/mage/cards/c/CausticHound.java
+++ b/Mage.Sets/src/mage/cards/c/CausticHound.java
@@ -19,7 +19,7 @@ public final class CausticHound extends CardImpl {
 
     public CausticHound (UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{5}{B}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
 
         this.power = new MageInt(4);
         this.toughness = new MageInt(4);
diff --git a/Mage.Sets/src/mage/cards/c/ChakramRetriever.java b/Mage.Sets/src/mage/cards/c/ChakramRetriever.java
index 5f606092e5..19fc2daf8e 100644
--- a/Mage.Sets/src/mage/cards/c/ChakramRetriever.java
+++ b/Mage.Sets/src/mage/cards/c/ChakramRetriever.java
@@ -25,7 +25,7 @@ public final class ChakramRetriever extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}");
 
         this.subtype.add(SubType.ELEMENTAL);
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.power = new MageInt(2);
         this.toughness = new MageInt(4);
 
diff --git a/Mage.Sets/src/mage/cards/c/ChampionOfArashin.java b/Mage.Sets/src/mage/cards/c/ChampionOfArashin.java
index 52d66a318c..a9941dacae 100644
--- a/Mage.Sets/src/mage/cards/c/ChampionOfArashin.java
+++ b/Mage.Sets/src/mage/cards/c/ChampionOfArashin.java
@@ -17,7 +17,7 @@ public final class ChampionOfArashin extends CardImpl {
 
     public ChampionOfArashin(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{W}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.subtype.add(SubType.WARRIOR);
         this.power = new MageInt(3);
         this.toughness = new MageInt(2);
diff --git a/Mage.Sets/src/mage/cards/c/CorpseCur.java b/Mage.Sets/src/mage/cards/c/CorpseCur.java
index 617c260215..79144ef43c 100644
--- a/Mage.Sets/src/mage/cards/c/CorpseCur.java
+++ b/Mage.Sets/src/mage/cards/c/CorpseCur.java
@@ -30,7 +30,7 @@ public final class CorpseCur extends CardImpl {
 
     public CorpseCur (UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{4}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
         this.addAbility(InfectAbility.getInstance());
diff --git a/Mage.Sets/src/mage/cards/d/DrossRipper.java b/Mage.Sets/src/mage/cards/d/DrossRipper.java
index d9867e585f..836f431631 100644
--- a/Mage.Sets/src/mage/cards/d/DrossRipper.java
+++ b/Mage.Sets/src/mage/cards/d/DrossRipper.java
@@ -22,7 +22,7 @@ public final class DrossRipper extends CardImpl {
 
     public DrossRipper (UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{4}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.power = new MageInt(3);
         this.toughness = new MageInt(3);
         this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, 1, Duration.EndOfTurn), new ManaCostsImpl("{2}{B}")));
diff --git a/Mage.Sets/src/mage/cards/e/ElvenWarhounds.java b/Mage.Sets/src/mage/cards/e/ElvenWarhounds.java
index e34c197214..6809e3f232 100644
--- a/Mage.Sets/src/mage/cards/e/ElvenWarhounds.java
+++ b/Mage.Sets/src/mage/cards/e/ElvenWarhounds.java
@@ -19,7 +19,7 @@ public final class ElvenWarhounds extends CardImpl {
 
     public ElvenWarhounds(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{G}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
 
diff --git a/Mage.Sets/src/mage/cards/e/EncampmentKeeper.java b/Mage.Sets/src/mage/cards/e/EncampmentKeeper.java
index 13fb82d3cb..d2c401c54f 100644
--- a/Mage.Sets/src/mage/cards/e/EncampmentKeeper.java
+++ b/Mage.Sets/src/mage/cards/e/EncampmentKeeper.java
@@ -26,7 +26,7 @@ public final class EncampmentKeeper extends CardImpl {
     public EncampmentKeeper(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}");
 
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.power = new MageInt(1);
         this.toughness = new MageInt(1);
 
diff --git a/Mage.Sets/src/mage/cards/f/FieryHellhound.java b/Mage.Sets/src/mage/cards/f/FieryHellhound.java
index 62815a4ed5..8381f487ef 100644
--- a/Mage.Sets/src/mage/cards/f/FieryHellhound.java
+++ b/Mage.Sets/src/mage/cards/f/FieryHellhound.java
@@ -23,7 +23,7 @@ public final class FieryHellhound extends CardImpl {
     public FieryHellhound(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{R}{R}");
         this.subtype.add(SubType.ELEMENTAL);
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
 
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
diff --git a/Mage.Sets/src/mage/cards/f/FilthyCur.java b/Mage.Sets/src/mage/cards/f/FilthyCur.java
index 9864aec012..e47525388a 100644
--- a/Mage.Sets/src/mage/cards/f/FilthyCur.java
+++ b/Mage.Sets/src/mage/cards/f/FilthyCur.java
@@ -23,7 +23,7 @@ public final class FilthyCur extends CardImpl {
 
     public FilthyCur(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
 
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
diff --git a/Mage.Sets/src/mage/cards/g/GateHound.java b/Mage.Sets/src/mage/cards/g/GateHound.java
index c25ebded1b..357df06b8f 100644
--- a/Mage.Sets/src/mage/cards/g/GateHound.java
+++ b/Mage.Sets/src/mage/cards/g/GateHound.java
@@ -24,7 +24,7 @@ public final class GateHound extends CardImpl {
 
     public GateHound(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
 
         this.power = new MageInt(1);
         this.toughness = new MageInt(1);
diff --git a/Mage.Sets/src/mage/cards/g/GhostHounds.java b/Mage.Sets/src/mage/cards/g/GhostHounds.java
index 1dcdcad465..cac1255c0a 100644
--- a/Mage.Sets/src/mage/cards/g/GhostHounds.java
+++ b/Mage.Sets/src/mage/cards/g/GhostHounds.java
@@ -31,7 +31,7 @@ public final class GhostHounds extends CardImpl {
     public GhostHounds(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}");
         
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.subtype.add(SubType.SPIRIT);
         this.power = new MageInt(1);
         this.toughness = new MageInt(1);
diff --git a/Mage.Sets/src/mage/cards/g/GoreVassal.java b/Mage.Sets/src/mage/cards/g/GoreVassal.java
index cdfbfbb094..4435be0dac 100644
--- a/Mage.Sets/src/mage/cards/g/GoreVassal.java
+++ b/Mage.Sets/src/mage/cards/g/GoreVassal.java
@@ -23,7 +23,7 @@ public final class GoreVassal extends CardImpl {
 
     public GoreVassal(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
 
         this.power = new MageInt(2);
         this.toughness = new MageInt(1);
diff --git a/Mage.Sets/src/mage/cards/g/GreaterMossdog.java b/Mage.Sets/src/mage/cards/g/GreaterMossdog.java
index 605af0ca90..13c03b9187 100644
--- a/Mage.Sets/src/mage/cards/g/GreaterMossdog.java
+++ b/Mage.Sets/src/mage/cards/g/GreaterMossdog.java
@@ -18,7 +18,7 @@ public final class GreaterMossdog extends CardImpl {
     public GreaterMossdog(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{G}");
         this.subtype.add(SubType.PLANT);
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
 
         this.power = new MageInt(3);
         this.toughness = new MageInt(3);
diff --git a/Mage.Sets/src/mage/cards/g/GuardDogs.java b/Mage.Sets/src/mage/cards/g/GuardDogs.java
index 6101684b23..30fe2c42ee 100644
--- a/Mage.Sets/src/mage/cards/g/GuardDogs.java
+++ b/Mage.Sets/src/mage/cards/g/GuardDogs.java
@@ -30,7 +30,7 @@ public final class GuardDogs extends CardImpl {
 
     public GuardDogs(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{W}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
 
diff --git a/Mage.Sets/src/mage/cards/h/HellfireMongrel.java b/Mage.Sets/src/mage/cards/h/HellfireMongrel.java
index 125442d401..da1da307dd 100644
--- a/Mage.Sets/src/mage/cards/h/HellfireMongrel.java
+++ b/Mage.Sets/src/mage/cards/h/HellfireMongrel.java
@@ -25,7 +25,7 @@ public final class HellfireMongrel extends CardImpl {
     public HellfireMongrel(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{R}");
         this.subtype.add(SubType.ELEMENTAL);
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
 
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
diff --git a/Mage.Sets/src/mage/cards/h/HollowDogs.java b/Mage.Sets/src/mage/cards/h/HollowDogs.java
index 16f4eaa5e7..b2daa9ed90 100644
--- a/Mage.Sets/src/mage/cards/h/HollowDogs.java
+++ b/Mage.Sets/src/mage/cards/h/HollowDogs.java
@@ -20,7 +20,7 @@ public final class HollowDogs extends CardImpl {
     public HollowDogs(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{B}");
         this.subtype.add(SubType.ZOMBIE);
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
 
         this.power = new MageInt(3);
         this.toughness = new MageInt(3);
diff --git a/Mage.Sets/src/mage/cards/h/HollowbornBarghest.java b/Mage.Sets/src/mage/cards/h/HollowbornBarghest.java
index 21a5fe789f..3e8991c95e 100644
--- a/Mage.Sets/src/mage/cards/h/HollowbornBarghest.java
+++ b/Mage.Sets/src/mage/cards/h/HollowbornBarghest.java
@@ -36,7 +36,7 @@ public final class HollowbornBarghest extends CardImpl {
     public HollowbornBarghest(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{B}{B}");
         this.subtype.add(SubType.DEMON);
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
 
         this.power = new MageInt(7);
         this.toughness = new MageInt(6);
diff --git a/Mage.Sets/src/mage/cards/h/HoundOfGriselbrand.java b/Mage.Sets/src/mage/cards/h/HoundOfGriselbrand.java
index 938d482164..50a3b66a2a 100644
--- a/Mage.Sets/src/mage/cards/h/HoundOfGriselbrand.java
+++ b/Mage.Sets/src/mage/cards/h/HoundOfGriselbrand.java
@@ -19,7 +19,7 @@ public final class HoundOfGriselbrand extends CardImpl {
     public HoundOfGriselbrand(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{R}{R}");
         this.subtype.add(SubType.ELEMENTAL);
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
 
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
diff --git a/Mage.Sets/src/mage/cards/h/HoundOfTheFarbogs.java b/Mage.Sets/src/mage/cards/h/HoundOfTheFarbogs.java
index 9206adf00f..3af2670f03 100644
--- a/Mage.Sets/src/mage/cards/h/HoundOfTheFarbogs.java
+++ b/Mage.Sets/src/mage/cards/h/HoundOfTheFarbogs.java
@@ -28,7 +28,7 @@ public final class HoundOfTheFarbogs extends CardImpl {
     public HoundOfTheFarbogs(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}");
         this.subtype.add(SubType.ZOMBIE);
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.power = new MageInt(5);
         this.toughness = new MageInt(3);
 
diff --git a/Mage.Sets/src/mage/cards/h/Hydradoodle.java b/Mage.Sets/src/mage/cards/h/Hydradoodle.java
index 75fc85bff8..9ab48ab269 100644
--- a/Mage.Sets/src/mage/cards/h/Hydradoodle.java
+++ b/Mage.Sets/src/mage/cards/h/Hydradoodle.java
@@ -32,7 +32,7 @@ public final class Hydradoodle extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{X}{X}{G}{G}");
 
         this.subtype.add(SubType.HYDRA);
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.power = new MageInt(0);
         this.toughness = new MageInt(0);
 
diff --git a/Mage.Sets/src/mage/cards/i/ImmolatingSouleater.java b/Mage.Sets/src/mage/cards/i/ImmolatingSouleater.java
index bbeb302609..02445eb06b 100644
--- a/Mage.Sets/src/mage/cards/i/ImmolatingSouleater.java
+++ b/Mage.Sets/src/mage/cards/i/ImmolatingSouleater.java
@@ -22,7 +22,7 @@ public final class ImmolatingSouleater extends CardImpl {
 
     public ImmolatingSouleater(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{2}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
 
         this.power = new MageInt(1);
         this.toughness = new MageInt(1);
diff --git a/Mage.Sets/src/mage/cards/i/IsamaruHoundOfKonda.java b/Mage.Sets/src/mage/cards/i/IsamaruHoundOfKonda.java
index 35296f3e40..4c7eb1b336 100644
--- a/Mage.Sets/src/mage/cards/i/IsamaruHoundOfKonda.java
+++ b/Mage.Sets/src/mage/cards/i/IsamaruHoundOfKonda.java
@@ -19,7 +19,7 @@ public final class IsamaruHoundOfKonda extends CardImpl {
     public IsamaruHoundOfKonda(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}");
         this.addSuperType(SuperType.LEGENDARY);
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
 
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
diff --git a/Mage.Sets/src/mage/cards/k/KarplusanHound.java b/Mage.Sets/src/mage/cards/k/KarplusanHound.java
index 1d0077baa7..62149c4f00 100644
--- a/Mage.Sets/src/mage/cards/k/KarplusanHound.java
+++ b/Mage.Sets/src/mage/cards/k/KarplusanHound.java
@@ -24,7 +24,7 @@ public final class KarplusanHound extends CardImpl {
     public KarplusanHound(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}");
 
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.power = new MageInt(3);
         this.toughness = new MageInt(3);
         FilterPlaneswalkerPermanent filter = new FilterPlaneswalkerPermanent("a Chandra planeswalker");
diff --git a/Mage.Sets/src/mage/cards/k/KunorosHoundOfAthreos.java b/Mage.Sets/src/mage/cards/k/KunorosHoundOfAthreos.java
index fe8e6d8b15..f6eddc8ae6 100644
--- a/Mage.Sets/src/mage/cards/k/KunorosHoundOfAthreos.java
+++ b/Mage.Sets/src/mage/cards/k/KunorosHoundOfAthreos.java
@@ -26,7 +26,7 @@ public final class KunorosHoundOfAthreos extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{B}");
 
         this.addSuperType(SuperType.LEGENDARY);
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.power = new MageInt(3);
         this.toughness = new MageInt(3);
 
diff --git a/Mage.Sets/src/mage/cards/l/LavaHounds.java b/Mage.Sets/src/mage/cards/l/LavaHounds.java
index 6e62d95e67..b7b481c90f 100644
--- a/Mage.Sets/src/mage/cards/l/LavaHounds.java
+++ b/Mage.Sets/src/mage/cards/l/LavaHounds.java
@@ -19,7 +19,7 @@ public final class LavaHounds extends CardImpl {
 
     public LavaHounds(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{R}{R}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
 
         this.power = new MageInt(4);
         this.toughness = new MageInt(4);
diff --git a/Mage.Sets/src/mage/cards/l/Leashling.java b/Mage.Sets/src/mage/cards/l/Leashling.java
index 41f2099042..c4dfc5cd1e 100644
--- a/Mage.Sets/src/mage/cards/l/Leashling.java
+++ b/Mage.Sets/src/mage/cards/l/Leashling.java
@@ -28,7 +28,7 @@ public final class Leashling extends CardImpl {
 
     public Leashling(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{6}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.power = new MageInt(3);
         this.toughness = new MageInt(3);
 
diff --git a/Mage.Sets/src/mage/cards/l/LightningHounds.java b/Mage.Sets/src/mage/cards/l/LightningHounds.java
index 4aba0b130e..36ed3340f5 100644
--- a/Mage.Sets/src/mage/cards/l/LightningHounds.java
+++ b/Mage.Sets/src/mage/cards/l/LightningHounds.java
@@ -17,7 +17,7 @@ public final class LightningHounds extends CardImpl {
 
     public LightningHounds(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{R}{R}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
 
         this.power = new MageInt(3);
         this.toughness = new MageInt(2);
diff --git a/Mage.Sets/src/mage/cards/l/LongshotSquad.java b/Mage.Sets/src/mage/cards/l/LongshotSquad.java
index 42676ccb5b..21f05d8648 100644
--- a/Mage.Sets/src/mage/cards/l/LongshotSquad.java
+++ b/Mage.Sets/src/mage/cards/l/LongshotSquad.java
@@ -35,7 +35,7 @@ public final class LongshotSquad extends CardImpl {
 
     public LongshotSquad(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{G}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.subtype.add(SubType.ARCHER);
 
         this.power = new MageInt(3);
diff --git a/Mage.Sets/src/mage/cards/l/LurkingJackals.java b/Mage.Sets/src/mage/cards/l/LurkingJackals.java
index 4e292aad81..b46ee2691a 100644
--- a/Mage.Sets/src/mage/cards/l/LurkingJackals.java
+++ b/Mage.Sets/src/mage/cards/l/LurkingJackals.java
@@ -110,7 +110,7 @@ class LurkingJackalsToken extends TokenImpl {
     public LurkingJackalsToken() {
         super("Hound", "3/2 Hound creature");
         cardType.add(CardType.CREATURE);
-        subtype.add(SubType.HOUND);
+        subtype.add(SubType.DOG);
         power = new MageInt(3);
         toughness = new MageInt(2);
     }
diff --git a/Mage.Sets/src/mage/cards/m/MadDog.java b/Mage.Sets/src/mage/cards/m/MadDog.java
index 17254366cd..797f4cf6c3 100644
--- a/Mage.Sets/src/mage/cards/m/MadDog.java
+++ b/Mage.Sets/src/mage/cards/m/MadDog.java
@@ -30,7 +30,7 @@ public final class MadDog extends CardImpl {
     public MadDog(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}");
 
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
 
diff --git a/Mage.Sets/src/mage/cards/m/MogissWarhound.java b/Mage.Sets/src/mage/cards/m/MogissWarhound.java
index 3ac5c7cc25..76b79af5e0 100644
--- a/Mage.Sets/src/mage/cards/m/MogissWarhound.java
+++ b/Mage.Sets/src/mage/cards/m/MogissWarhound.java
@@ -26,7 +26,7 @@ public final class MogissWarhound extends CardImpl {
 
     public MogissWarhound(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{1}{R}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
 
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
diff --git a/Mage.Sets/src/mage/cards/m/MongrelPack.java b/Mage.Sets/src/mage/cards/m/MongrelPack.java
index e2a0f56e40..732f0c97ac 100644
--- a/Mage.Sets/src/mage/cards/m/MongrelPack.java
+++ b/Mage.Sets/src/mage/cards/m/MongrelPack.java
@@ -1,7 +1,5 @@
-
 package mage.cards.m;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.common.ZoneChangeTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
@@ -13,18 +11,18 @@ import mage.constants.TurnPhase;
 import mage.constants.Zone;
 import mage.game.Game;
 import mage.game.events.GameEvent;
-import mage.game.permanent.token.TokenImpl;
-import mage.game.permanent.token.Token;
+import mage.game.permanent.token.DogToken;
+
+import java.util.UUID;
 
 /**
- *
  * @author North
  */
 public final class MongrelPack extends CardImpl {
 
     public MongrelPack(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{G}");
-        this.subtype.add(SubType.HOUND);
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}");
+        this.subtype.add(SubType.DOG);
 
         this.power = new MageInt(4);
         this.toughness = new MageInt(1);
@@ -33,7 +31,7 @@ public final class MongrelPack extends CardImpl {
         this.addAbility(new MongrelPackAbility());
     }
 
-    public MongrelPack(final MongrelPack card) {
+    private MongrelPack(final MongrelPack card) {
         super(card);
     }
 
@@ -45,11 +43,11 @@ public final class MongrelPack extends CardImpl {
 
 class MongrelPackAbility extends ZoneChangeTriggeredAbility {
 
-    public MongrelPackAbility() {
-        super(Zone.BATTLEFIELD, Zone.GRAVEYARD, new CreateTokenEffect(new HoundToken(), 4), "When {this} dies during combat, ", false);
+    MongrelPackAbility() {
+        super(Zone.BATTLEFIELD, Zone.GRAVEYARD, new CreateTokenEffect(new DogToken(), 4), "When {this} dies during combat, ", false);
     }
 
-    public MongrelPackAbility(MongrelPackAbility ability) {
+    private MongrelPackAbility(MongrelPackAbility ability) {
         super(ability);
     }
 
@@ -68,24 +66,3 @@ class MongrelPackAbility extends ZoneChangeTriggeredAbility {
         return false;
     }
 }
-
-class HoundToken extends TokenImpl {
-
-    public HoundToken() {
-        super("Hound", "1/1 green Hound creature token");
-        cardType.add(CardType.CREATURE);
-        subtype.add(SubType.HOUND);
-
-        color.setGreen(true);
-        power = new MageInt(1);
-        toughness = new MageInt(1);
-    }
-
-    public HoundToken(final HoundToken token) {
-        super(token);
-    }
-
-    public HoundToken copy() {
-        return new HoundToken(this);
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/m/MonstrousHound.java b/Mage.Sets/src/mage/cards/m/MonstrousHound.java
index cdebb8caa7..1eba97be67 100644
--- a/Mage.Sets/src/mage/cards/m/MonstrousHound.java
+++ b/Mage.Sets/src/mage/cards/m/MonstrousHound.java
@@ -26,7 +26,7 @@ public final class MonstrousHound extends CardImpl {
     public MonstrousHound(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}");
 
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.power = new MageInt(4);
         this.toughness = new MageInt(4);
 
diff --git a/Mage.Sets/src/mage/cards/m/MoonEatingDog.java b/Mage.Sets/src/mage/cards/m/MoonEatingDog.java
index 1467030f59..d5d2a004f1 100644
--- a/Mage.Sets/src/mage/cards/m/MoonEatingDog.java
+++ b/Mage.Sets/src/mage/cards/m/MoonEatingDog.java
@@ -30,7 +30,7 @@ public final class MoonEatingDog extends CardImpl {
     public MoonEatingDog(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}");
 
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.power = new MageInt(3);
         this.toughness = new MageInt(3);
 
diff --git a/Mage.Sets/src/mage/cards/m/MortisDogs.java b/Mage.Sets/src/mage/cards/m/MortisDogs.java
index 310a8b8867..2f138d7819 100644
--- a/Mage.Sets/src/mage/cards/m/MortisDogs.java
+++ b/Mage.Sets/src/mage/cards/m/MortisDogs.java
@@ -24,7 +24,7 @@ public final class MortisDogs extends CardImpl {
 
     public MortisDogs(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
 
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
diff --git a/Mage.Sets/src/mage/cards/m/Mossdog.java b/Mage.Sets/src/mage/cards/m/Mossdog.java
index 6f3bff2827..b052767d07 100644
--- a/Mage.Sets/src/mage/cards/m/Mossdog.java
+++ b/Mage.Sets/src/mage/cards/m/Mossdog.java
@@ -24,7 +24,7 @@ public final class Mossdog extends CardImpl {
     public Mossdog(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}");
         this.subtype.add(SubType.PLANT);
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.power = new MageInt(1);
         this.toughness = new MageInt(1);
 
diff --git a/Mage.Sets/src/mage/cards/m/MowuLoyalCompanion.java b/Mage.Sets/src/mage/cards/m/MowuLoyalCompanion.java
index 946b515178..03ec484dcc 100644
--- a/Mage.Sets/src/mage/cards/m/MowuLoyalCompanion.java
+++ b/Mage.Sets/src/mage/cards/m/MowuLoyalCompanion.java
@@ -25,7 +25,7 @@ public final class MowuLoyalCompanion extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}");
 
         this.addSuperType(SuperType.LEGENDARY);
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.power = new MageInt(3);
         this.toughness = new MageInt(3);
 
diff --git a/Mage.Sets/src/mage/cards/p/PackMastiff.java b/Mage.Sets/src/mage/cards/p/PackMastiff.java
index 92a049678b..92413d6e2c 100644
--- a/Mage.Sets/src/mage/cards/p/PackMastiff.java
+++ b/Mage.Sets/src/mage/cards/p/PackMastiff.java
@@ -28,7 +28,7 @@ public final class PackMastiff extends CardImpl {
     public PackMastiff(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}");
 
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
 
diff --git a/Mage.Sets/src/mage/cards/p/PakoArcaneRetriever.java b/Mage.Sets/src/mage/cards/p/PakoArcaneRetriever.java
index c0b8a7ce63..f7abe82234 100644
--- a/Mage.Sets/src/mage/cards/p/PakoArcaneRetriever.java
+++ b/Mage.Sets/src/mage/cards/p/PakoArcaneRetriever.java
@@ -29,7 +29,7 @@ public final class PakoArcaneRetriever extends CardImpl {
 
         this.addSuperType(SuperType.LEGENDARY);
         this.subtype.add(SubType.ELEMENTAL);
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.power = new MageInt(3);
         this.toughness = new MageInt(3);
 
diff --git a/Mage.Sets/src/mage/cards/p/PatrolHound.java b/Mage.Sets/src/mage/cards/p/PatrolHound.java
index aa3c45f9ee..266ba9535a 100644
--- a/Mage.Sets/src/mage/cards/p/PatrolHound.java
+++ b/Mage.Sets/src/mage/cards/p/PatrolHound.java
@@ -22,7 +22,7 @@ public final class PatrolHound extends CardImpl {
 
     public PatrolHound(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{W}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
 
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
diff --git a/Mage.Sets/src/mage/cards/p/PhantomWhelp.java b/Mage.Sets/src/mage/cards/p/PhantomWhelp.java
index 33d2e7e760..f397cf302a 100644
--- a/Mage.Sets/src/mage/cards/p/PhantomWhelp.java
+++ b/Mage.Sets/src/mage/cards/p/PhantomWhelp.java
@@ -22,7 +22,7 @@ public final class PhantomWhelp extends CardImpl {
     public PhantomWhelp(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}");
         this.subtype.add(SubType.ILLUSION);
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
 
diff --git a/Mage.Sets/src/mage/cards/p/PlagueDogs.java b/Mage.Sets/src/mage/cards/p/PlagueDogs.java
index 9eda0693bc..4982b29055 100644
--- a/Mage.Sets/src/mage/cards/p/PlagueDogs.java
+++ b/Mage.Sets/src/mage/cards/p/PlagueDogs.java
@@ -26,7 +26,7 @@ public final class PlagueDogs extends CardImpl {
     public PlagueDogs(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{B}");
         this.subtype.add(SubType.ZOMBIE);
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.power = new MageInt(3);
         this.toughness = new MageInt(3);
 
diff --git a/Mage.Sets/src/mage/cards/p/PyreHound.java b/Mage.Sets/src/mage/cards/p/PyreHound.java
index 6e42ee1b4c..4a17383d6d 100644
--- a/Mage.Sets/src/mage/cards/p/PyreHound.java
+++ b/Mage.Sets/src/mage/cards/p/PyreHound.java
@@ -22,7 +22,7 @@ public final class PyreHound extends CardImpl {
     public PyreHound(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{R}");
         this.subtype.add(SubType.ELEMENTAL);
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.power = new MageInt(2);
         this.toughness = new MageInt(3);
 
diff --git a/Mage.Sets/src/mage/cards/r/RakdosRagemutt.java b/Mage.Sets/src/mage/cards/r/RakdosRagemutt.java
index e4c1a606b2..90171b0a0c 100644
--- a/Mage.Sets/src/mage/cards/r/RakdosRagemutt.java
+++ b/Mage.Sets/src/mage/cards/r/RakdosRagemutt.java
@@ -19,7 +19,7 @@ public final class RakdosRagemutt extends CardImpl {
     public RakdosRagemutt(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}{R}");
         this.subtype.add(SubType.ELEMENTAL);
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
 
         this.power = new MageInt(3);
         this.toughness = new MageInt(3);
diff --git a/Mage.Sets/src/mage/cards/r/ResoluteWatchdog.java b/Mage.Sets/src/mage/cards/r/ResoluteWatchdog.java
index 80f127f700..2376752bd1 100644
--- a/Mage.Sets/src/mage/cards/r/ResoluteWatchdog.java
+++ b/Mage.Sets/src/mage/cards/r/ResoluteWatchdog.java
@@ -25,7 +25,7 @@ public final class ResoluteWatchdog extends CardImpl {
     public ResoluteWatchdog(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}");
 
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.power = new MageInt(1);
         this.toughness = new MageInt(3);
 
diff --git a/Mage.Sets/src/mage/cards/s/SaltRoadAmbushers.java b/Mage.Sets/src/mage/cards/s/SaltRoadAmbushers.java
index 3329be2819..95f4f69c7e 100644
--- a/Mage.Sets/src/mage/cards/s/SaltRoadAmbushers.java
+++ b/Mage.Sets/src/mage/cards/s/SaltRoadAmbushers.java
@@ -23,7 +23,7 @@ public final class SaltRoadAmbushers extends CardImpl {
 
     public SaltRoadAmbushers(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{G}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.subtype.add(SubType.WARRIOR);
         this.power = new MageInt(3);
         this.toughness = new MageInt(3);
diff --git a/Mage.Sets/src/mage/cards/s/SandsteppeScavenger.java b/Mage.Sets/src/mage/cards/s/SandsteppeScavenger.java
index 770fa83768..d23385995d 100644
--- a/Mage.Sets/src/mage/cards/s/SandsteppeScavenger.java
+++ b/Mage.Sets/src/mage/cards/s/SandsteppeScavenger.java
@@ -18,7 +18,7 @@ public final class SandsteppeScavenger extends CardImpl {
 
     public SandsteppeScavenger(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{G}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.subtype.add(SubType.SCOUT);
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
diff --git a/Mage.Sets/src/mage/cards/s/ScrapyardMongrel.java b/Mage.Sets/src/mage/cards/s/ScrapyardMongrel.java
index dc4727c640..412f2bf00f 100644
--- a/Mage.Sets/src/mage/cards/s/ScrapyardMongrel.java
+++ b/Mage.Sets/src/mage/cards/s/ScrapyardMongrel.java
@@ -26,7 +26,7 @@ public final class ScrapyardMongrel extends CardImpl {
 
     public ScrapyardMongrel(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
 
         this.color.setRed(true);
         this.power = new MageInt(3);
diff --git a/Mage.Sets/src/mage/cards/s/SnowHound.java b/Mage.Sets/src/mage/cards/s/SnowHound.java
index e671b45802..1b9e1212b5 100644
--- a/Mage.Sets/src/mage/cards/s/SnowHound.java
+++ b/Mage.Sets/src/mage/cards/s/SnowHound.java
@@ -35,7 +35,7 @@ public final class SnowHound extends CardImpl {
 
     public SnowHound(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.power = new MageInt(1);
         this.toughness = new MageInt(1);
 
diff --git a/Mage.Sets/src/mage/cards/t/ThrabenFoulbloods.java b/Mage.Sets/src/mage/cards/t/ThrabenFoulbloods.java
index 7460f5d5a2..a2a07b2283 100644
--- a/Mage.Sets/src/mage/cards/t/ThrabenFoulbloods.java
+++ b/Mage.Sets/src/mage/cards/t/ThrabenFoulbloods.java
@@ -26,7 +26,7 @@ public final class ThrabenFoulbloods extends CardImpl {
     public ThrabenFoulbloods(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}");
         this.subtype.add(SubType.ZOMBIE);
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.power = new MageInt(3);
         this.toughness = new MageInt(2);
 
diff --git a/Mage.Sets/src/mage/cards/t/ThrabenPurebloods.java b/Mage.Sets/src/mage/cards/t/ThrabenPurebloods.java
index ecc9344de7..e5c7d92fb6 100644
--- a/Mage.Sets/src/mage/cards/t/ThrabenPurebloods.java
+++ b/Mage.Sets/src/mage/cards/t/ThrabenPurebloods.java
@@ -16,7 +16,7 @@ public final class ThrabenPurebloods extends CardImpl {
 
     public ThrabenPurebloods(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{W}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
 
         this.power = new MageInt(3);
         this.toughness = new MageInt(5);
diff --git a/Mage.Sets/src/mage/cards/t/ThrashingMossdog.java b/Mage.Sets/src/mage/cards/t/ThrashingMossdog.java
index d3a8501c73..c107bab3a1 100644
--- a/Mage.Sets/src/mage/cards/t/ThrashingMossdog.java
+++ b/Mage.Sets/src/mage/cards/t/ThrashingMossdog.java
@@ -23,7 +23,7 @@ public final class ThrashingMossdog extends CardImpl {
     public ThrashingMossdog (UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{G}");
         this.subtype.add(SubType.PLANT);
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
 
         this.power = new MageInt(3);
         this.toughness = new MageInt(3);
diff --git a/Mage.Sets/src/mage/cards/t/TwoHeadedCerberus.java b/Mage.Sets/src/mage/cards/t/TwoHeadedCerberus.java
index 495ca8c029..476abb0bce 100644
--- a/Mage.Sets/src/mage/cards/t/TwoHeadedCerberus.java
+++ b/Mage.Sets/src/mage/cards/t/TwoHeadedCerberus.java
@@ -17,7 +17,7 @@ public final class TwoHeadedCerberus extends CardImpl {
 
     public TwoHeadedCerberus(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{R}{R}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
 
         this.power = new MageInt(1);
         this.toughness = new MageInt(2);
diff --git a/Mage.Sets/src/mage/cards/u/UnderworldCerberus.java b/Mage.Sets/src/mage/cards/u/UnderworldCerberus.java
index 85bed3bdbc..85f0a72e52 100644
--- a/Mage.Sets/src/mage/cards/u/UnderworldCerberus.java
+++ b/Mage.Sets/src/mage/cards/u/UnderworldCerberus.java
@@ -25,7 +25,7 @@ public final class UnderworldCerberus extends CardImpl {
 
     public UnderworldCerberus(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{R}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
 
         this.power = new MageInt(6);
         this.toughness = new MageInt(6);
diff --git a/Mage.Sets/src/mage/cards/u/UnderworldRageHound.java b/Mage.Sets/src/mage/cards/u/UnderworldRageHound.java
index ee3ee87feb..0040b79403 100644
--- a/Mage.Sets/src/mage/cards/u/UnderworldRageHound.java
+++ b/Mage.Sets/src/mage/cards/u/UnderworldRageHound.java
@@ -20,7 +20,7 @@ public final class UnderworldRageHound extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}");
 
         this.subtype.add(SubType.ELEMENTAL);
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.power = new MageInt(3);
         this.toughness = new MageInt(1);
 
diff --git a/Mage.Sets/src/mage/cards/v/VampireHounds.java b/Mage.Sets/src/mage/cards/v/VampireHounds.java
index f231c28a65..7cfaeabfd4 100644
--- a/Mage.Sets/src/mage/cards/v/VampireHounds.java
+++ b/Mage.Sets/src/mage/cards/v/VampireHounds.java
@@ -23,7 +23,7 @@ public final class VampireHounds extends CardImpl {
     public VampireHounds(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}");
         this.subtype.add(SubType.VAMPIRE);
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
 
diff --git a/Mage.Sets/src/mage/cards/w/WarclampMastiff.java b/Mage.Sets/src/mage/cards/w/WarclampMastiff.java
index 80478a69b6..1235a7b540 100644
--- a/Mage.Sets/src/mage/cards/w/WarclampMastiff.java
+++ b/Mage.Sets/src/mage/cards/w/WarclampMastiff.java
@@ -17,7 +17,7 @@ public final class WarclampMastiff extends CardImpl {
 
     public WarclampMastiff(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{W}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
 
         this.power = new MageInt(1);
         this.toughness = new MageInt(1);
diff --git a/Mage.Sets/src/mage/cards/w/Watchdog.java b/Mage.Sets/src/mage/cards/w/Watchdog.java
index f2fe75c581..e3abcaf92c 100644
--- a/Mage.Sets/src/mage/cards/w/Watchdog.java
+++ b/Mage.Sets/src/mage/cards/w/Watchdog.java
@@ -28,7 +28,7 @@ public final class Watchdog extends CardImpl {
 
     public Watchdog(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.power = new MageInt(1);
         this.toughness = new MageInt(2);
 
diff --git a/Mage.Sets/src/mage/cards/w/WildDogs.java b/Mage.Sets/src/mage/cards/w/WildDogs.java
index 6d5b91cd19..58202239c7 100644
--- a/Mage.Sets/src/mage/cards/w/WildDogs.java
+++ b/Mage.Sets/src/mage/cards/w/WildDogs.java
@@ -30,7 +30,7 @@ public final class WildDogs extends CardImpl {
 
     public WildDogs(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{G}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
         this.power = new MageInt(2);
         this.toughness = new MageInt(1);
 
diff --git a/Mage.Sets/src/mage/cards/w/WildMongrel.java b/Mage.Sets/src/mage/cards/w/WildMongrel.java
index 861fc78d4c..004c7b44fa 100644
--- a/Mage.Sets/src/mage/cards/w/WildMongrel.java
+++ b/Mage.Sets/src/mage/cards/w/WildMongrel.java
@@ -24,7 +24,7 @@ public final class WildMongrel extends CardImpl {
 
     public WildMongrel(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{G}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
 
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
diff --git a/Mage.Sets/src/mage/cards/w/WildfireCerberus.java b/Mage.Sets/src/mage/cards/w/WildfireCerberus.java
index 33bedaa6ae..109b94a19a 100644
--- a/Mage.Sets/src/mage/cards/w/WildfireCerberus.java
+++ b/Mage.Sets/src/mage/cards/w/WildfireCerberus.java
@@ -30,7 +30,7 @@ public final class WildfireCerberus extends CardImpl {
 
     public WildfireCerberus(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{R}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
 
         this.power = new MageInt(4);
         this.toughness = new MageInt(3);
diff --git a/Mage.Sets/src/mage/cards/z/ZodiacDog.java b/Mage.Sets/src/mage/cards/z/ZodiacDog.java
index 9fb27e6f9b..d0e42b72b4 100644
--- a/Mage.Sets/src/mage/cards/z/ZodiacDog.java
+++ b/Mage.Sets/src/mage/cards/z/ZodiacDog.java
@@ -17,7 +17,7 @@ public final class ZodiacDog extends CardImpl {
 
     public ZodiacDog(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{R}");
-        this.subtype.add(SubType.HOUND);
+        this.subtype.add(SubType.DOG);
 
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
diff --git a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java
index 23adfada9f..8e32f24aec 100644
--- a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java
+++ b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java
@@ -117,6 +117,9 @@ public class VerifyCardDataTest {
         // subtype
         skipListCreate(SKIP_LIST_SUBTYPE);
         skipListAddName(SKIP_LIST_SUBTYPE, "UGL", "Miss Demeanor");
+        // the following is temporary
+        subtypesToIgnore.add("Dog");
+        subtypesToIgnore.add("Hound");
 
         // number
         skipListCreate(SKIP_LIST_NUMBER);
diff --git a/Mage/src/main/java/mage/constants/SubType.java b/Mage/src/main/java/mage/constants/SubType.java
index d9a0ad83b0..7f0c89242f 100644
--- a/Mage/src/main/java/mage/constants/SubType.java
+++ b/Mage/src/main/java/mage/constants/SubType.java
@@ -119,6 +119,7 @@ public enum SubType {
     DEVIL("Devil", SubTypeSet.CreatureType),
     DINOSAUR("Dinosaur", SubTypeSet.CreatureType), // With Ixalan now being spoiled, need this to be selectable
     DJINN("Djinn", SubTypeSet.CreatureType),
+    DOG("Dog", SubTypeSet.CreatureType),
     DRAGON("Dragon", SubTypeSet.CreatureType),
     DRAKE("Drake", SubTypeSet.CreatureType),
     DREADNOUGHT("Dreadnought", SubTypeSet.CreatureType),
@@ -175,7 +176,6 @@ public enum SubType {
     HOMUNCULUS("Homunculus", SubTypeSet.CreatureType),
     HORROR("Horror", SubTypeSet.CreatureType),
     HORSE("Horse", SubTypeSet.CreatureType),
-    HOUND("Hound", SubTypeSet.CreatureType),
     HUMAN("Human", SubTypeSet.CreatureType),
     HUNTER("Hunter", SubTypeSet.CreatureType),
     HUTT("Hutt", SubTypeSet.CreatureType, true), // Star Wars
diff --git a/Mage/src/main/java/mage/game/permanent/token/HoundToken.java b/Mage/src/main/java/mage/game/permanent/token/DogToken.java
similarity index 53%
rename from Mage/src/main/java/mage/game/permanent/token/HoundToken.java
rename to Mage/src/main/java/mage/game/permanent/token/DogToken.java
index a20c160fc1..a229350513 100644
--- a/Mage/src/main/java/mage/game/permanent/token/HoundToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/DogToken.java
@@ -1,31 +1,29 @@
-
-
 package mage.game.permanent.token;
+
+import mage.MageInt;
 import mage.constants.CardType;
 import mage.constants.SubType;
-import mage.MageInt;
 
 /**
- *
  * @author spjspj
  */
-public final class HoundToken extends TokenImpl {
+public final class DogToken extends TokenImpl {
 
-    public HoundToken() {
-        super("Hound", "1/1 green Hound creature token");
+    public DogToken() {
+        super("Dog", "1/1 green Dog creature token");
         cardType.add(CardType.CREATURE);
-        subtype.add(SubType.HOUND);
+        subtype.add(SubType.DOG);
 
         color.setGreen(true);
         power = new MageInt(1);
         toughness = new MageInt(1);
     }
 
-    public HoundToken(final HoundToken token) {
+    private DogToken(final DogToken token) {
         super(token);
     }
 
-    public HoundToken copy() {
-        return new HoundToken(this);
+    public DogToken copy() {
+        return new DogToken(this);
     }
 }
diff --git a/Mage/src/main/java/mage/game/permanent/token/MowuToken.java b/Mage/src/main/java/mage/game/permanent/token/MowuToken.java
index 7312106610..5d38df4ce0 100644
--- a/Mage/src/main/java/mage/game/permanent/token/MowuToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/MowuToken.java
@@ -1,4 +1,3 @@
-
 package mage.game.permanent.token;
 
 import mage.MageInt;
@@ -13,11 +12,11 @@ import mage.constants.SuperType;
 public final class MowuToken extends TokenImpl {
 
     public MowuToken() {
-        super("Mowu", "legendary 3/3 green Hound creature token named Mowu");
+        super("Mowu", "legendary 3/3 green Dog creature token named Mowu");
         cardType.add(CardType.CREATURE);
         color.setGreen(true);
         this.addSuperType(SuperType.LEGENDARY);
-        subtype.add(SubType.HOUND);
+        subtype.add(SubType.DOG);
         power = new MageInt(3);
         toughness = new MageInt(3);
     }

From fad462bfecbc499a06b4f725e4ce391a020cead1 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Fri, 5 Jun 2020 20:20:43 -0400
Subject: [PATCH 106/586] Implemented Teferi's Protege

---
 .../src/mage/cards/t/TeferisProtege.java      | 45 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 46 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/t/TeferisProtege.java

diff --git a/Mage.Sets/src/mage/cards/t/TeferisProtege.java b/Mage.Sets/src/mage/cards/t/TeferisProtege.java
new file mode 100644
index 0000000000..cf392d9350
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/t/TeferisProtege.java
@@ -0,0 +1,45 @@
+package mage.cards.t;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.common.TapSourceCost;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.effects.common.DrawDiscardControllerEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class TeferisProtege extends CardImpl {
+
+    public TeferisProtege(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}");
+
+        this.subtype.add(SubType.HUMAN);
+        this.subtype.add(SubType.WIZARD);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(3);
+
+        // {1}{U}, {T}: Draw a card, then discard a card.
+        Ability ability = new SimpleActivatedAbility(
+                new DrawDiscardControllerEffect(1, 1), new ManaCostsImpl("{1}{U}")
+        );
+        ability.addCost(new TapSourceCost());
+        this.addAbility(ability);
+    }
+
+    private TeferisProtege(final TeferisProtege card) {
+        super(card);
+    }
+
+    @Override
+    public TeferisProtege copy() {
+        return new TeferisProtege(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index be34ac3a06..58795902e5 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -44,6 +44,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Quirion Dryad", 198, Rarity.UNCOMMON, mage.cards.q.QuirionDryad.class));
         cards.add(new SetCardInfo("Rain of Revelation", 61, Rarity.UNCOMMON, mage.cards.r.RainOfRevelation.class));
         cards.add(new SetCardInfo("Runed Halo", 32, Rarity.RARE, mage.cards.r.RunedHalo.class));
+        cards.add(new SetCardInfo("Teferi's Protege", 77, Rarity.COMMON, mage.cards.t.TeferisProtege.class));
         cards.add(new SetCardInfo("Tormod's Crypt", 241, Rarity.UNCOMMON, mage.cards.t.TormodsCrypt.class));
         cards.add(new SetCardInfo("Ugin, the Spirit Dragon", 1, Rarity.MYTHIC, mage.cards.u.UginTheSpiritDragon.class));
     }

From d18aab340a5ac06e54d060dcb5d9901fa564c0a3 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Fri, 5 Jun 2020 20:21:11 -0400
Subject: [PATCH 107/586] Implemented Wildwood Patrol

---
 .../src/mage/cards/w/WildwoodPatrol.java      | 37 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 38 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/w/WildwoodPatrol.java

diff --git a/Mage.Sets/src/mage/cards/w/WildwoodPatrol.java b/Mage.Sets/src/mage/cards/w/WildwoodPatrol.java
new file mode 100644
index 0000000000..e0ab07f65b
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/w/WildwoodPatrol.java
@@ -0,0 +1,37 @@
+package mage.cards.w;
+
+import mage.MageInt;
+import mage.abilities.keyword.TrampleAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class WildwoodPatrol extends CardImpl {
+
+    public WildwoodPatrol(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}");
+
+        this.subtype.add(SubType.CENTAUR);
+        this.subtype.add(SubType.SCOUT);
+        this.power = new MageInt(4);
+        this.toughness = new MageInt(2);
+
+        // Trample
+        this.addAbility(TrampleAbility.getInstance());
+    }
+
+    private WildwoodPatrol(final WildwoodPatrol card) {
+        super(card);
+    }
+
+    @Override
+    public WildwoodPatrol copy() {
+        return new WildwoodPatrol(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 58795902e5..6bc142e8a1 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -47,5 +47,6 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Teferi's Protege", 77, Rarity.COMMON, mage.cards.t.TeferisProtege.class));
         cards.add(new SetCardInfo("Tormod's Crypt", 241, Rarity.UNCOMMON, mage.cards.t.TormodsCrypt.class));
         cards.add(new SetCardInfo("Ugin, the Spirit Dragon", 1, Rarity.MYTHIC, mage.cards.u.UginTheSpiritDragon.class));
+        cards.add(new SetCardInfo("Wildwood Patrol", 339, Rarity.COMMON, mage.cards.w.WildwoodPatrol.class));
     }
 }

From 28d86f7a152bc62c8648cd360074d3050d169622 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Fri, 5 Jun 2020 20:23:00 -0400
Subject: [PATCH 108/586] Implemented Mystic Skyfish

---
 Mage.Sets/src/mage/cards/m/MysticSkyfish.java | 41 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 42 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/m/MysticSkyfish.java

diff --git a/Mage.Sets/src/mage/cards/m/MysticSkyfish.java b/Mage.Sets/src/mage/cards/m/MysticSkyfish.java
new file mode 100644
index 0000000000..9c75a23b5c
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/m/MysticSkyfish.java
@@ -0,0 +1,41 @@
+package mage.cards.m;
+
+import mage.MageInt;
+import mage.abilities.common.DrawSecondCardTriggeredAbility;
+import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
+import mage.abilities.keyword.FlyingAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class MysticSkyfish extends CardImpl {
+
+    public MysticSkyfish(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}");
+
+        this.subtype.add(SubType.FISH);
+        this.power = new MageInt(3);
+        this.toughness = new MageInt(1);
+
+        // Whenever you draw your second card each turn, Mystic Skyfish gains flying until end of turn.
+        this.addAbility(new DrawSecondCardTriggeredAbility(
+                new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.EndOfTurn), false)
+        );
+    }
+
+    private MysticSkyfish(final MysticSkyfish card) {
+        super(card);
+    }
+
+    @Override
+    public MysticSkyfish copy() {
+        return new MysticSkyfish(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 6bc142e8a1..c8cee958be 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -41,6 +41,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Island", 310, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Jeskai Elder", 53, Rarity.UNCOMMON, mage.cards.j.JeskaiElder.class));
         cards.add(new SetCardInfo("Mangara, the Diplomat", 27, Rarity.MYTHIC, mage.cards.m.MangaraTheDiplomat.class));
+        cards.add(new SetCardInfo("Mystic Skyfish", 326, Rarity.COMMON, mage.cards.m.MysticSkyfish.class));
         cards.add(new SetCardInfo("Quirion Dryad", 198, Rarity.UNCOMMON, mage.cards.q.QuirionDryad.class));
         cards.add(new SetCardInfo("Rain of Revelation", 61, Rarity.UNCOMMON, mage.cards.r.RainOfRevelation.class));
         cards.add(new SetCardInfo("Runed Halo", 32, Rarity.RARE, mage.cards.r.RunedHalo.class));

From 430ac1a2634a704f0cea966e1a12137c08af5b63 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Fri, 5 Jun 2020 20:28:07 -0400
Subject: [PATCH 109/586] Implemented Shipwreck Dowser

---
 .../src/mage/cards/s/ShipwreckDowser.java     | 51 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 52 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/s/ShipwreckDowser.java

diff --git a/Mage.Sets/src/mage/cards/s/ShipwreckDowser.java b/Mage.Sets/src/mage/cards/s/ShipwreckDowser.java
new file mode 100644
index 0000000000..b68abcb097
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/ShipwreckDowser.java
@@ -0,0 +1,51 @@
+package mage.cards.s;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect;
+import mage.abilities.keyword.ProwessAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.filter.FilterCard;
+import mage.filter.common.FilterInstantOrSorceryCard;
+import mage.target.common.TargetCardInYourGraveyard;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class ShipwreckDowser extends CardImpl {
+
+    private static final FilterCard filter
+            = new FilterInstantOrSorceryCard("instant or sorcery card from your graveyard");
+
+    public ShipwreckDowser(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{U}");
+
+        this.subtype.add(SubType.MERFOLK);
+        this.subtype.add(SubType.WIZARD);
+        this.power = new MageInt(3);
+        this.toughness = new MageInt(3);
+
+        // Prowess
+        this.addAbility(new ProwessAbility());
+
+        // When Shipwreck Dowser enters the battlefield, return target instant or sorcery card from your graveyard to your hand.
+        Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect());
+        ability.addTarget(new TargetCardInYourGraveyard(filter));
+        this.addAbility(ability);
+    }
+
+    private ShipwreckDowser(final ShipwreckDowser card) {
+        super(card);
+    }
+
+    @Override
+    public ShipwreckDowser copy() {
+        return new ShipwreckDowser(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index c8cee958be..b33360948b 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -45,6 +45,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Quirion Dryad", 198, Rarity.UNCOMMON, mage.cards.q.QuirionDryad.class));
         cards.add(new SetCardInfo("Rain of Revelation", 61, Rarity.UNCOMMON, mage.cards.r.RainOfRevelation.class));
         cards.add(new SetCardInfo("Runed Halo", 32, Rarity.RARE, mage.cards.r.RunedHalo.class));
+        cards.add(new SetCardInfo("Shipwreck Dowser", 71, Rarity.UNCOMMON, mage.cards.s.ShipwreckDowser.class));
         cards.add(new SetCardInfo("Teferi's Protege", 77, Rarity.COMMON, mage.cards.t.TeferisProtege.class));
         cards.add(new SetCardInfo("Tormod's Crypt", 241, Rarity.UNCOMMON, mage.cards.t.TormodsCrypt.class));
         cards.add(new SetCardInfo("Ugin, the Spirit Dragon", 1, Rarity.MYTHIC, mage.cards.u.UginTheSpiritDragon.class));

From 7dda0903e248ec21481c4111ae22fa2ed9374d9f Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Fri, 5 Jun 2020 20:29:15 -0400
Subject: [PATCH 110/586] Implemented Storm Caller

---
 Mage.Sets/src/mage/cards/s/StormCaller.java | 41 +++++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java    |  1 +
 2 files changed, 42 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/s/StormCaller.java

diff --git a/Mage.Sets/src/mage/cards/s/StormCaller.java b/Mage.Sets/src/mage/cards/s/StormCaller.java
new file mode 100644
index 0000000000..0fbd4f64b1
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/StormCaller.java
@@ -0,0 +1,41 @@
+package mage.cards.s;
+
+import mage.MageInt;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.effects.common.DamagePlayersEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.TargetController;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class StormCaller extends CardImpl {
+
+    public StormCaller(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}");
+
+        this.subtype.add(SubType.OGRE);
+        this.subtype.add(SubType.SHAMAN);
+        this.power = new MageInt(3);
+        this.toughness = new MageInt(2);
+
+        // When Storm Caller enters the battlefield, it deals 2 damage to each opponent.
+        this.addAbility(new EntersBattlefieldTriggeredAbility(
+                new DamagePlayersEffect(2, TargetController.OPPONENT, "it")
+        ));
+    }
+
+    private StormCaller(final StormCaller card) {
+        super(card);
+    }
+
+    @Override
+    public StormCaller copy() {
+        return new StormCaller(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index b33360948b..5a075c0769 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -46,6 +46,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Rain of Revelation", 61, Rarity.UNCOMMON, mage.cards.r.RainOfRevelation.class));
         cards.add(new SetCardInfo("Runed Halo", 32, Rarity.RARE, mage.cards.r.RunedHalo.class));
         cards.add(new SetCardInfo("Shipwreck Dowser", 71, Rarity.UNCOMMON, mage.cards.s.ShipwreckDowser.class));
+        cards.add(new SetCardInfo("Storm Caller", 335, Rarity.COMMON, mage.cards.s.StormCaller.class));
         cards.add(new SetCardInfo("Teferi's Protege", 77, Rarity.COMMON, mage.cards.t.TeferisProtege.class));
         cards.add(new SetCardInfo("Tormod's Crypt", 241, Rarity.UNCOMMON, mage.cards.t.TormodsCrypt.class));
         cards.add(new SetCardInfo("Ugin, the Spirit Dragon", 1, Rarity.MYTHIC, mage.cards.u.UginTheSpiritDragon.class));

From e4acf7affb868f5029c035b71784119b32c8f43c Mon Sep 17 00:00:00 2001
From: 18ths <13023067+18ths@users.noreply.github.com>
Date: Sat, 6 Jun 2020 06:01:36 +0200
Subject: [PATCH 111/586] fixed #6540 qasali ambusher (#6573)

* Qasali Ambusher - fixed that it can't be casted without mana cost and with flash (#6540)
---
 .../src/mage/cards/q/QasaliAmbusher.java      | 90 ++++++-------------
 1 file changed, 29 insertions(+), 61 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/q/QasaliAmbusher.java b/Mage.Sets/src/mage/cards/q/QasaliAmbusher.java
index 9fa8d53023..32f19ddb9e 100644
--- a/Mage.Sets/src/mage/cards/q/QasaliAmbusher.java
+++ b/Mage.Sets/src/mage/cards/q/QasaliAmbusher.java
@@ -2,16 +2,22 @@ package mage.cards.q;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.ActivatedAbilityImpl;
-import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.Ability;
+import mage.abilities.condition.CompoundCondition;
+import mage.abilities.condition.Condition;
+import mage.abilities.condition.common.AttackedThisStepCondition;
+import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
+import mage.abilities.costs.AlternativeCostSourceAbility;
+import mage.abilities.decorator.ConditionalAsThoughEffect;
 import mage.abilities.effects.common.continuous.CastAsThoughItHadFlashSourceEffect;
 import mage.abilities.keyword.ReachAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.*;
 import mage.filter.common.FilterControlledLandPermanent;
-import mage.game.Game;
-import mage.game.combat.CombatGroup;
+import mage.filter.common.FilterControlledPermanent;
+import mage.filter.predicate.Predicates;
+import mage.watchers.common.PlayerAttackedStepWatcher;
 
 /**
  *
@@ -19,6 +25,18 @@ import mage.game.combat.CombatGroup;
  */
 public final class QasaliAmbusher extends CardImpl {
 
+    private static final FilterControlledPermanent filterForest = new FilterControlledPermanent();
+    private static final FilterControlledPermanent filterPlains = new FilterControlledPermanent();
+
+    static {
+        filterForest.add(SubType.FOREST.getPredicate());
+        filterPlains.add(SubType.PLAINS.getPredicate());
+    }
+
+    private static final Condition condition =
+            new CompoundCondition("If a creature is attacking you and you control a Forest and a Plains",
+              AttackedThisStepCondition.instance, new PermanentsOnTheBattlefieldCondition(filterForest), new PermanentsOnTheBattlefieldCondition(filterPlains));
+
     public QasaliAmbusher(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}{W}");
         this.subtype.add(SubType.CAT);
@@ -30,10 +48,13 @@ public final class QasaliAmbusher extends CardImpl {
         // Reach
         this.addAbility(ReachAbility.getInstance());
 
-        // If a creature is attacking you and you control a Forest and a Plains, 
+        // If a creature is attacking you and you control a Forest and a Plains,
         // you may cast Qasali Ambusher without paying its mana cost and as though it had flash.
-        this.addAbility(new QasaliAmbusherAbility());
-
+        Ability ability = new AlternativeCostSourceAbility(null, condition);
+        ability.addEffect(new ConditionalAsThoughEffect(new CastAsThoughItHadFlashSourceEffect(Duration.EndOfGame), condition)
+                .setText("you may cast {this} without paying its mana cost and as though it had flash"));
+        ability.addWatcher(new PlayerAttackedStepWatcher());
+        this.addAbility(ability);
     }
 
     public QasaliAmbusher(final QasaliAmbusher card) {
@@ -44,57 +65,4 @@ public final class QasaliAmbusher extends CardImpl {
     public QasaliAmbusher copy() {
         return new QasaliAmbusher(this);
     }
-}
-
-class QasaliAmbusherAbility extends ActivatedAbilityImpl {
-
-    private static final FilterControlledLandPermanent filterPlains = new FilterControlledLandPermanent();
-    private static final FilterControlledLandPermanent filterForest = new FilterControlledLandPermanent();
-
-    static {
-        filterPlains.add(SubType.PLAINS.getPredicate());
-        filterForest.add(SubType.FOREST.getPredicate());
-    }
-
-    public QasaliAmbusherAbility() {
-        super(Zone.HAND, new CastAsThoughItHadFlashSourceEffect(Duration.EndOfGame), new ManaCostsImpl());
-        this.timing = TimingRule.INSTANT;
-        this.usesStack = false;
-    }
-
-    public QasaliAmbusherAbility(final QasaliAmbusherAbility ability) {
-        super(ability);
-    }
-
-    @Override
-    public QasaliAmbusherAbility copy() {
-        return new QasaliAmbusherAbility(this);
-    }
-
-    @Override
-    public ActivationStatus canActivate(UUID playerId, Game game) {
-        if (!game.getBattlefield().getActivePermanents(filterPlains,
-                this.getControllerId(), this.getSourceId(), game).isEmpty()
-                && !game.getBattlefield().getActivePermanents(filterForest,
-                        this.getControllerId(), this.getSourceId(), game).isEmpty()) {
-            for (CombatGroup group : game.getCombat().getGroups()) {
-                if (isControlledBy(group.getDefenderId())) {
-                    return super.canActivate(playerId, game);
-                }
-            }
-        }
-        return ActivationStatus.getFalse();
-    }
-
-    @Override
-    public String getRule(boolean all) {
-        return this.getRule();
-    }
-
-    @Override
-    public String getRule() {
-        return "If a creature is attacking you and you control a Forest and "
-                + "a Plains, you may cast {this} without paying its mana "
-                + "cost and as though it had flash.";
-    }
-}
+}
\ No newline at end of file

From 818ec22d8e340af5b965b5245dea229eef53cd61 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sat, 6 Jun 2020 09:00:46 -0400
Subject: [PATCH 112/586] updated M21 spoiler

---
 Mage.Sets/src/mage/sets/CoreSet2021.java | 2 +-
 Utils/mtg-cards-data.txt                 | 5 ++++-
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index fe86ef65f4..8c6533363d 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -33,10 +33,10 @@ public final class CoreSet2021 extends ExpansionSet {
         this.ratioBoosterMythic = 8;
         this.maxCardNumberInBooster = 274;
 
-        cards.add(new SetCardInfo("Double Vision", 142, Rarity.RARE, mage.cards.d.DoubleVision.class));
         cards.add(new SetCardInfo("Azusa, Lost but Seeking", 173, Rarity.RARE, mage.cards.a.AzusaLostButSeeking.class));
         cards.add(new SetCardInfo("Baneslayer Angel", 4, Rarity.MYTHIC, mage.cards.b.BaneslayerAngel.class));
         cards.add(new SetCardInfo("Containment Priest", 314, Rarity.RARE, mage.cards.c.ContainmentPriest.class));
+        cards.add(new SetCardInfo("Double Vision", 142, Rarity.RARE, mage.cards.d.DoubleVision.class));
         cards.add(new SetCardInfo("Fierce Empath", 181, Rarity.UNCOMMON, mage.cards.f.FierceEmpath.class));
         cards.add(new SetCardInfo("Grim Tutor", 103, Rarity.MYTHIC, mage.cards.g.GrimTutor.class));
         cards.add(new SetCardInfo("Island", 310, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt
index 79d88a93f9..5ef42ee306 100644
--- a/Utils/mtg-cards-data.txt
+++ b/Utils/mtg-cards-data.txt
@@ -37450,18 +37450,21 @@ Teferi, Master of Time|Core Set 2021|75|M|{2}{U}{U}|Legendary Planeswalker - Tef
 Teferi's Ageless Insight|Core Set 2021|76|R|{2}{U}{U}|Legendary Enchantment|||If you would draw a card except the first one you draw in each of your draw steps, draw two cards instead.|
 Teferi's Protege|Core Set 2021|77|C|{2}{U}|Creature - Human Wizard|2|3|{1}{U}, {T}: Draw a card, then discard a card.|
 Teferi's Tutelage|Core Set 2021|78|U|{2}{U}|Enchantment|||When Teferi's Tutelage enters the battlefield, draw a card, then discard a card.$Whenever you draw a card, target opponent mills two cards.|
+Bad Deal|Core Set 2021|89|U|{4}{B}{B}|Sorcery|||You draw two cards and each opponent discards two cards. Each player loses 2 life.|
 Grim Tutor|Core Set 2021|103|M|{1}{B}{B}|Sorcery|||Search your library for a card and put that card into your hand, then shuffle your library. You lose 3 life.|
 Liliana, Waker of the Dead|Core Set 2021|108|M|{2}{B}{B}|Legendary Planeswalker - Liliana|4|+1: Each player discards a card. Each opponent who can't loses 3 life.$−3: Target creature gets -X/-X until end of turn, where X is the number of cards in your graveyard.$−7: You get an emblem with "At the beginning of combat on your turn, put target creature card from a graveyard onto the battlefield under your control. It gains haste."|
+Necromentia|Core Set 2021|116|R|{1}{B}{B}|Sorcery|||Choose a card name other than a basic land card name. Search target opponent's graveyard, hand, and library for any number of cards with that name and exile them. That player shuffles their library, then creates a 2/2 black Zombie creature token for each card exiled from their hand this way.|
 Peer into the Abyss|Core Set 2021|117|R|{4}{B}{B}{B}|Sorcery|||Target player draws cards equal to half the number of cards in their library and loses half their life. Round up each time.|
 Vito, Thorn of the Dusk Rose|Core Set 2021|127|R|{2}{B}|Legendary Creature - Vampire Cleric|1|3|Whenever you gain life, target opponent loses that much life.${3}{B}{B}: Creatures you control gain lifelink until end of turn.|
 Double Vision|Core Set 2021|142|R|{3}{R}{R}|Enchantment|||Whenever you cast your first instant or sorcery spell each turn, copy that spell. You may choose new targets for the copy.|
 Gadrak, the Crown-Scourge|Core Set 2021|146|R|{2}{R}|Legendary Creature - Dragon|5|4|Flying$Gadrak, the Crown-Scourge can't attack unless you control four or more artifacts.$At the beginning of your end step, create a Treasure token for each nontoken creature that died this turn.|
 Azusa, Lost but Seeking|Core Set 2021|173|R|{2}{G}|Legendary Creature - Human Monk|1|2|You may play two additional lands on each of your turns.|
 Fierce Empath|Core Set 2021|181|U|{2}{G}|Creature - Elf|1|1|When Fierce Empath enters the battlefield, you may search your library for a creature card with converted mana cost 6 or greater, reveal it, put it into your hand, then shuffle your library.|
+Llanowar Visionary|Core Set 2021|193|C|{2}{G}|Creature - Elf Druid|2|2|When Llanowar Visionary enters the battlefield, draw a card.${T}: Add {G}.|
 Primal Might|Core Set 2021|197|R|{X}{G}|Sorcery|||Target creature you control gets +X/+X until end of turn. Then it fights up to one target creature you don't control.|
 Quirion Dryad|Core Set 2021|198|U|{1}{G}|Creature - Dryad|1|1|Whenever you cast a white, blue, black, or red spell, put a +1/+1 counter on Quirion Dryad.|
 Indulging Patrician|Core Set 2021|219|U|{1}{W}{B}|Creature - Vampire Noble|1|4|Flying$Lifelink$At the beginning of your end step, if you gained 3 or more life this turn, each opponent loses 3 life.|
-Chromatic Orrery|Core Set 2021|228|M|{7}|Legendary Artifact|||You may spend mana as though it were mana of any color.${T}, Add {C}{C}{C}{C}{C}.${5}, {T}: Draw a card for each color among permanents you control.|
+Chromatic Orrery|Core Set 2021|228|M|{7}|Legendary Artifact|||You may spend mana as though it were mana of any color.${T}: Add {C}{C}{C}{C}{C}.${5}, {T}: Draw a card for each color among permanents you control.|
 Sparkhunter Masticore|Core Set 2021|240|R|{3}|Artifact Creature - Masticore|3|4|As an additional cost to cast this spell, discard a card.$Protection from planeswalkers${1}: Sparkhunter Masticore deals 1 damage to target planeswalker.${3}: Sparkhunter Masticore gains indestructible until end of turn.|
 Tormod's Crypt|Core Set 2021|241|U|{0}|Artifact|||{T}, Sacrifice Tormod's Crypt: Exile all cards from target player's graveyard.|
 Rin and Seri, Inseparable|Core Set 2021|278|M|{1}{R}{G}{W}|Legendary Creature - Dog Cat|4|4|Whenever you cast a Dog spell, create a 1/1 green Cat creature token.$Whenever you cast a Cat spell, create a 1/1 white Dog creature token.${R}{G}{W}: Rin and Seri, Inseparable deals damage to any target equal to the number of Dogs you control. You gain life equal to the number of Cats you control.|

From 3b80b44fb9203a9e62d66a0209f3bdb4cf1964aa Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sat, 6 Jun 2020 09:02:03 -0400
Subject: [PATCH 113/586] Implemented Llanowar Visionary

---
 .../src/mage/cards/l/LlanowarVisionary.java   | 42 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 43 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/l/LlanowarVisionary.java

diff --git a/Mage.Sets/src/mage/cards/l/LlanowarVisionary.java b/Mage.Sets/src/mage/cards/l/LlanowarVisionary.java
new file mode 100644
index 0000000000..d8bb237913
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/l/LlanowarVisionary.java
@@ -0,0 +1,42 @@
+package mage.cards.l;
+
+import mage.MageInt;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.effects.common.DrawCardSourceControllerEffect;
+import mage.abilities.mana.GreenManaAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class LlanowarVisionary extends CardImpl {
+
+    public LlanowarVisionary(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}");
+
+        this.subtype.add(SubType.ELF);
+        this.subtype.add(SubType.DRUID);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(2);
+
+        // When Llanowar Visionary enters the battlefield, draw a card.
+        this.addAbility(new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1)));
+
+        // {T}: Add {G}.
+        this.addAbility(new GreenManaAbility());
+    }
+
+    private LlanowarVisionary(final LlanowarVisionary card) {
+        super(card);
+    }
+
+    @Override
+    public LlanowarVisionary copy() {
+        return new LlanowarVisionary(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 8c6533363d..a843a3fb1a 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -41,6 +41,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Grim Tutor", 103, Rarity.MYTHIC, mage.cards.g.GrimTutor.class));
         cards.add(new SetCardInfo("Island", 310, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Jeskai Elder", 53, Rarity.UNCOMMON, mage.cards.j.JeskaiElder.class));
+        cards.add(new SetCardInfo("Llanowar Visionary", 193, Rarity.COMMON, mage.cards.l.LlanowarVisionary.class));
         cards.add(new SetCardInfo("Mangara, the Diplomat", 27, Rarity.MYTHIC, mage.cards.m.MangaraTheDiplomat.class));
         cards.add(new SetCardInfo("Mystic Skyfish", 326, Rarity.COMMON, mage.cards.m.MysticSkyfish.class));
         cards.add(new SetCardInfo("Quirion Dryad", 198, Rarity.UNCOMMON, mage.cards.q.QuirionDryad.class));

From b722362378a516c94fbd6be757611637bee88f0c Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sat, 6 Jun 2020 09:10:08 -0400
Subject: [PATCH 114/586] Implemented Pack Leader

---
 Mage.Sets/src/mage/cards/p/PackLeader.java | 55 ++++++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java   |  1 +
 2 files changed, 56 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/p/PackLeader.java

diff --git a/Mage.Sets/src/mage/cards/p/PackLeader.java b/Mage.Sets/src/mage/cards/p/PackLeader.java
new file mode 100644
index 0000000000..86fac27413
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/p/PackLeader.java
@@ -0,0 +1,55 @@
+package mage.cards.p;
+
+import mage.MageInt;
+import mage.abilities.common.AttacksTriggeredAbility;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.effects.common.PreventAllDamageToAllEffect;
+import mage.abilities.effects.common.continuous.BoostControlledEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+import mage.constants.TargetController;
+import mage.filter.common.FilterCreaturePermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class PackLeader extends CardImpl {
+
+    private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(SubType.DOG, "Dogs");
+
+    static {
+        filter.add(TargetController.YOU.getControllerPredicate());
+    }
+
+    public PackLeader(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}");
+
+        this.subtype.add(SubType.DOG);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(2);
+
+        // Other Dogs you control get +1/+1.
+        this.addAbility(new SimpleStaticAbility(new BoostControlledEffect(
+                1, 1, Duration.WhileOnBattlefield, filter, true
+        )));
+
+        // Whenever Pack Leader attacks, prevent all combat damage that would be dealt this turn to Dogs you control.
+        this.addAbility(new AttacksTriggeredAbility(new PreventAllDamageToAllEffect(
+                Duration.EndOfTurn, filter, true
+        ).setText("prevent all combat damage that would be dealt this turn to Dogs you control"), false));
+    }
+
+    private PackLeader(final PackLeader card) {
+        super(card);
+    }
+
+    @Override
+    public PackLeader copy() {
+        return new PackLeader(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index a843a3fb1a..e03f7cc786 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -44,6 +44,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Llanowar Visionary", 193, Rarity.COMMON, mage.cards.l.LlanowarVisionary.class));
         cards.add(new SetCardInfo("Mangara, the Diplomat", 27, Rarity.MYTHIC, mage.cards.m.MangaraTheDiplomat.class));
         cards.add(new SetCardInfo("Mystic Skyfish", 326, Rarity.COMMON, mage.cards.m.MysticSkyfish.class));
+        cards.add(new SetCardInfo("Pack Leader", 29, Rarity.RARE, mage.cards.p.PackLeader.class));
         cards.add(new SetCardInfo("Quirion Dryad", 198, Rarity.UNCOMMON, mage.cards.q.QuirionDryad.class));
         cards.add(new SetCardInfo("Rain of Revelation", 61, Rarity.UNCOMMON, mage.cards.r.RainOfRevelation.class));
         cards.add(new SetCardInfo("Runed Halo", 32, Rarity.RARE, mage.cards.r.RunedHalo.class));

From 8f3cde252126f9d93b793e252e475c880107c3c7 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sat, 6 Jun 2020 09:16:38 -0400
Subject: [PATCH 115/586] Implemented Bad Deal

---
 Mage.Sets/src/mage/cards/b/BadDeal.java  | 38 ++++++++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java |  1 +
 2 files changed, 39 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/b/BadDeal.java

diff --git a/Mage.Sets/src/mage/cards/b/BadDeal.java b/Mage.Sets/src/mage/cards/b/BadDeal.java
new file mode 100644
index 0000000000..22116bb83c
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/b/BadDeal.java
@@ -0,0 +1,38 @@
+package mage.cards.b;
+
+import mage.abilities.dynamicvalue.common.StaticValue;
+import mage.abilities.effects.common.DrawCardSourceControllerEffect;
+import mage.abilities.effects.common.LoseLifeAllPlayersEffect;
+import mage.abilities.effects.common.discard.DiscardEachPlayerEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.TargetController;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class BadDeal extends CardImpl {
+
+    public BadDeal(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{B}{B}");
+
+        // You draw two cards and each opponent discards two cards. Each player loses 2 life.
+        this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2).setText("You draw two cards"));
+        this.getSpellAbility().addEffect(new DiscardEachPlayerEffect(
+                StaticValue.get(2), false, TargetController.OPPONENT
+        ).concatBy("and"));
+        this.getSpellAbility().addEffect(new LoseLifeAllPlayersEffect(2).setText("Each player loses 2 life"));
+    }
+
+    private BadDeal(final BadDeal card) {
+        super(card);
+    }
+
+    @Override
+    public BadDeal copy() {
+        return new BadDeal(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index e03f7cc786..94958d0172 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -34,6 +34,7 @@ public final class CoreSet2021 extends ExpansionSet {
         this.maxCardNumberInBooster = 274;
 
         cards.add(new SetCardInfo("Azusa, Lost but Seeking", 173, Rarity.RARE, mage.cards.a.AzusaLostButSeeking.class));
+        cards.add(new SetCardInfo("Bad Deal", 89, Rarity.UNCOMMON, mage.cards.b.BadDeal.class));
         cards.add(new SetCardInfo("Baneslayer Angel", 4, Rarity.MYTHIC, mage.cards.b.BaneslayerAngel.class));
         cards.add(new SetCardInfo("Containment Priest", 314, Rarity.RARE, mage.cards.c.ContainmentPriest.class));
         cards.add(new SetCardInfo("Double Vision", 142, Rarity.RARE, mage.cards.d.DoubleVision.class));

From c5ec9c190fe398d7cf4da88942328b2865431b24 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sat, 6 Jun 2020 09:38:45 -0400
Subject: [PATCH 116/586] Implemented Gadrak, the Crown-Scourge

---
 .../mage/cards/g/GadrakTheCrownScourge.java   | 126 ++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |   1 +
 2 files changed, 127 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/g/GadrakTheCrownScourge.java

diff --git a/Mage.Sets/src/mage/cards/g/GadrakTheCrownScourge.java b/Mage.Sets/src/mage/cards/g/GadrakTheCrownScourge.java
new file mode 100644
index 0000000000..2449c94373
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/g/GadrakTheCrownScourge.java
@@ -0,0 +1,126 @@
+package mage.cards.g;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.BeginningOfEndStepTriggeredAbility;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.condition.Condition;
+import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
+import mage.abilities.decorator.ConditionalRestrictionEffect;
+import mage.abilities.dynamicvalue.DynamicValue;
+import mage.abilities.effects.Effect;
+import mage.abilities.effects.common.CreateTokenEffect;
+import mage.abilities.effects.common.combat.CantAttackSourceEffect;
+import mage.abilities.keyword.FlyingAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.*;
+import mage.filter.StaticFilters;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+import mage.game.events.ZoneChangeEvent;
+import mage.game.permanent.PermanentToken;
+import mage.game.permanent.token.TreasureToken;
+import mage.watchers.Watcher;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class GadrakTheCrownScourge extends CardImpl {
+
+    private static final Condition condition = new PermanentsOnTheBattlefieldCondition(
+            StaticFilters.FILTER_PERMANENT_ARTIFACT, ComparisonType.MORE_THAN, 3
+    );
+
+    public GadrakTheCrownScourge(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}");
+
+        this.addSuperType(SuperType.LEGENDARY);
+        this.subtype.add(SubType.DRAGON);
+        this.power = new MageInt(5);
+        this.toughness = new MageInt(4);
+
+        // Flying
+        this.addAbility(FlyingAbility.getInstance());
+
+        // Gadrak, the Crown-Scourge can't attack unless you control four or more artifacts.
+        this.addAbility(new SimpleStaticAbility(new ConditionalRestrictionEffect(
+                new CantAttackSourceEffect(Duration.WhileOnBattlefield), condition,
+                "{this} can't attack unless you control four or more artifacts"
+        )));
+
+        // At the beginning of your end step, create a Treasure token for each nontoken creature that died this turn.
+        this.addAbility(new BeginningOfEndStepTriggeredAbility(new CreateTokenEffect(
+                new TreasureToken(), GadrakTheCrownScourgeValue.instance
+        ), TargetController.YOU, false), new GadrakTheCrownScourgeWatcher());
+    }
+
+    private GadrakTheCrownScourge(final GadrakTheCrownScourge card) {
+        super(card);
+    }
+
+    @Override
+    public GadrakTheCrownScourge copy() {
+        return new GadrakTheCrownScourge(this);
+    }
+}
+
+enum GadrakTheCrownScourgeValue implements DynamicValue {
+    instance;
+
+    @Override
+    public int calculate(Game game, Ability sourceAbility, Effect effect) {
+        GadrakTheCrownScourgeWatcher watcher = game.getState().getWatcher(GadrakTheCrownScourgeWatcher.class);
+        return watcher != null ? watcher.getDiedThisTurn() : 0;
+    }
+
+    @Override
+    public GadrakTheCrownScourgeValue copy() {
+        return instance;
+    }
+
+    @Override
+    public String getMessage() {
+        return "nontoken creature that died this turn";
+    }
+
+    @Override
+    public String toString() {
+        return "1";
+    }
+}
+
+class GadrakTheCrownScourgeWatcher extends Watcher {
+
+    private int diedThisTurn = 0;
+
+    GadrakTheCrownScourgeWatcher() {
+        super(WatcherScope.GAME);
+    }
+
+    @Override
+    public void watch(GameEvent event, Game game) {
+        if (event.getType() != GameEvent.EventType.ZONE_CHANGE) {
+            return;
+        }
+        ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
+        if (zEvent.isDiesEvent()
+                && zEvent.getTarget() != null
+                && zEvent.getTarget().isCreature()
+                && !(zEvent.getTarget() instanceof PermanentToken)) {
+            diedThisTurn++;
+        }
+    }
+
+    @Override
+    public void reset() {
+        super.reset();
+        diedThisTurn = 0;
+    }
+
+    int getDiedThisTurn() {
+        return diedThisTurn;
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 94958d0172..ff49782f46 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -39,6 +39,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Containment Priest", 314, Rarity.RARE, mage.cards.c.ContainmentPriest.class));
         cards.add(new SetCardInfo("Double Vision", 142, Rarity.RARE, mage.cards.d.DoubleVision.class));
         cards.add(new SetCardInfo("Fierce Empath", 181, Rarity.UNCOMMON, mage.cards.f.FierceEmpath.class));
+        cards.add(new SetCardInfo("Gadrak, the Crown-Scourge", 146, Rarity.RARE, mage.cards.g.GadrakTheCrownScourge.class));
         cards.add(new SetCardInfo("Grim Tutor", 103, Rarity.MYTHIC, mage.cards.g.GrimTutor.class));
         cards.add(new SetCardInfo("Island", 310, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Jeskai Elder", 53, Rarity.UNCOMMON, mage.cards.j.JeskaiElder.class));

From 9e2d822f97a212fe6ca91adeae700e4f647babe2 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sat, 6 Jun 2020 09:49:36 -0400
Subject: [PATCH 117/586] Implemented Spirit of Malevolence

---
 .../src/mage/cards/s/SpiritOfMalevolence.java | 41 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 42 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/s/SpiritOfMalevolence.java

diff --git a/Mage.Sets/src/mage/cards/s/SpiritOfMalevolence.java b/Mage.Sets/src/mage/cards/s/SpiritOfMalevolence.java
new file mode 100644
index 0000000000..946050840c
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/SpiritOfMalevolence.java
@@ -0,0 +1,41 @@
+package mage.cards.s;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.effects.common.GainLifeEffect;
+import mage.abilities.effects.common.LoseLifeOpponentsEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class SpiritOfMalevolence extends CardImpl {
+
+    public SpiritOfMalevolence(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}");
+
+        this.subtype.add(SubType.SPIRIT);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(1);
+
+        // When Spirit of Malevolence dies, each opponent loses 1 life and you gain 1 life.
+        Ability ability = new DiesTriggeredAbility(new LoseLifeOpponentsEffect(1));
+        ability.addEffect(new GainLifeEffect(1).concatBy("and"));
+        this.addAbility(ability);
+    }
+
+    private SpiritOfMalevolence(final SpiritOfMalevolence card) {
+        super(card);
+    }
+
+    @Override
+    public SpiritOfMalevolence copy() {
+        return new SpiritOfMalevolence(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index ff49782f46..8e519520c4 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -51,6 +51,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Rain of Revelation", 61, Rarity.UNCOMMON, mage.cards.r.RainOfRevelation.class));
         cards.add(new SetCardInfo("Runed Halo", 32, Rarity.RARE, mage.cards.r.RunedHalo.class));
         cards.add(new SetCardInfo("Shipwreck Dowser", 71, Rarity.UNCOMMON, mage.cards.s.ShipwreckDowser.class));
+        cards.add(new SetCardInfo("Spirit of Malevolence", 331, Rarity.COMMON, mage.cards.s.SpiritOfMalevolence.class));
         cards.add(new SetCardInfo("Storm Caller", 335, Rarity.COMMON, mage.cards.s.StormCaller.class));
         cards.add(new SetCardInfo("Teferi's Protege", 77, Rarity.COMMON, mage.cards.t.TeferisProtege.class));
         cards.add(new SetCardInfo("Tormod's Crypt", 241, Rarity.UNCOMMON, mage.cards.t.TormodsCrypt.class));

From 154a62060414cca207703a2aa1cd123c8a51a480 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sat, 6 Jun 2020 11:21:40 -0400
Subject: [PATCH 118/586] Implemented Vito, Thorn of the Dusk Rose

---
 .../mage/cards/v/VitoThornOfTheDuskRose.java  | 88 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 89 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/v/VitoThornOfTheDuskRose.java

diff --git a/Mage.Sets/src/mage/cards/v/VitoThornOfTheDuskRose.java b/Mage.Sets/src/mage/cards/v/VitoThornOfTheDuskRose.java
new file mode 100644
index 0000000000..244d8a96fd
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/v/VitoThornOfTheDuskRose.java
@@ -0,0 +1,88 @@
+package mage.cards.v;
+
+import mage.MageInt;
+import mage.abilities.TriggeredAbilityImpl;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.effects.common.LoseLifeTargetEffect;
+import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
+import mage.abilities.keyword.LifelinkAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.*;
+import mage.filter.StaticFilters;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+import mage.target.common.TargetOpponent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class VitoThornOfTheDuskRose extends CardImpl {
+
+    public VitoThornOfTheDuskRose(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}");
+
+        this.addSuperType(SuperType.LEGENDARY);
+        this.subtype.add(SubType.VAMPIRE);
+        this.subtype.add(SubType.CLERIC);
+        this.power = new MageInt(1);
+        this.toughness = new MageInt(3);
+
+        // Whenever you gain life, target opponent loses that much life.
+        this.addAbility(new VitoThornOfTheDuskRoseTriggeredAbility());
+
+        // {3}{B}{B}: Creatures you control gain lifelink until end of turn.
+        this.addAbility(new SimpleActivatedAbility(new GainAbilityControlledEffect(
+                LifelinkAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURES
+        ), new ManaCostsImpl("{3}{B}{B}")));
+    }
+
+    private VitoThornOfTheDuskRose(final VitoThornOfTheDuskRose card) {
+        super(card);
+    }
+
+    @Override
+    public VitoThornOfTheDuskRose copy() {
+        return new VitoThornOfTheDuskRose(this);
+    }
+}
+
+class VitoThornOfTheDuskRoseTriggeredAbility extends TriggeredAbilityImpl {
+
+    VitoThornOfTheDuskRoseTriggeredAbility() {
+        super(Zone.BATTLEFIELD, null);
+        this.addTarget(new TargetOpponent());
+    }
+
+    private VitoThornOfTheDuskRoseTriggeredAbility(final VitoThornOfTheDuskRoseTriggeredAbility ability) {
+        super(ability);
+    }
+
+    @Override
+    public VitoThornOfTheDuskRoseTriggeredAbility copy() {
+        return new VitoThornOfTheDuskRoseTriggeredAbility(this);
+    }
+
+    @Override
+    public boolean checkEventType(GameEvent event, Game game) {
+        return event.getType() == GameEvent.EventType.GAINED_LIFE;
+    }
+
+    @Override
+    public boolean checkTrigger(GameEvent event, Game game) {
+        if (event.getPlayerId().equals(this.controllerId)) {
+            this.getEffects().clear();
+            this.addEffect(new LoseLifeTargetEffect(event.getAmount()));
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public String getRule() {
+        return "Whenever you gain life, target opponent loses that much life.";
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 8e519520c4..73681a7b69 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -56,6 +56,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Teferi's Protege", 77, Rarity.COMMON, mage.cards.t.TeferisProtege.class));
         cards.add(new SetCardInfo("Tormod's Crypt", 241, Rarity.UNCOMMON, mage.cards.t.TormodsCrypt.class));
         cards.add(new SetCardInfo("Ugin, the Spirit Dragon", 1, Rarity.MYTHIC, mage.cards.u.UginTheSpiritDragon.class));
+        cards.add(new SetCardInfo("Vito, Thorn of the Dusk Rose", 127, Rarity.RARE, mage.cards.v.VitoThornOfTheDuskRose.class));
         cards.add(new SetCardInfo("Wildwood Patrol", 339, Rarity.COMMON, mage.cards.w.WildwoodPatrol.class));
     }
 }

From 55fc950a3f0d37e6aa5d653e12f4b1ae49cbc9f3 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sat, 6 Jun 2020 15:07:40 -0400
Subject: [PATCH 119/586] Implemented Peer into the Abyss

---
 .../src/mage/cards/p/PeerIntoTheAbyss.java    | 65 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 66 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/p/PeerIntoTheAbyss.java

diff --git a/Mage.Sets/src/mage/cards/p/PeerIntoTheAbyss.java b/Mage.Sets/src/mage/cards/p/PeerIntoTheAbyss.java
new file mode 100644
index 0000000000..7f5e8372ea
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/p/PeerIntoTheAbyss.java
@@ -0,0 +1,65 @@
+package mage.cards.p;
+
+import mage.abilities.Ability;
+import mage.abilities.effects.OneShotEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Outcome;
+import mage.game.Game;
+import mage.players.Player;
+import mage.target.TargetPlayer;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class PeerIntoTheAbyss extends CardImpl {
+
+    public PeerIntoTheAbyss(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{B}{B}{B}");
+
+        // Target player draws cards equal to half the number of cards in their library and loses half their life. Round up each time.
+        this.getSpellAbility().addEffect(new PeerIntoTheAbyssEffect());
+        this.getSpellAbility().addTarget(new TargetPlayer());
+    }
+
+    private PeerIntoTheAbyss(final PeerIntoTheAbyss card) {
+        super(card);
+    }
+
+    @Override
+    public PeerIntoTheAbyss copy() {
+        return new PeerIntoTheAbyss(this);
+    }
+}
+
+class PeerIntoTheAbyssEffect extends OneShotEffect {
+
+    PeerIntoTheAbyssEffect() {
+        super(Outcome.Benefit);
+        staticText = "Target player draws cards equal to half the number of cards in their library " +
+                "and loses half their life. Round up each time.";
+    }
+
+    private PeerIntoTheAbyssEffect(final PeerIntoTheAbyssEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public PeerIntoTheAbyssEffect copy() {
+        return new PeerIntoTheAbyssEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Player player = game.getPlayer(source.getControllerId());
+        if (player == null) {
+            return false;
+        }
+        player.drawCards((int) Math.ceil(player.getLibrary().size() / 2), source.getSourceId(), game);
+        player.loseLife((int) Math.ceil(player.getLife() / 2), game, false);
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 73681a7b69..9340844cb5 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -47,6 +47,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Mangara, the Diplomat", 27, Rarity.MYTHIC, mage.cards.m.MangaraTheDiplomat.class));
         cards.add(new SetCardInfo("Mystic Skyfish", 326, Rarity.COMMON, mage.cards.m.MysticSkyfish.class));
         cards.add(new SetCardInfo("Pack Leader", 29, Rarity.RARE, mage.cards.p.PackLeader.class));
+        cards.add(new SetCardInfo("Peer into the Abyss", 117, Rarity.RARE, mage.cards.p.PeerIntoTheAbyss.class));
         cards.add(new SetCardInfo("Quirion Dryad", 198, Rarity.UNCOMMON, mage.cards.q.QuirionDryad.class));
         cards.add(new SetCardInfo("Rain of Revelation", 61, Rarity.UNCOMMON, mage.cards.r.RainOfRevelation.class));
         cards.add(new SetCardInfo("Runed Halo", 32, Rarity.RARE, mage.cards.r.RunedHalo.class));

From 54c23005b4892886a3af3af097e7ab1aef25498c Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sat, 6 Jun 2020 15:10:43 -0400
Subject: [PATCH 120/586] Implemented Sigiled Contender

---
 .../src/mage/cards/s/SigiledContender.java    | 50 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 51 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/s/SigiledContender.java

diff --git a/Mage.Sets/src/mage/cards/s/SigiledContender.java b/Mage.Sets/src/mage/cards/s/SigiledContender.java
new file mode 100644
index 0000000000..9e89d6bf83
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/SigiledContender.java
@@ -0,0 +1,50 @@
+package mage.cards.s;
+
+import mage.MageInt;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.condition.Condition;
+import mage.abilities.condition.common.SourceHasCounterCondition;
+import mage.abilities.decorator.ConditionalContinuousEffect;
+import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
+import mage.abilities.keyword.LifelinkAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+import mage.counters.CounterType;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class SigiledContender extends CardImpl {
+
+    private static final Condition condition = new SourceHasCounterCondition(CounterType.P1P1);
+
+    public SigiledContender(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}");
+
+        this.subtype.add(SubType.HUMAN);
+        this.subtype.add(SubType.WARRIOR);
+        this.power = new MageInt(3);
+        this.toughness = new MageInt(3);
+
+        // Sigiled Contender has lifelink as long as it has a +1/+1 counter on it.
+        this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect(
+                new GainAbilitySourceEffect(
+                        LifelinkAbility.getInstance(), Duration.WhileOnBattlefield
+                ), condition, "{this} has lifelink as long as it has a +1/+1 counter on it"
+        )));
+    }
+
+    private SigiledContender(final SigiledContender card) {
+        super(card);
+    }
+
+    @Override
+    public SigiledContender copy() {
+        return new SigiledContender(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 9340844cb5..d0cb77d645 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -52,6 +52,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Rain of Revelation", 61, Rarity.UNCOMMON, mage.cards.r.RainOfRevelation.class));
         cards.add(new SetCardInfo("Runed Halo", 32, Rarity.RARE, mage.cards.r.RunedHalo.class));
         cards.add(new SetCardInfo("Shipwreck Dowser", 71, Rarity.UNCOMMON, mage.cards.s.ShipwreckDowser.class));
+        cards.add(new SetCardInfo("Sigiled Contender", 323, Rarity.UNCOMMON, mage.cards.s.SigiledContender.class));
         cards.add(new SetCardInfo("Spirit of Malevolence", 331, Rarity.COMMON, mage.cards.s.SpiritOfMalevolence.class));
         cards.add(new SetCardInfo("Storm Caller", 335, Rarity.COMMON, mage.cards.s.StormCaller.class));
         cards.add(new SetCardInfo("Teferi's Protege", 77, Rarity.COMMON, mage.cards.t.TeferisProtege.class));

From 65c10a9cf45a4866674f6c2d7030207ca865e23d Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sat, 6 Jun 2020 16:03:02 -0400
Subject: [PATCH 121/586] Implemented Teferi, Master of Time

---
 .../src/mage/cards/t/TeferiMasterOfTime.java  | 115 ++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |   1 +
 2 files changed, 116 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/t/TeferiMasterOfTime.java

diff --git a/Mage.Sets/src/mage/cards/t/TeferiMasterOfTime.java b/Mage.Sets/src/mage/cards/t/TeferiMasterOfTime.java
new file mode 100644
index 0000000000..3ba2279cb2
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/t/TeferiMasterOfTime.java
@@ -0,0 +1,115 @@
+package mage.cards.t;
+
+import mage.abilities.Ability;
+import mage.abilities.LoyaltyAbility;
+import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.effects.AsThoughEffectImpl;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.DrawDiscardControllerEffect;
+import mage.abilities.effects.common.PhaseOutTargetEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.*;
+import mage.filter.StaticFilters;
+import mage.game.Game;
+import mage.game.turn.TurnMod;
+import mage.target.TargetPermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class TeferiMasterOfTime extends CardImpl {
+
+    public TeferiMasterOfTime(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{2}{U}{U}");
+
+        this.addSuperType(SuperType.LEGENDARY);
+        this.subtype.add(SubType.TEFERI);
+        this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3));
+
+        // You may activate loyalty abilities of Teferi, Master of Time on any player's turn any time you could cast an instant.
+        this.addAbility(new SimpleStaticAbility(new TeferiMasterOfTimeActivationEffect()));
+
+        // +1: Draw a card, then discard a card.
+        this.addAbility(new LoyaltyAbility(new DrawDiscardControllerEffect(1, 1), 1));
+
+        // −3: Target creature you don't control phases out.
+        Ability ability = new LoyaltyAbility(new PhaseOutTargetEffect(), -3);
+        ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL));
+        this.addAbility(ability);
+
+        // −10: Take two extra turns after this one.
+        this.addAbility(new LoyaltyAbility(new TeferiMasterOfTimeTurnEffect(), -10));
+    }
+
+    private TeferiMasterOfTime(final TeferiMasterOfTime card) {
+        super(card);
+    }
+
+    @Override
+    public TeferiMasterOfTime copy() {
+        return new TeferiMasterOfTime(this);
+    }
+}
+
+class TeferiMasterOfTimeActivationEffect extends AsThoughEffectImpl {
+
+    TeferiMasterOfTimeActivationEffect() {
+        super(AsThoughEffectType.ACTIVATE_AS_INSTANT, Duration.EndOfGame, Outcome.Benefit);
+        staticText = "You may activate loyalty abilities of {this} " +
+                "on any player's turn any time you could cast an instant";
+    }
+
+    private TeferiMasterOfTimeActivationEffect(final TeferiMasterOfTimeActivationEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        return true;
+    }
+
+    @Override
+    public TeferiMasterOfTimeActivationEffect copy() {
+        return new TeferiMasterOfTimeActivationEffect(this);
+    }
+
+    @Override
+    public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) {
+        return affectedAbility.isControlledBy(source.getControllerId())
+                && affectedAbility.getSourceId().equals(source.getSourceId())
+                && affectedAbility instanceof LoyaltyAbility;
+    }
+
+    @Override
+    public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
+        return false;
+    }
+}
+
+class TeferiMasterOfTimeTurnEffect extends OneShotEffect {
+
+    TeferiMasterOfTimeTurnEffect() {
+        super(Outcome.ExtraTurn);
+        staticText = "take two extra turns after this one";
+    }
+
+    private TeferiMasterOfTimeTurnEffect(final TeferiMasterOfTimeTurnEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public TeferiMasterOfTimeTurnEffect copy() {
+        return new TeferiMasterOfTimeTurnEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        game.getState().getTurnMods().add(new TurnMod(source.getControllerId(), false));
+        game.getState().getTurnMods().add(new TurnMod(source.getControllerId(), false));
+        return true;
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index d0cb77d645..4fb8189e24 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -56,6 +56,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Spirit of Malevolence", 331, Rarity.COMMON, mage.cards.s.SpiritOfMalevolence.class));
         cards.add(new SetCardInfo("Storm Caller", 335, Rarity.COMMON, mage.cards.s.StormCaller.class));
         cards.add(new SetCardInfo("Teferi's Protege", 77, Rarity.COMMON, mage.cards.t.TeferisProtege.class));
+        cards.add(new SetCardInfo("Teferi, Master of Time", 75, Rarity.MYTHIC, mage.cards.t.TeferiMasterOfTime.class));
         cards.add(new SetCardInfo("Tormod's Crypt", 241, Rarity.UNCOMMON, mage.cards.t.TormodsCrypt.class));
         cards.add(new SetCardInfo("Ugin, the Spirit Dragon", 1, Rarity.MYTHIC, mage.cards.u.UginTheSpiritDragon.class));
         cards.add(new SetCardInfo("Vito, Thorn of the Dusk Rose", 127, Rarity.RARE, mage.cards.v.VitoThornOfTheDuskRose.class));

From 2c4ffeb5abbe4b5c08d1444c19355e35e55051e0 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sat, 6 Jun 2020 16:15:35 -0400
Subject: [PATCH 122/586] Implemented Sparkhunter Masticore

---
 .../mage/cards/s/SparkhunterMasticore.java    | 65 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 66 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/s/SparkhunterMasticore.java

diff --git a/Mage.Sets/src/mage/cards/s/SparkhunterMasticore.java b/Mage.Sets/src/mage/cards/s/SparkhunterMasticore.java
new file mode 100644
index 0000000000..46afd30c0e
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/SparkhunterMasticore.java
@@ -0,0 +1,65 @@
+package mage.cards.s;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.common.DiscardCardCost;
+import mage.abilities.costs.mana.GenericManaCost;
+import mage.abilities.effects.common.DamageTargetEffect;
+import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
+import mage.abilities.keyword.IndestructibleAbility;
+import mage.abilities.keyword.ProtectionAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+import mage.filter.FilterObject;
+import mage.target.common.TargetPlaneswalkerPermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class SparkhunterMasticore extends CardImpl {
+
+    private static final FilterObject filter = new FilterObject("planeswalkers");
+
+    static {
+        filter.add(CardType.PLANESWALKER.getPredicate());
+    }
+
+    public SparkhunterMasticore(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}");
+
+        this.subtype.add(SubType.MASTICORE);
+        this.power = new MageInt(3);
+        this.toughness = new MageInt(4);
+
+        // As an additional cost to cast this spell, discard a card.
+        this.getSpellAbility().addCost(new DiscardCardCost());
+
+        // Protection from planeswalkers
+        this.addAbility(new ProtectionAbility(filter));
+
+        // {1}: Sparkhunter Masticore deals 1 damage to target planeswalker.
+        Ability ability = new SimpleActivatedAbility(new DamageTargetEffect(1), new GenericManaCost(1));
+        ability.addTarget(new TargetPlaneswalkerPermanent());
+        this.addAbility(ability);
+
+        // {3}: Sparkhunter Masticore gains indestructible until end of turn.
+        this.addAbility(new SimpleActivatedAbility(new GainAbilitySourceEffect(
+                IndestructibleAbility.getInstance(), Duration.EndOfTurn
+        ), new GenericManaCost(3)));
+    }
+
+    private SparkhunterMasticore(final SparkhunterMasticore card) {
+        super(card);
+    }
+
+    @Override
+    public SparkhunterMasticore copy() {
+        return new SparkhunterMasticore(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 4fb8189e24..97181b19f4 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -53,6 +53,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Runed Halo", 32, Rarity.RARE, mage.cards.r.RunedHalo.class));
         cards.add(new SetCardInfo("Shipwreck Dowser", 71, Rarity.UNCOMMON, mage.cards.s.ShipwreckDowser.class));
         cards.add(new SetCardInfo("Sigiled Contender", 323, Rarity.UNCOMMON, mage.cards.s.SigiledContender.class));
+        cards.add(new SetCardInfo("Sparkhunter Masticore", 240, Rarity.RARE, mage.cards.s.SparkhunterMasticore.class));
         cards.add(new SetCardInfo("Spirit of Malevolence", 331, Rarity.COMMON, mage.cards.s.SpiritOfMalevolence.class));
         cards.add(new SetCardInfo("Storm Caller", 335, Rarity.COMMON, mage.cards.s.StormCaller.class));
         cards.add(new SetCardInfo("Teferi's Protege", 77, Rarity.COMMON, mage.cards.t.TeferisProtege.class));

From 218854becd662e9f999bab08bbf79e6bf7adebab Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sat, 6 Jun 2020 16:41:23 -0400
Subject: [PATCH 123/586] Implemented Chandra's Firemaw

---
 .../src/mage/cards/c/ChandrasFiremaw.java     | 49 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 50 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/c/ChandrasFiremaw.java

diff --git a/Mage.Sets/src/mage/cards/c/ChandrasFiremaw.java b/Mage.Sets/src/mage/cards/c/ChandrasFiremaw.java
new file mode 100644
index 0000000000..368e39f919
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/c/ChandrasFiremaw.java
@@ -0,0 +1,49 @@
+package mage.cards.c;
+
+import mage.MageInt;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.effects.common.search.SearchLibraryGraveyardPutInHandEffect;
+import mage.abilities.keyword.HasteAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.filter.FilterCard;
+import mage.filter.predicate.mageobject.NamePredicate;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class ChandrasFiremaw extends CardImpl {
+
+    private static final FilterCard filter = new FilterCard("Chandra, Flame's Catalyst");
+
+    static {
+        filter.add(new NamePredicate("Chandra, Flame's Catalyst"));
+    }
+
+    public ChandrasFiremaw(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{R}");
+
+        this.subtype.add(SubType.HELLION);
+        this.power = new MageInt(4);
+        this.toughness = new MageInt(2);
+
+        // Haste
+        this.addAbility(HasteAbility.getInstance());
+
+        // When Chandra's Firemaw enters the battlefield, you may search your library and/or graveyard for a card named Chandra, Flame's Catalyst, reveal it, and put it into your hand. If you search your library this way, shuffle it.
+        this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryGraveyardPutInHandEffect(filter)));
+    }
+
+    private ChandrasFiremaw(final ChandrasFiremaw card) {
+        super(card);
+    }
+
+    @Override
+    public ChandrasFiremaw copy() {
+        return new ChandrasFiremaw(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 97181b19f4..dd90d51e1a 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -36,6 +36,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Azusa, Lost but Seeking", 173, Rarity.RARE, mage.cards.a.AzusaLostButSeeking.class));
         cards.add(new SetCardInfo("Bad Deal", 89, Rarity.UNCOMMON, mage.cards.b.BadDeal.class));
         cards.add(new SetCardInfo("Baneslayer Angel", 4, Rarity.MYTHIC, mage.cards.b.BaneslayerAngel.class));
+        cards.add(new SetCardInfo("Chandra's Firemaw", 333, Rarity.RARE, mage.cards.c.ChandrasFiremaw.class));
         cards.add(new SetCardInfo("Containment Priest", 314, Rarity.RARE, mage.cards.c.ContainmentPriest.class));
         cards.add(new SetCardInfo("Double Vision", 142, Rarity.RARE, mage.cards.d.DoubleVision.class));
         cards.add(new SetCardInfo("Fierce Empath", 181, Rarity.UNCOMMON, mage.cards.f.FierceEmpath.class));

From 25f8212aebc588bfb211728a8e9896e66d0cae24 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sat, 6 Jun 2020 16:48:10 -0400
Subject: [PATCH 124/586] Implemented Indulging Patrician

---
 .../src/mage/cards/i/IndulgingPatrician.java  | 67 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 68 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/i/IndulgingPatrician.java

diff --git a/Mage.Sets/src/mage/cards/i/IndulgingPatrician.java b/Mage.Sets/src/mage/cards/i/IndulgingPatrician.java
new file mode 100644
index 0000000000..8c969db225
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/i/IndulgingPatrician.java
@@ -0,0 +1,67 @@
+package mage.cards.i;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.BeginningOfEndStepTriggeredAbility;
+import mage.abilities.condition.Condition;
+import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
+import mage.abilities.effects.common.LoseLifeOpponentsEffect;
+import mage.abilities.keyword.FlyingAbility;
+import mage.abilities.keyword.LifelinkAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.TargetController;
+import mage.game.Game;
+import mage.watchers.common.PlayerGainedLifeWatcher;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class IndulgingPatrician extends CardImpl {
+
+    public IndulgingPatrician(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{B}");
+
+        this.subtype.add(SubType.VAMPIRE);
+        this.subtype.add(SubType.NOBLE);
+        this.power = new MageInt(1);
+        this.toughness = new MageInt(4);
+
+        // Flying
+        this.addAbility(FlyingAbility.getInstance());
+
+        // Lifelink
+        this.addAbility(LifelinkAbility.getInstance());
+
+        // At the beginning of your end step, if you gained 3 or more life this turn, each opponent loses 3 life.
+        this.addAbility(new ConditionalInterveningIfTriggeredAbility(
+                new BeginningOfEndStepTriggeredAbility(
+                        new LoseLifeOpponentsEffect(3), TargetController.YOU, false
+                ), IndulgingPatricianCondition.instance, "At the beginning of your end step, " +
+                "if you gained 3 or more life this turn, each opponent loses 3 life."
+        ), new PlayerGainedLifeWatcher());
+    }
+
+    private IndulgingPatrician(final IndulgingPatrician card) {
+        super(card);
+    }
+
+    @Override
+    public IndulgingPatrician copy() {
+        return new IndulgingPatrician(this);
+    }
+}
+
+enum IndulgingPatricianCondition implements Condition {
+    instance;
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        PlayerGainedLifeWatcher watcher = game.getState().getWatcher(PlayerGainedLifeWatcher.class);
+        return watcher != null && watcher.getLifeGained(source.getControllerId()) >= 3;
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index dd90d51e1a..3f21057e34 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -42,6 +42,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Fierce Empath", 181, Rarity.UNCOMMON, mage.cards.f.FierceEmpath.class));
         cards.add(new SetCardInfo("Gadrak, the Crown-Scourge", 146, Rarity.RARE, mage.cards.g.GadrakTheCrownScourge.class));
         cards.add(new SetCardInfo("Grim Tutor", 103, Rarity.MYTHIC, mage.cards.g.GrimTutor.class));
+        cards.add(new SetCardInfo("Indulging Patrician", 219, Rarity.UNCOMMON, mage.cards.i.IndulgingPatrician.class));
         cards.add(new SetCardInfo("Island", 310, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Jeskai Elder", 53, Rarity.UNCOMMON, mage.cards.j.JeskaiElder.class));
         cards.add(new SetCardInfo("Llanowar Visionary", 193, Rarity.COMMON, mage.cards.l.LlanowarVisionary.class));

From bb3e58994b2bc013fcc37e538e428ab46e48fc0d Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sat, 6 Jun 2020 16:56:00 -0400
Subject: [PATCH 125/586] Implemented Historian of Zhalfir

---
 .../src/mage/cards/h/HistorianOfZhalfir.java  | 49 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 50 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/h/HistorianOfZhalfir.java

diff --git a/Mage.Sets/src/mage/cards/h/HistorianOfZhalfir.java b/Mage.Sets/src/mage/cards/h/HistorianOfZhalfir.java
new file mode 100644
index 0000000000..4bbe30d4f3
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/h/HistorianOfZhalfir.java
@@ -0,0 +1,49 @@
+package mage.cards.h;
+
+import mage.MageInt;
+import mage.abilities.common.AttacksTriggeredAbility;
+import mage.abilities.condition.Condition;
+import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
+import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
+import mage.abilities.effects.common.DrawCardSourceControllerEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.filter.FilterPermanent;
+import mage.filter.common.FilterControlledPlaneswalkerPermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class HistorianOfZhalfir extends CardImpl {
+
+    private static final FilterPermanent filter = new FilterControlledPlaneswalkerPermanent(SubType.TEFERI);
+    private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter);
+
+    public HistorianOfZhalfir(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}{U}");
+
+        this.subtype.add(SubType.HUMAN);
+        this.subtype.add(SubType.WIZARD);
+        this.power = new MageInt(3);
+        this.toughness = new MageInt(3);
+
+        // Whenever Historian of Zhalfir attacks, if you control a Teferi planeswalker, draw a card.
+        this.addAbility(new ConditionalInterveningIfTriggeredAbility(
+                new AttacksTriggeredAbility(new DrawCardSourceControllerEffect(1), false),
+                condition, "Whenever {this} attacks, if you control a Teferi planeswalker, draw a card")
+        );
+    }
+
+    private HistorianOfZhalfir(final HistorianOfZhalfir card) {
+        super(card);
+    }
+
+    @Override
+    public HistorianOfZhalfir copy() {
+        return new HistorianOfZhalfir(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 3f21057e34..1439cc247f 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -42,6 +42,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Fierce Empath", 181, Rarity.UNCOMMON, mage.cards.f.FierceEmpath.class));
         cards.add(new SetCardInfo("Gadrak, the Crown-Scourge", 146, Rarity.RARE, mage.cards.g.GadrakTheCrownScourge.class));
         cards.add(new SetCardInfo("Grim Tutor", 103, Rarity.MYTHIC, mage.cards.g.GrimTutor.class));
+        cards.add(new SetCardInfo("Historian of Zhalfir", 325, Rarity.UNCOMMON, mage.cards.h.HistorianOfZhalfir.class));
         cards.add(new SetCardInfo("Indulging Patrician", 219, Rarity.UNCOMMON, mage.cards.i.IndulgingPatrician.class));
         cards.add(new SetCardInfo("Island", 310, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Jeskai Elder", 53, Rarity.UNCOMMON, mage.cards.j.JeskaiElder.class));

From d1da9ad8432a9e615dfeca6c7a093e3b4a0ab29a Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sat, 6 Jun 2020 17:18:40 -0400
Subject: [PATCH 126/586] Implemented Liliana's Scrounger

---
 .../src/mage/cards/l/LilianasScrounger.java   | 90 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 91 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/l/LilianasScrounger.java

diff --git a/Mage.Sets/src/mage/cards/l/LilianasScrounger.java b/Mage.Sets/src/mage/cards/l/LilianasScrounger.java
new file mode 100644
index 0000000000..0553782b08
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/l/LilianasScrounger.java
@@ -0,0 +1,90 @@
+package mage.cards.l;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.BeginningOfEndStepTriggeredAbility;
+import mage.abilities.condition.common.MorbidCondition;
+import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
+import mage.abilities.effects.OneShotEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Outcome;
+import mage.constants.SubType;
+import mage.constants.TargetController;
+import mage.counters.CounterType;
+import mage.filter.FilterPermanent;
+import mage.filter.common.FilterControlledPlaneswalkerPermanent;
+import mage.game.Game;
+import mage.game.permanent.Permanent;
+import mage.players.Player;
+import mage.target.TargetPermanent;
+import mage.watchers.common.MorbidWatcher;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class LilianasScrounger extends CardImpl {
+
+    public LilianasScrounger(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}");
+
+        this.subtype.add(SubType.HUMAN);
+        this.subtype.add(SubType.WIZARD);
+        this.power = new MageInt(3);
+        this.toughness = new MageInt(2);
+
+        // At the beginning of each end step, if a creature died this turn, you may put a loyalty counter on a Liliana planeswalker you control.
+        this.addAbility(new ConditionalInterveningIfTriggeredAbility(new BeginningOfEndStepTriggeredAbility(
+                new LilianasScroungerEffect(), TargetController.ANY, false
+        ), MorbidCondition.instance, "At the beginning of each end step, " +
+                "if a creature died this turn, you may put a loyalty counter on a Liliana planeswalker you control."
+        ), new MorbidWatcher());
+    }
+
+    private LilianasScrounger(final LilianasScrounger card) {
+        super(card);
+    }
+
+    @Override
+    public LilianasScrounger copy() {
+        return new LilianasScrounger(this);
+    }
+}
+
+class LilianasScroungerEffect extends OneShotEffect {
+
+    private static final FilterPermanent filter
+            = new FilterControlledPlaneswalkerPermanent(SubType.LILIANA, "Liliana planeswalker you control");
+
+    LilianasScroungerEffect() {
+        super(Outcome.Benefit);
+    }
+
+    private LilianasScroungerEffect(final LilianasScroungerEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public LilianasScroungerEffect copy() {
+        return new LilianasScroungerEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Player player = game.getPlayer(source.getControllerId());
+        if (player == null || game.getBattlefield().count(
+                filter, source.getSourceId(), source.getControllerId(), game
+        ) < 1 || !player.chooseUse(
+                outcome, "Put a loyalty counter on a Liliana planeswalker you control?", source, game
+        )) {
+            return false;
+        }
+        TargetPermanent target = new TargetPermanent(0, 1, filter, true);
+        player.choose(outcome, target, source.getSourceId(), game);
+        Permanent permanent = game.getPermanent(target.getFirstTarget());
+        return permanent != null && permanent.addCounters(CounterType.LOYALTY.createInstance(), source, game);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 1439cc247f..974055a407 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -46,6 +46,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Indulging Patrician", 219, Rarity.UNCOMMON, mage.cards.i.IndulgingPatrician.class));
         cards.add(new SetCardInfo("Island", 310, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Jeskai Elder", 53, Rarity.UNCOMMON, mage.cards.j.JeskaiElder.class));
+        cards.add(new SetCardInfo("Liliana's Scrounger", 330, Rarity.UNCOMMON, mage.cards.l.LilianasScrounger.class));
         cards.add(new SetCardInfo("Llanowar Visionary", 193, Rarity.COMMON, mage.cards.l.LlanowarVisionary.class));
         cards.add(new SetCardInfo("Mangara, the Diplomat", 27, Rarity.MYTHIC, mage.cards.m.MangaraTheDiplomat.class));
         cards.add(new SetCardInfo("Mystic Skyfish", 326, Rarity.COMMON, mage.cards.m.MysticSkyfish.class));

From a897df7c79ad47a2f2a7d4ee5728d7c67c0ca3f6 Mon Sep 17 00:00:00 2001
From: htrajan <htrajan2002@yahoo.com>
Date: Sat, 6 Jun 2020 19:13:20 -0700
Subject: [PATCH 127/586] Implement 6 cmc pws (except Teferi) and rin (#6611)

[M21] Implement 6 cmc pws (except Teferi) and rin
---
 .../src/main/resources/card-pictures-tok.txt  |   4 +-
 .../src/mage/cards/b/BasriDevotedPaladin.java | 113 +++++++++++++++++
 .../mage/cards/c/ChandraFlamesCatalyst.java   |  75 +++++++++++
 .../src/mage/cards/g/GarrukSavageHerald.java  | 107 ++++++++++++++++
 .../src/mage/cards/j/JaceTelepathUnbound.java | 120 +-----------------
 .../src/mage/cards/l/LilianaDeathMage.java    | 117 +++++++++++++++++
 Mage.Sets/src/mage/cards/m/MongrelPack.java   |   4 +-
 .../mage/cards/r/RinAndSeriInseparable.java   |  91 +++++++++++++
 .../src/mage/cards/w/WaitingInTheWeeds.java   |   4 +-
 Mage.Sets/src/mage/sets/CoreSet2021.java      |   5 +
 ...astCardFromGraveyardThenExileItEffect.java | 115 +++++++++++++++++
 ...stFromHandWithoutPayingManaCostEffect.java |   8 +-
 .../src/main/java/mage/constants/SubType.java |   1 +
 ...eWeedsCatToken.java => GreenCatToken.java} |  10 +-
 .../{DogToken.java => GreenDogToken.java}     |  10 +-
 .../game/permanent/token/WhiteDogToken.java   |  31 +++++
 16 files changed, 681 insertions(+), 134 deletions(-)
 create mode 100644 Mage.Sets/src/mage/cards/b/BasriDevotedPaladin.java
 create mode 100644 Mage.Sets/src/mage/cards/c/ChandraFlamesCatalyst.java
 create mode 100644 Mage.Sets/src/mage/cards/g/GarrukSavageHerald.java
 create mode 100644 Mage.Sets/src/mage/cards/l/LilianaDeathMage.java
 create mode 100644 Mage.Sets/src/mage/cards/r/RinAndSeriInseparable.java
 create mode 100644 Mage/src/main/java/mage/abilities/effects/CastCardFromGraveyardThenExileItEffect.java
 rename Mage/src/main/java/mage/game/permanent/token/{WaitingInTheWeedsCatToken.java => GreenCatToken.java} (60%)
 rename Mage/src/main/java/mage/game/permanent/token/{DogToken.java => GreenDogToken.java} (66%)
 create mode 100644 Mage/src/main/java/mage/game/permanent/token/WhiteDogToken.java

diff --git a/Mage.Client/src/main/resources/card-pictures-tok.txt b/Mage.Client/src/main/resources/card-pictures-tok.txt
index 6c46c6c064..d3426689e9 100644
--- a/Mage.Client/src/main/resources/card-pictures-tok.txt
+++ b/Mage.Client/src/main/resources/card-pictures-tok.txt
@@ -154,7 +154,7 @@
 |Generate|TOK:5ED|Serf|||SerfToken|
 |Generate|TOK:5ED|Snake|||SerpentGeneratorSnakeToken|
 |Generate|TOK:5ED|Thrull|||BreedingPitBlackInsectToken|
-|Generate|TOK:6ED|Cat|||WaitingInTheWeedsCatToken|
+|Generate|TOK:6ED|Cat|||GreenCatToken|
 |Generate|TOK:6ED|Citizen|||CitizenToken|
 |Generate|TOK:6ED|Djinn|||DjinnToken|
 |Generate|TOK:6ED|Goblin|||GoblinToken|
@@ -1147,7 +1147,7 @@
 |Generate|TOK:THS|Soldier|1||SoldierToken|
 |Generate|TOK:THS|Soldier|2||SoldierToken|
 |Generate|TOK:TMP|Beast|||CarnivoreToken|
-|Generate|TOK:TMP|Hound|||HoundToken|
+|Generate|TOK:TMP|Dog|||GreenDogToken|
 |Generate|TOK:TMP|Pegasus|||PegasusToken|
 |Generate|TOK:TMP|Reflection|||ReflectionToken|
 |Generate|TOK:TMP|Saproling|||SaprolingToken|
diff --git a/Mage.Sets/src/mage/cards/b/BasriDevotedPaladin.java b/Mage.Sets/src/mage/cards/b/BasriDevotedPaladin.java
new file mode 100644
index 0000000000..c484ec5995
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/b/BasriDevotedPaladin.java
@@ -0,0 +1,113 @@
+package mage.cards.b;
+
+import mage.abilities.Ability;
+import mage.abilities.DelayedTriggeredAbility;
+import mage.abilities.LoyaltyAbility;
+import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
+import mage.abilities.effects.Effect;
+import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
+import mage.abilities.effects.common.continuous.BoostControlledEffect;
+import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
+import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
+import mage.abilities.effects.common.counter.AddCountersTargetEffect;
+import mage.abilities.keyword.FlyingAbility;
+import mage.abilities.keyword.VigilanceAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+import mage.constants.SuperType;
+import mage.counters.CounterType;
+import mage.filter.StaticFilters;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+import mage.game.permanent.Permanent;
+import mage.target.common.TargetCreaturePermanent;
+import mage.target.targetpointer.FixedTarget;
+
+import java.util.UUID;
+
+/**
+ *
+ * @author htrajan
+ */
+public final class BasriDevotedPaladin extends CardImpl {
+
+    public BasriDevotedPaladin(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{4}{W}{W}");
+        
+        this.addSuperType(SuperType.LEGENDARY);
+        this.subtype.add(SubType.BASRI);
+        this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4));
+
+        // +1: Put a +1/+1 counter on up to one target creature. It gains vigilance until end of turn.
+        Ability ability = new LoyaltyAbility(new AddCountersTargetEffect(
+                CounterType.P1P1.createInstance()
+        ).setText("Put a +1/+1 counter on up to one target creature"), 1);
+        ability.addEffect(new GainAbilityTargetEffect(
+                VigilanceAbility.getInstance(), Duration.EndOfTurn
+        ).setText("It gains vigilance until end of turn"));
+        ability.addTarget(new TargetCreaturePermanent(0, 1));
+        this.addAbility(ability);
+
+        // −1: Whenever a creature attacks this turn, put a +1/+1 counter on it.
+        this.addAbility(new LoyaltyAbility(new CreateDelayedTriggeredAbilityEffect(new BasriDevotedPaladinTriggeredAbility()), -1));
+
+        // −6: Creatures you control get +2/+2 and gain flying until end of turn.
+        Effect effect = new BoostControlledEffect(2, 2, Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURE);
+        effect.setText("Creatures you control get +2/+2");
+        LoyaltyAbility ultimateAbility = new LoyaltyAbility(effect, -6);
+        effect = new GainAbilityControlledEffect(FlyingAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURE);
+        effect.setText("and gain flying until end of turn");
+        ultimateAbility.addEffect(effect);
+        this.addAbility(ultimateAbility);
+    }
+
+    private BasriDevotedPaladin(final BasriDevotedPaladin card) {
+        super(card);
+    }
+
+    @Override
+    public BasriDevotedPaladin copy() {
+        return new BasriDevotedPaladin(this);
+    }
+}
+
+class BasriDevotedPaladinTriggeredAbility extends DelayedTriggeredAbility {
+
+    public BasriDevotedPaladinTriggeredAbility() {
+        super(new AddCountersTargetEffect(CounterType.P1P1.createInstance()), Duration.EndOfTurn, false);
+    }
+
+    public BasriDevotedPaladinTriggeredAbility(BasriDevotedPaladinTriggeredAbility ability) {
+        super(ability);
+    }
+
+    @Override
+    public BasriDevotedPaladinTriggeredAbility copy() {
+        return new BasriDevotedPaladinTriggeredAbility(this);
+    }
+
+    @Override
+    public boolean checkEventType(GameEvent event, Game game) {
+        return event.getType() == GameEvent.EventType.ATTACKER_DECLARED;
+    }
+
+    @Override
+    public boolean checkTrigger(GameEvent event, Game game) {
+        Permanent permanent = game.getPermanent(event.getSourceId());
+        if (permanent != null) {
+            for (Effect effect : getEffects()) {
+                effect.setTargetPointer(new FixedTarget(permanent, game));
+            }
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public String getRule() {
+        return "Whenever a creature attacks this turn, put a +1/+1 counter on it.";
+    }
+}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/c/ChandraFlamesCatalyst.java b/Mage.Sets/src/mage/cards/c/ChandraFlamesCatalyst.java
new file mode 100644
index 0000000000..9c78e245a3
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/c/ChandraFlamesCatalyst.java
@@ -0,0 +1,75 @@
+package mage.cards.c;
+
+import mage.ObjectColor;
+import mage.abilities.Ability;
+import mage.abilities.LoyaltyAbility;
+import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
+import mage.abilities.effects.CastCardFromGraveyardThenExileItEffect;
+import mage.abilities.effects.Effect;
+import mage.abilities.effects.common.DamagePlayersEffect;
+import mage.abilities.effects.common.DrawCardSourceControllerEffect;
+import mage.abilities.effects.common.continuous.CastFromHandWithoutPayingManaCostEffect;
+import mage.abilities.effects.common.discard.DiscardHandControllerEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.*;
+import mage.filter.FilterCard;
+import mage.filter.StaticFilters;
+import mage.filter.predicate.Predicates;
+import mage.filter.predicate.mageobject.ColorPredicate;
+import mage.target.common.TargetCardInYourGraveyard;
+
+import java.util.UUID;
+
+/**
+ *
+ * @author htrajan
+ */
+public final class ChandraFlamesCatalyst extends CardImpl {
+
+    private static final FilterCard filter = new FilterCard();
+
+    static {
+        filter.add(new ColorPredicate(ObjectColor.RED));
+        filter.add(Predicates.or(CardType.INSTANT.getPredicate(), CardType.SORCERY.getPredicate()));
+    }
+
+    public ChandraFlamesCatalyst(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{4}{R}{R}");
+        
+        this.addSuperType(SuperType.LEGENDARY);
+        this.subtype.add(SubType.CHANDRA);
+        this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5));
+
+        // +1: Chandra, Flame's Catalyst deals 3 damage to each opponent.
+        this.addAbility(new LoyaltyAbility(new DamagePlayersEffect(3, TargetController.OPPONENT), 1));
+
+        // −2: You may cast target red instant or sorcery card from your graveyard. If that spell would be put into your graveyard this turn, exile it instead.
+        CastCardFromGraveyardThenExileItEffect minusEffect = new CastCardFromGraveyardThenExileItEffect();
+        minusEffect.setText("You may cast target red instant or sorcery card from your graveyard. If that spell would be put into your graveyard this turn, exile it instead");
+        Ability ability = new LoyaltyAbility(minusEffect, -2);
+        ability.addTarget(new TargetCardInYourGraveyard(filter));
+        this.addAbility(ability);
+
+        // −8: Discard your hand, then draw seven cards. Until end of turn, you may cast spells from your hand without paying their mana costs.
+        Effect discardHandEffect = new DiscardHandControllerEffect();
+        Effect drawEffect = new DrawCardSourceControllerEffect(7);
+        drawEffect.setText(", then draw seven cards");
+        Effect castSpellsFromHandEffect = new CastFromHandWithoutPayingManaCostEffect(
+                StaticFilters.FILTER_CARD_NON_LAND, true, Duration.EndOfTurn);
+        castSpellsFromHandEffect.setText("Until end of turn, you may cast spells from your hand without paying their mana costs");
+        Ability ultimateAbility = new LoyaltyAbility(discardHandEffect, -8);
+        ultimateAbility.addEffect(drawEffect);
+        ultimateAbility.addEffect(castSpellsFromHandEffect);
+        this.addAbility(ultimateAbility);
+    }
+
+    private ChandraFlamesCatalyst(final ChandraFlamesCatalyst card) {
+        super(card);
+    }
+
+    @Override
+    public ChandraFlamesCatalyst copy() {
+        return new ChandraFlamesCatalyst(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/cards/g/GarrukSavageHerald.java b/Mage.Sets/src/mage/cards/g/GarrukSavageHerald.java
new file mode 100644
index 0000000000..614653fe34
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/g/GarrukSavageHerald.java
@@ -0,0 +1,107 @@
+package mage.cards.g;
+
+import mage.abilities.Ability;
+import mage.abilities.LoyaltyAbility;
+import mage.abilities.common.DamageAsThoughNotBlockedAbility;
+import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
+import mage.abilities.effects.ContinuousEffect;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.DamageWithPowerFromOneToAnotherTargetEffect;
+import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
+import mage.cards.Card;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.cards.CardsImpl;
+import mage.constants.*;
+import mage.filter.common.FilterCreaturePermanent;
+import mage.filter.predicate.mageobject.AnotherTargetPredicate;
+import mage.game.Game;
+import mage.players.Player;
+import mage.target.common.TargetControlledCreaturePermanent;
+import mage.target.common.TargetCreaturePermanent;
+
+import java.util.UUID;
+
+/**
+ *
+ * @author htrajan
+ */
+public final class GarrukSavageHerald extends CardImpl {
+
+    public GarrukSavageHerald(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{4}{G}{G}");
+        
+        this.addSuperType(SuperType.LEGENDARY);
+        this.subtype.add(SubType.GARRUK);
+        this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5));
+
+        // +1: Reveal the top card of your library. If it's a creature card, put it into your hand. Otherwise, put it on the bottom of your library.
+        this.addAbility(new LoyaltyAbility(new GarrukSavageHeraldEffect(), 1));
+
+        // −2: Target creature you control deals damage equal to its power to another target creature.
+        DamageWithPowerFromOneToAnotherTargetEffect effect = new DamageWithPowerFromOneToAnotherTargetEffect();
+        effect.setText("Target creature you control deals damage equal to its power to another target creature");
+
+        Ability minusAbility = new LoyaltyAbility(effect, -2);
+        TargetControlledCreaturePermanent controlledCreature = new TargetControlledCreaturePermanent();
+        controlledCreature.setTargetTag(1);
+        minusAbility.addTarget(controlledCreature);
+
+        FilterCreaturePermanent filter = new FilterCreaturePermanent("Another creature: damage dealt to");
+        filter.add(new AnotherTargetPredicate(2));
+        TargetCreaturePermanent anotherTargetCreature = new TargetCreaturePermanent(filter);
+        minusAbility.addTarget(anotherTargetCreature);
+
+        this.addAbility(minusAbility);
+
+        // −7: Until end of turn, creatures you control gain "You may have this creature assign its combat damage as though it weren't blocked."
+        ContinuousEffect ultimateEffect = new GainAbilityControlledEffect(DamageAsThoughNotBlockedAbility.getInstance(), Duration.EndOfTurn);
+        ultimateEffect.setText("Until end of turn, creatures you control gain \"You may have this creature assign its combat damage as though it weren't blocked.\"");
+        this.addAbility(new LoyaltyAbility(ultimateEffect, -7));
+    }
+
+    private GarrukSavageHerald(final GarrukSavageHerald card) {
+        super(card);
+    }
+
+    @Override
+    public GarrukSavageHerald copy() {
+        return new GarrukSavageHerald(this);
+    }
+}
+
+class GarrukSavageHeraldEffect extends OneShotEffect {
+
+    GarrukSavageHeraldEffect() {
+        super(Outcome.Benefit);
+        staticText = "reveal the top card of your library. If it's a creature card, " +
+                "put it into your hand. Otherwise, put it on the bottom of your library";
+    }
+
+    private GarrukSavageHeraldEffect(final GarrukSavageHeraldEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public GarrukSavageHeraldEffect copy() {
+        return new GarrukSavageHeraldEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Player player = game.getPlayer(source.getControllerId());
+        if (player == null) {
+            return false;
+        }
+        Card card = player.getLibrary().getFromTop(game);
+        if (card == null) {
+            return false;
+        }
+        player.revealCards(source, new CardsImpl(card), game);
+        if (card.isCreature()) {
+            return player.moveCards(card, Zone.HAND, source, game);
+        } else {
+            return player.putCardsOnBottomOfLibrary(card, game, source, false);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/j/JaceTelepathUnbound.java b/Mage.Sets/src/mage/cards/j/JaceTelepathUnbound.java
index e9504663f3..cb756e9150 100644
--- a/Mage.Sets/src/mage/cards/j/JaceTelepathUnbound.java
+++ b/Mage.Sets/src/mage/cards/j/JaceTelepathUnbound.java
@@ -5,32 +5,19 @@ import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.LoyaltyAbility;
 import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
-import mage.abilities.effects.AsThoughEffectImpl;
-import mage.abilities.effects.ContinuousEffect;
-import mage.abilities.effects.Effect;
-import mage.abilities.effects.OneShotEffect;
-import mage.abilities.effects.ReplacementEffectImpl;
+import mage.abilities.effects.*;
 import mage.abilities.effects.common.GetEmblemEffect;
 import mage.abilities.effects.common.continuous.BoostTargetEffect;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.AsThoughEffectType;
 import mage.constants.CardType;
 import mage.constants.SubType;
 import mage.constants.Duration;
-import mage.constants.Outcome;
 import mage.constants.SuperType;
-import mage.constants.Zone;
 import mage.filter.common.FilterInstantOrSorceryCard;
-import mage.game.Game;
 import mage.game.command.emblems.JaceTelepathUnboundEmblem;
-import mage.game.events.GameEvent;
-import mage.game.events.ZoneChangeEvent;
-import mage.players.Player;
 import mage.target.common.TargetCardInYourGraveyard;
 import mage.target.common.TargetCreaturePermanent;
-import mage.target.targetpointer.FixedTarget;
 
 /**
  *
@@ -57,7 +44,9 @@ public final class JaceTelepathUnbound extends CardImpl {
         this.addAbility(ability);
 
         // -3: You may cast target instant or sorcery card from your graveyard this turn. If that card would be put into your graveyard this turn, exile it instead.
-        ability = new LoyaltyAbility(new JaceTelepathUnboundEffect(), -3);
+        CastCardFromGraveyardThenExileItEffect minusEffect = new CastCardFromGraveyardThenExileItEffect();
+        minusEffect.setText("You may cast target instant or sorcery card from your graveyard this turn. If that card would be put into your graveyard this turn, exile it instead");
+        ability = new LoyaltyAbility(minusEffect, -3);
         ability.addTarget(new TargetCardInYourGraveyard(new FilterInstantOrSorceryCard()));
         this.addAbility(ability);
 
@@ -74,104 +63,3 @@ public final class JaceTelepathUnbound extends CardImpl {
         return new JaceTelepathUnbound(this);
     }
 }
-
-class JaceTelepathUnboundEffect extends OneShotEffect {
-
-    JaceTelepathUnboundEffect() {
-        super(Outcome.Benefit);
-        this.staticText = "You may cast target instant or sorcery card from your graveyard this turn. If that card would be put into your graveyard this turn, exile it instead";
-    }
-
-    JaceTelepathUnboundEffect(final JaceTelepathUnboundEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public JaceTelepathUnboundEffect copy() {
-        return new JaceTelepathUnboundEffect(this);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        Card card = game.getCard(this.getTargetPointer().getFirst(game, source));
-        if (card != null) {
-            ContinuousEffect effect = new JaceTelepathUnboundCastFromGraveyardEffect();
-            effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game)));
-            game.addEffect(effect, source);
-            effect = new JaceTelepathUnboundReplacementEffect(card.getId());
-            game.addEffect(effect, source);
-            return true;
-        }
-        return false;
-    }
-}
-
-class JaceTelepathUnboundCastFromGraveyardEffect extends AsThoughEffectImpl {
-
-    JaceTelepathUnboundCastFromGraveyardEffect() {
-        super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit);
-    }
-
-    JaceTelepathUnboundCastFromGraveyardEffect(final JaceTelepathUnboundCastFromGraveyardEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        return true;
-    }
-
-    @Override
-    public JaceTelepathUnboundCastFromGraveyardEffect copy() {
-        return new JaceTelepathUnboundCastFromGraveyardEffect(this);
-    }
-
-    @Override
-    public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
-        return objectId.equals(this.getTargetPointer().getFirst(game, source)) && affectedControllerId.equals(source.getControllerId());
-    }
-}
-
-class JaceTelepathUnboundReplacementEffect extends ReplacementEffectImpl {
-
-    private final UUID cardId;
-
-    JaceTelepathUnboundReplacementEffect(UUID cardId) {
-        super(Duration.EndOfTurn, Outcome.Exile);
-        this.cardId = cardId;
-        staticText = "If that card would be put into your graveyard this turn, exile it instead";
-    }
-
-    JaceTelepathUnboundReplacementEffect(final JaceTelepathUnboundReplacementEffect effect) {
-        super(effect);
-        this.cardId = effect.cardId;
-    }
-
-    @Override
-    public JaceTelepathUnboundReplacementEffect copy() {
-        return new JaceTelepathUnboundReplacementEffect(this);
-    }
-
-    @Override
-    public boolean replaceEvent(GameEvent event, Ability source, Game game) {
-        Player controller = game.getPlayer(source.getControllerId());
-        Card card = game.getCard(this.cardId);
-        if (controller != null && card != null) {
-            controller.moveCardToExileWithInfo(card, null, "", source.getSourceId(), game, Zone.STACK, true);
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    public boolean checksEventType(GameEvent event, Game game) {
-        return event.getType() == GameEvent.EventType.ZONE_CHANGE;
-    }
-
-    @Override
-    public boolean applies(GameEvent event, Ability source, Game game) {
-        ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
-        return zEvent.getToZone() == Zone.GRAVEYARD
-                && zEvent.getTargetId().equals(this.cardId);
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/l/LilianaDeathMage.java b/Mage.Sets/src/mage/cards/l/LilianaDeathMage.java
new file mode 100644
index 0000000000..45c189ded9
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/l/LilianaDeathMage.java
@@ -0,0 +1,117 @@
+package mage.cards.l;
+
+import mage.abilities.Ability;
+import mage.abilities.LoyaltyAbility;
+import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.DestroyTargetEffect;
+import mage.abilities.effects.common.LoseLifeTargetControllerEffect;
+import mage.cards.Card;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.*;
+import mage.filter.StaticFilters;
+import mage.game.Game;
+import mage.players.Player;
+import mage.target.common.TargetCardInYourGraveyard;
+import mage.target.common.TargetCreaturePermanent;
+import mage.target.common.TargetOpponent;
+
+import java.util.UUID;
+
+/**
+ *
+ * @author htrajan
+ */
+public final class LilianaDeathMage extends CardImpl {
+
+    public LilianaDeathMage(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{4}{B}{B}");
+        
+        this.addSuperType(SuperType.LEGENDARY);
+        this.subtype.add(SubType.LILIANA);
+        this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4));
+
+        // +1: Return up to one target creature card from your graveyard to your hand.
+        Ability plusAbility = new LoyaltyAbility(new LilianaDeathMagePlusEffect(), 1);
+        plusAbility.addTarget(new TargetCardInYourGraveyard(0, 1, StaticFilters.FILTER_CARD_CREATURE));
+        this.addAbility(plusAbility);
+
+        // −3: Destroy target creature. Its controller loses 2 life.
+        Ability minusAbility = new LoyaltyAbility(new DestroyTargetEffect(), -3);
+        minusAbility.addTarget(new TargetCreaturePermanent());
+        minusAbility.addEffect(new LoseLifeTargetControllerEffect(2));
+        this.addAbility(minusAbility);
+
+        // −7: Target opponent loses 2 life for each creature card in their graveyard.
+        Ability ultimateAbility = new LoyaltyAbility(new LilianaDeathMageUltimateEffect(), -7);
+        ultimateAbility.addTarget(new TargetOpponent());
+        this.addAbility(ultimateAbility);
+    }
+
+    private LilianaDeathMage(final LilianaDeathMage card) {
+        super(card);
+    }
+
+    @Override
+    public LilianaDeathMage copy() {
+        return new LilianaDeathMage(this);
+    }
+}
+
+class LilianaDeathMagePlusEffect extends OneShotEffect {
+
+    LilianaDeathMagePlusEffect() {
+        super(Outcome.Benefit);
+        staticText = "Return up to one target creature card from your graveyard to your hand";
+    }
+
+    private LilianaDeathMagePlusEffect(LilianaDeathMagePlusEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public LilianaDeathMagePlusEffect copy() {
+        return new LilianaDeathMagePlusEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Player player = game.getPlayer(source.getControllerId());
+        if (player == null || !player.chooseUse(Outcome.Benefit, "Return a creature card from your graveyard to your hand?", source, game)) {
+            return false;
+        }
+        Card card = game.getCard(source.getTargets().get(0).getFirstTarget());
+        if (card == null) {
+            return false;
+        }
+        return player.moveCards(card, Zone.HAND, source, game);
+    }
+}
+
+class LilianaDeathMageUltimateEffect extends OneShotEffect {
+
+    LilianaDeathMageUltimateEffect() {
+        super(Outcome.Damage);
+        staticText = "Target opponent loses 2 life for each creature card in their graveyard";
+    }
+
+    private LilianaDeathMageUltimateEffect(LilianaDeathMageUltimateEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public LilianaDeathMageUltimateEffect copy() {
+        return new LilianaDeathMageUltimateEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Player opponent = game.getPlayer(source.getFirstTarget());
+        if (opponent != null) {
+            int amount = opponent.getGraveyard().count(StaticFilters.FILTER_CARD_CREATURE, game);
+            opponent.loseLife(amount * 2, game, false);
+        }
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/m/MongrelPack.java b/Mage.Sets/src/mage/cards/m/MongrelPack.java
index 732f0c97ac..94d40ab16d 100644
--- a/Mage.Sets/src/mage/cards/m/MongrelPack.java
+++ b/Mage.Sets/src/mage/cards/m/MongrelPack.java
@@ -11,7 +11,7 @@ import mage.constants.TurnPhase;
 import mage.constants.Zone;
 import mage.game.Game;
 import mage.game.events.GameEvent;
-import mage.game.permanent.token.DogToken;
+import mage.game.permanent.token.GreenDogToken;
 
 import java.util.UUID;
 
@@ -44,7 +44,7 @@ public final class MongrelPack extends CardImpl {
 class MongrelPackAbility extends ZoneChangeTriggeredAbility {
 
     MongrelPackAbility() {
-        super(Zone.BATTLEFIELD, Zone.GRAVEYARD, new CreateTokenEffect(new DogToken(), 4), "When {this} dies during combat, ", false);
+        super(Zone.BATTLEFIELD, Zone.GRAVEYARD, new CreateTokenEffect(new GreenDogToken(), 4), "When {this} dies during combat, ", false);
     }
 
     private MongrelPackAbility(MongrelPackAbility ability) {
diff --git a/Mage.Sets/src/mage/cards/r/RinAndSeriInseparable.java b/Mage.Sets/src/mage/cards/r/RinAndSeriInseparable.java
new file mode 100644
index 0000000000..21789c8869
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/r/RinAndSeriInseparable.java
@@ -0,0 +1,91 @@
+package mage.cards.r;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.common.SpellCastControllerTriggeredAbility;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.dynamicvalue.DynamicValue;
+import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
+import mage.abilities.effects.Effect;
+import mage.abilities.effects.common.CreateTokenEffect;
+import mage.abilities.effects.common.DamageTargetEffect;
+import mage.abilities.effects.common.GainLifeEffect;
+import mage.abilities.hint.ValueHint;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.SuperType;
+import mage.filter.FilterPermanent;
+import mage.filter.FilterSpell;
+import mage.filter.common.FilterControlledCreaturePermanent;
+import mage.game.permanent.token.GreenCatToken;
+import mage.game.permanent.token.WhiteDogToken;
+import mage.target.common.TargetAnyTarget;
+
+import java.util.UUID;
+
+/**
+ *
+ * @author htrajan
+ */
+public final class RinAndSeriInseparable extends CardImpl {
+
+    private static final FilterSpell dogSpellFilter = new FilterSpell("a Dog spell");
+    private static final FilterSpell catSpellFilter = new FilterSpell("a Cat spell");
+
+    private static final FilterPermanent dogPermanentFilter = new FilterControlledCreaturePermanent("Dogs you control");
+    private static final FilterPermanent catPermanentFilter = new FilterControlledCreaturePermanent("Cats you control");
+
+    static {
+        dogSpellFilter.add(SubType.DOG.getPredicate());
+        catSpellFilter.add(SubType.CAT.getPredicate());
+
+        dogPermanentFilter.add(SubType.DOG.getPredicate());
+        catPermanentFilter.add(SubType.CAT.getPredicate());
+    }
+
+    public RinAndSeriInseparable(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}{G}{W}");
+        
+        this.addSuperType(SuperType.LEGENDARY);
+        this.subtype.add(SubType.DOG);
+        this.subtype.add(SubType.CAT);
+        this.power = new MageInt(4);
+        this.toughness = new MageInt(4);
+
+        // Whenever you cast a Dog spell, create a 1/1 green Cat creature token.
+        this.addAbility(new SpellCastControllerTriggeredAbility(
+                new CreateTokenEffect(new GreenCatToken()), dogSpellFilter, false
+        ));
+
+        // Whenever you cast a Cat spell, create a 1/1 white Dog creature token.
+        this.addAbility(new SpellCastControllerTriggeredAbility(
+                new CreateTokenEffect(new WhiteDogToken()), catSpellFilter, false
+        ));
+
+        // {R}{G}{W}: Rin and Seri, Inseparable deals damage to any target equal to the number of Dogs you control. You gain life equal to the number of Cats you control.
+        DynamicValue dogCount = new PermanentsOnBattlefieldCount(dogPermanentFilter);
+        Effect damageEffect = new DamageTargetEffect(dogCount);
+        damageEffect.setText("{source} deals damage to any target equal to the number of Dogs you control");
+        DynamicValue catCount = new PermanentsOnBattlefieldCount(catPermanentFilter);
+        Effect lifeGainEffect = new GainLifeEffect(catCount);
+        lifeGainEffect.setText("You gain life equal to the number of Cats you control");
+        Ability ability = new SimpleActivatedAbility(damageEffect, new ManaCostsImpl("{R}{G}{W}"));
+        ability.addEffect(lifeGainEffect);
+        ability.addTarget(new TargetAnyTarget());
+        ability.addHint(new ValueHint("Dogs you control", dogCount));
+        ability.addHint(new ValueHint("Cats you control", catCount));
+        this.addAbility(ability);
+    }
+
+    private RinAndSeriInseparable(final RinAndSeriInseparable card) {
+        super(card);
+    }
+
+    @Override
+    public RinAndSeriInseparable copy() {
+        return new RinAndSeriInseparable(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/cards/w/WaitingInTheWeeds.java b/Mage.Sets/src/mage/cards/w/WaitingInTheWeeds.java
index d7e6015625..114c0fa813 100644
--- a/Mage.Sets/src/mage/cards/w/WaitingInTheWeeds.java
+++ b/Mage.Sets/src/mage/cards/w/WaitingInTheWeeds.java
@@ -14,7 +14,7 @@ import mage.filter.predicate.Predicates;
 import mage.filter.predicate.permanent.TappedPredicate;
 import mage.game.Game;
 import mage.game.permanent.token.Token;
-import mage.game.permanent.token.WaitingInTheWeedsCatToken;
+import mage.game.permanent.token.GreenCatToken;
 import mage.players.Player;
 
 /**
@@ -68,7 +68,7 @@ class WaitingInTheWeedsEffect extends OneShotEffect {
         Player controller = game.getPlayer(source.getControllerId());
         if (controller != null) {
             for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
-                Token token = new WaitingInTheWeedsCatToken();
+                Token token = new GreenCatToken();
                 int amount = game.getBattlefield().getAllActivePermanents(filter, playerId, game).size();
                 token.putOntoBattlefield(amount, game, source.getSourceId(), playerId);
             }
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 974055a407..ce980c6f23 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -36,16 +36,20 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Azusa, Lost but Seeking", 173, Rarity.RARE, mage.cards.a.AzusaLostButSeeking.class));
         cards.add(new SetCardInfo("Bad Deal", 89, Rarity.UNCOMMON, mage.cards.b.BadDeal.class));
         cards.add(new SetCardInfo("Baneslayer Angel", 4, Rarity.MYTHIC, mage.cards.b.BaneslayerAngel.class));
+        cards.add(new SetCardInfo("Basri, Devoted Paladin", 320, Rarity.MYTHIC, mage.cards.b.BasriDevotedPaladin.class));
+        cards.add(new SetCardInfo("Chandra, Flame's Catalyst", 332, Rarity.MYTHIC, mage.cards.c.ChandraFlamesCatalyst.class));
         cards.add(new SetCardInfo("Chandra's Firemaw", 333, Rarity.RARE, mage.cards.c.ChandrasFiremaw.class));
         cards.add(new SetCardInfo("Containment Priest", 314, Rarity.RARE, mage.cards.c.ContainmentPriest.class));
         cards.add(new SetCardInfo("Double Vision", 142, Rarity.RARE, mage.cards.d.DoubleVision.class));
         cards.add(new SetCardInfo("Fierce Empath", 181, Rarity.UNCOMMON, mage.cards.f.FierceEmpath.class));
         cards.add(new SetCardInfo("Gadrak, the Crown-Scourge", 146, Rarity.RARE, mage.cards.g.GadrakTheCrownScourge.class));
+        cards.add(new SetCardInfo("Garruk, Savage Herald", 336, Rarity.MYTHIC, mage.cards.g.GarrukSavageHerald.class));
         cards.add(new SetCardInfo("Grim Tutor", 103, Rarity.MYTHIC, mage.cards.g.GrimTutor.class));
         cards.add(new SetCardInfo("Historian of Zhalfir", 325, Rarity.UNCOMMON, mage.cards.h.HistorianOfZhalfir.class));
         cards.add(new SetCardInfo("Indulging Patrician", 219, Rarity.UNCOMMON, mage.cards.i.IndulgingPatrician.class));
         cards.add(new SetCardInfo("Island", 310, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Jeskai Elder", 53, Rarity.UNCOMMON, mage.cards.j.JeskaiElder.class));
+        cards.add(new SetCardInfo("Liliana, Death Mage", 328, Rarity.MYTHIC, mage.cards.l.LilianaDeathMage.class));
         cards.add(new SetCardInfo("Liliana's Scrounger", 330, Rarity.UNCOMMON, mage.cards.l.LilianasScrounger.class));
         cards.add(new SetCardInfo("Llanowar Visionary", 193, Rarity.COMMON, mage.cards.l.LlanowarVisionary.class));
         cards.add(new SetCardInfo("Mangara, the Diplomat", 27, Rarity.MYTHIC, mage.cards.m.MangaraTheDiplomat.class));
@@ -54,6 +58,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Peer into the Abyss", 117, Rarity.RARE, mage.cards.p.PeerIntoTheAbyss.class));
         cards.add(new SetCardInfo("Quirion Dryad", 198, Rarity.UNCOMMON, mage.cards.q.QuirionDryad.class));
         cards.add(new SetCardInfo("Rain of Revelation", 61, Rarity.UNCOMMON, mage.cards.r.RainOfRevelation.class));
+        cards.add(new SetCardInfo("Rin and Seri, Inseparable", 278, Rarity.MYTHIC, mage.cards.r.RinAndSeriInseparable.class));
         cards.add(new SetCardInfo("Runed Halo", 32, Rarity.RARE, mage.cards.r.RunedHalo.class));
         cards.add(new SetCardInfo("Shipwreck Dowser", 71, Rarity.UNCOMMON, mage.cards.s.ShipwreckDowser.class));
         cards.add(new SetCardInfo("Sigiled Contender", 323, Rarity.UNCOMMON, mage.cards.s.SigiledContender.class));
diff --git a/Mage/src/main/java/mage/abilities/effects/CastCardFromGraveyardThenExileItEffect.java b/Mage/src/main/java/mage/abilities/effects/CastCardFromGraveyardThenExileItEffect.java
new file mode 100644
index 0000000000..c17f6fae73
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/effects/CastCardFromGraveyardThenExileItEffect.java
@@ -0,0 +1,115 @@
+package mage.abilities.effects;
+
+import mage.abilities.Ability;
+import mage.cards.Card;
+import mage.constants.AsThoughEffectType;
+import mage.constants.Duration;
+import mage.constants.Outcome;
+import mage.constants.Zone;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+import mage.game.events.ZoneChangeEvent;
+import mage.players.Player;
+import mage.target.targetpointer.FixedTarget;
+
+import java.util.UUID;
+
+public class CastCardFromGraveyardThenExileItEffect extends OneShotEffect {
+
+    public CastCardFromGraveyardThenExileItEffect() {
+        super(Outcome.Benefit);
+    }
+
+    CastCardFromGraveyardThenExileItEffect(final CastCardFromGraveyardThenExileItEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public CastCardFromGraveyardThenExileItEffect copy() {
+        return new CastCardFromGraveyardThenExileItEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Card card = game.getCard(this.getTargetPointer().getFirst(game, source));
+        if (card != null) {
+            ContinuousEffect effect = new CastCardFromGraveyardEffect();
+            effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game)));
+            game.addEffect(effect, source);
+            effect = new ExileReplacementEffect(card.getId());
+            game.addEffect(effect, source);
+            return true;
+        }
+        return false;
+    }
+}
+
+class CastCardFromGraveyardEffect extends AsThoughEffectImpl {
+
+    CastCardFromGraveyardEffect() {
+        super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit);
+        this.staticText = "You may cast target card from your graveyard";
+    }
+
+    CastCardFromGraveyardEffect(final CastCardFromGraveyardEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        return true;
+    }
+
+    @Override
+    public CastCardFromGraveyardEffect copy() {
+        return new CastCardFromGraveyardEffect(this);
+    }
+
+    @Override
+    public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
+        return objectId.equals(this.getTargetPointer().getFirst(game, source)) && affectedControllerId.equals(source.getControllerId());
+    }
+}
+
+class ExileReplacementEffect extends ReplacementEffectImpl {
+
+    private final UUID cardId;
+
+    ExileReplacementEffect(UUID cardId) {
+        super(Duration.EndOfTurn, Outcome.Exile);
+        this.cardId = cardId;
+        this.staticText = "If that card would be put into your graveyard this turn, exile it instead";
+    }
+
+    ExileReplacementEffect(final ExileReplacementEffect effect) {
+        super(effect);
+        this.cardId = effect.cardId;
+    }
+
+    @Override
+    public ExileReplacementEffect copy() {
+        return new ExileReplacementEffect(this);
+    }
+
+    @Override
+    public boolean replaceEvent(GameEvent event, Ability source, Game game) {
+        Player controller = game.getPlayer(source.getControllerId());
+        Card card = game.getCard(this.cardId);
+        if (controller != null && card != null) {
+            return controller.moveCards(card, Zone.EXILED, source, game);
+        }
+        return false;
+    }
+
+    @Override
+    public boolean checksEventType(GameEvent event, Game game) {
+        return event.getType() == GameEvent.EventType.ZONE_CHANGE;
+    }
+
+    @Override
+    public boolean applies(GameEvent event, Ability source, Game game) {
+        ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
+        return zEvent.getToZone() == Zone.GRAVEYARD
+                && zEvent.getTargetId().equals(this.cardId);
+    }
+}
\ No newline at end of file
diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/CastFromHandWithoutPayingManaCostEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/CastFromHandWithoutPayingManaCostEffect.java
index a330989828..c72a601922 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/continuous/CastFromHandWithoutPayingManaCostEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/CastFromHandWithoutPayingManaCostEffect.java
@@ -28,10 +28,14 @@ public class CastFromHandWithoutPayingManaCostEffect extends ContinuousEffectImp
     }
 
     public CastFromHandWithoutPayingManaCostEffect(FilterCard filter, boolean fromHand) {
-        super(Duration.WhileOnBattlefield, Outcome.Detriment);
+        this(filter, fromHand, Duration.WhileOnBattlefield);
+    }
+
+    public CastFromHandWithoutPayingManaCostEffect(FilterCard filter, boolean fromHand, Duration duration) {
+        super(duration, Outcome.Detriment);
         this.filter = filter;
         this.fromHand = fromHand;
-        staticText = "You may cast " + filter.getMessage()
+        this.staticText = "You may cast " + filter.getMessage()
                 + (fromHand ? " from your hand" : "")
                 + " without paying their mana costs";
     }
diff --git a/Mage/src/main/java/mage/constants/SubType.java b/Mage/src/main/java/mage/constants/SubType.java
index 7f0c89242f..7724b09214 100644
--- a/Mage/src/main/java/mage/constants/SubType.java
+++ b/Mage/src/main/java/mage/constants/SubType.java
@@ -391,6 +391,7 @@ public enum SubType {
     ARLINN("Arlinn", SubTypeSet.PlaneswalkerType),
     ASHIOK("Ashiok", SubTypeSet.PlaneswalkerType),
     AURRA("Aurra", SubTypeSet.PlaneswalkerType, true), // Star Wars
+    BASRI("Basri", SubTypeSet.PlaneswalkerType),
     BOLAS("Bolas", SubTypeSet.PlaneswalkerType),
     CALIX("Calix", SubTypeSet.PlaneswalkerType),
     CHANDRA("Chandra", SubTypeSet.PlaneswalkerType),
diff --git a/Mage/src/main/java/mage/game/permanent/token/WaitingInTheWeedsCatToken.java b/Mage/src/main/java/mage/game/permanent/token/GreenCatToken.java
similarity index 60%
rename from Mage/src/main/java/mage/game/permanent/token/WaitingInTheWeedsCatToken.java
rename to Mage/src/main/java/mage/game/permanent/token/GreenCatToken.java
index d7d78e4205..da32a283f9 100644
--- a/Mage/src/main/java/mage/game/permanent/token/WaitingInTheWeedsCatToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/GreenCatToken.java
@@ -9,9 +9,9 @@ import mage.constants.SubType;
  *
  * @author spjspj
  */
-public final class WaitingInTheWeedsCatToken extends TokenImpl {
+public final class GreenCatToken extends TokenImpl {
 
-    public WaitingInTheWeedsCatToken() {
+    public GreenCatToken() {
         super("Cat", "1/1 green Cat creature token");
         cardType.add(CardType.CREATURE);
         color.setGreen(true);
@@ -20,11 +20,11 @@ public final class WaitingInTheWeedsCatToken extends TokenImpl {
         toughness = new MageInt(1);
     }
     
-    public WaitingInTheWeedsCatToken(final WaitingInTheWeedsCatToken token) {
+    public GreenCatToken(final GreenCatToken token) {
         super(token);
     }
 
-    public WaitingInTheWeedsCatToken copy() {
-        return new WaitingInTheWeedsCatToken(this);
+    public GreenCatToken copy() {
+        return new GreenCatToken(this);
     }
 }
diff --git a/Mage/src/main/java/mage/game/permanent/token/DogToken.java b/Mage/src/main/java/mage/game/permanent/token/GreenDogToken.java
similarity index 66%
rename from Mage/src/main/java/mage/game/permanent/token/DogToken.java
rename to Mage/src/main/java/mage/game/permanent/token/GreenDogToken.java
index a229350513..dddd731733 100644
--- a/Mage/src/main/java/mage/game/permanent/token/DogToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/GreenDogToken.java
@@ -7,9 +7,9 @@ import mage.constants.SubType;
 /**
  * @author spjspj
  */
-public final class DogToken extends TokenImpl {
+public final class GreenDogToken extends TokenImpl {
 
-    public DogToken() {
+    public GreenDogToken() {
         super("Dog", "1/1 green Dog creature token");
         cardType.add(CardType.CREATURE);
         subtype.add(SubType.DOG);
@@ -19,11 +19,11 @@ public final class DogToken extends TokenImpl {
         toughness = new MageInt(1);
     }
 
-    private DogToken(final DogToken token) {
+    private GreenDogToken(final GreenDogToken token) {
         super(token);
     }
 
-    public DogToken copy() {
-        return new DogToken(this);
+    public GreenDogToken copy() {
+        return new GreenDogToken(this);
     }
 }
diff --git a/Mage/src/main/java/mage/game/permanent/token/WhiteDogToken.java b/Mage/src/main/java/mage/game/permanent/token/WhiteDogToken.java
new file mode 100644
index 0000000000..d9742c0fc7
--- /dev/null
+++ b/Mage/src/main/java/mage/game/permanent/token/WhiteDogToken.java
@@ -0,0 +1,31 @@
+package mage.game.permanent.token;
+
+import mage.MageInt;
+import mage.constants.CardType;
+import mage.constants.SubType;
+
+/**
+ *
+ * @author htrajan
+ */
+public final class WhiteDogToken extends TokenImpl {
+
+    public WhiteDogToken() {
+        super("Dog", "1/1 white Dog creature token");
+        cardType.add(CardType.CREATURE);
+        subtype.add(SubType.DOG);
+
+        color.setWhite(true);
+        power = new MageInt(1);
+        toughness = new MageInt(1);
+    }
+
+    private WhiteDogToken(final WhiteDogToken token) {
+        super(token);
+    }
+
+    public WhiteDogToken copy() {
+        return new WhiteDogToken(this);
+    }
+
+}

From 7c1625513b1688b4996437faec25ef8b143f2822 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sat, 6 Jun 2020 22:14:13 -0400
Subject: [PATCH 128/586] updated M21 spoiler and reprints

---
 Mage.Sets/src/mage/sets/CoreSet2021.java | 5 +++--
 Utils/mtg-cards-data.txt                 | 2 ++
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index ce980c6f23..843ea24849 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -37,8 +37,8 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Bad Deal", 89, Rarity.UNCOMMON, mage.cards.b.BadDeal.class));
         cards.add(new SetCardInfo("Baneslayer Angel", 4, Rarity.MYTHIC, mage.cards.b.BaneslayerAngel.class));
         cards.add(new SetCardInfo("Basri, Devoted Paladin", 320, Rarity.MYTHIC, mage.cards.b.BasriDevotedPaladin.class));
-        cards.add(new SetCardInfo("Chandra, Flame's Catalyst", 332, Rarity.MYTHIC, mage.cards.c.ChandraFlamesCatalyst.class));
         cards.add(new SetCardInfo("Chandra's Firemaw", 333, Rarity.RARE, mage.cards.c.ChandrasFiremaw.class));
+        cards.add(new SetCardInfo("Chandra, Flame's Catalyst", 332, Rarity.MYTHIC, mage.cards.c.ChandraFlamesCatalyst.class));
         cards.add(new SetCardInfo("Containment Priest", 314, Rarity.RARE, mage.cards.c.ContainmentPriest.class));
         cards.add(new SetCardInfo("Double Vision", 142, Rarity.RARE, mage.cards.d.DoubleVision.class));
         cards.add(new SetCardInfo("Fierce Empath", 181, Rarity.UNCOMMON, mage.cards.f.FierceEmpath.class));
@@ -49,8 +49,8 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Indulging Patrician", 219, Rarity.UNCOMMON, mage.cards.i.IndulgingPatrician.class));
         cards.add(new SetCardInfo("Island", 310, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Jeskai Elder", 53, Rarity.UNCOMMON, mage.cards.j.JeskaiElder.class));
-        cards.add(new SetCardInfo("Liliana, Death Mage", 328, Rarity.MYTHIC, mage.cards.l.LilianaDeathMage.class));
         cards.add(new SetCardInfo("Liliana's Scrounger", 330, Rarity.UNCOMMON, mage.cards.l.LilianasScrounger.class));
+        cards.add(new SetCardInfo("Liliana, Death Mage", 328, Rarity.MYTHIC, mage.cards.l.LilianaDeathMage.class));
         cards.add(new SetCardInfo("Llanowar Visionary", 193, Rarity.COMMON, mage.cards.l.LlanowarVisionary.class));
         cards.add(new SetCardInfo("Mangara, the Diplomat", 27, Rarity.MYTHIC, mage.cards.m.MangaraTheDiplomat.class));
         cards.add(new SetCardInfo("Mystic Skyfish", 326, Rarity.COMMON, mage.cards.m.MysticSkyfish.class));
@@ -58,6 +58,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Peer into the Abyss", 117, Rarity.RARE, mage.cards.p.PeerIntoTheAbyss.class));
         cards.add(new SetCardInfo("Quirion Dryad", 198, Rarity.UNCOMMON, mage.cards.q.QuirionDryad.class));
         cards.add(new SetCardInfo("Rain of Revelation", 61, Rarity.UNCOMMON, mage.cards.r.RainOfRevelation.class));
+        cards.add(new SetCardInfo("Rewind", 63, Rarity.UNCOMMON, mage.cards.r.Rewind.class));
         cards.add(new SetCardInfo("Rin and Seri, Inseparable", 278, Rarity.MYTHIC, mage.cards.r.RinAndSeriInseparable.class));
         cards.add(new SetCardInfo("Runed Halo", 32, Rarity.RARE, mage.cards.r.RunedHalo.class));
         cards.add(new SetCardInfo("Shipwreck Dowser", 71, Rarity.UNCOMMON, mage.cards.s.ShipwreckDowser.class));
diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt
index 5ef42ee306..63fcff2db8 100644
--- a/Utils/mtg-cards-data.txt
+++ b/Utils/mtg-cards-data.txt
@@ -37445,6 +37445,7 @@ Pack Leader|Core Set 2021|29|R|{1}{W}|Creature - Dog|2|2|Other Dogs you control
 Runed Halo|Core Set 2021|32|R|{W}{W}|Enchantment|||As Runed Halo enters the battlefield, choose a card name.$You have protection from the chosen name.|
 Jeskai Elder|Core Set 2021|53|U|{1}{U}|Creature - Human Monk|1|2|Prowess$Whenever Jeskai Elder deals combat damage to a player, you may draw a card. If you do, discard a card.|
 Rain of Revelation|Core Set 2021|61|U|{3}{U}|Instant|||Draw three cards, then discard a card.|
+Rewind|Core Set 2021|63|U|{2}{U}{U}|Instant|||Counter target spell. Untap up to four lands.|
 Shipwreck Dowser|Core Set 2021|71|U|{3}{U}{U}|Creature - Merfolk Wizard|3|3|Prowess$When Shipwreck Dowser enters the battlefield, return target instant or sorcery card from your graveyard to your hand.|
 Teferi, Master of Time|Core Set 2021|75|M|{2}{U}{U}|Legendary Planeswalker - Teferi|3|You may activate loyalty abilities of Teferi, Master of Time on any player's turn any time you could cast an instant.$+1: Draw a card, then discard a card.$−3: Target creature you don't control phases out.$−10: Take two extra turns after this one.|
 Teferi's Ageless Insight|Core Set 2021|76|R|{2}{U}{U}|Legendary Enchantment|||If you would draw a card except the first one you draw in each of your draw steps, draw two cards instead.|
@@ -37460,6 +37461,7 @@ Double Vision|Core Set 2021|142|R|{3}{R}{R}|Enchantment|||Whenever you cast your
 Gadrak, the Crown-Scourge|Core Set 2021|146|R|{2}{R}|Legendary Creature - Dragon|5|4|Flying$Gadrak, the Crown-Scourge can't attack unless you control four or more artifacts.$At the beginning of your end step, create a Treasure token for each nontoken creature that died this turn.|
 Azusa, Lost but Seeking|Core Set 2021|173|R|{2}{G}|Legendary Creature - Human Monk|1|2|You may play two additional lands on each of your turns.|
 Fierce Empath|Core Set 2021|181|U|{2}{G}|Creature - Elf|1|1|When Fierce Empath enters the battlefield, you may search your library for a creature card with converted mana cost 6 or greater, reveal it, put it into your hand, then shuffle your library.|
+Fungal Rebirth|Core Set 2021|182|U|{2}{G}|Instant|||Return target permanent card from your graveyard to your hand. If a creature died this turn, create two 1/1 green Saproling creature tokens.|
 Llanowar Visionary|Core Set 2021|193|C|{2}{G}|Creature - Elf Druid|2|2|When Llanowar Visionary enters the battlefield, draw a card.${T}: Add {G}.|
 Primal Might|Core Set 2021|197|R|{X}{G}|Sorcery|||Target creature you control gets +X/+X until end of turn. Then it fights up to one target creature you don't control.|
 Quirion Dryad|Core Set 2021|198|U|{1}{G}|Creature - Dryad|1|1|Whenever you cast a white, blue, black, or red spell, put a +1/+1 counter on Quirion Dryad.|

From ef04d24ba6dd6472964e39dbd6f5df7cc287ee6c Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sun, 7 Jun 2020 13:10:53 -0400
Subject: [PATCH 129/586] Implemented Liliana's Scorn

---
 Mage.Sets/src/mage/cards/l/LilianasScorn.java | 42 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 43 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/l/LilianasScorn.java

diff --git a/Mage.Sets/src/mage/cards/l/LilianasScorn.java b/Mage.Sets/src/mage/cards/l/LilianasScorn.java
new file mode 100644
index 0000000000..0b577a559c
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/l/LilianasScorn.java
@@ -0,0 +1,42 @@
+package mage.cards.l;
+
+import mage.abilities.effects.common.DestroyTargetEffect;
+import mage.abilities.effects.common.search.SearchLibraryGraveyardPutInHandEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.filter.FilterCard;
+import mage.filter.predicate.mageobject.NamePredicate;
+import mage.target.common.TargetCreaturePermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class LilianasScorn extends CardImpl {
+
+    private static final FilterCard filter = new FilterCard("Liliana, Death Mage");
+
+    static {
+        filter.add(new NamePredicate("Liliana, Death Mage"));
+    }
+
+    public LilianasScorn(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{B}{B}");
+
+        // Destroy target creature. You may search your library and/or graveyard for a card named Liliana, Death Mage, reveal it, and put it into your hand. If you search your library this way, shuffle it.
+        this.getSpellAbility().addEffect(new DestroyTargetEffect());
+        this.getSpellAbility().addEffect(new SearchLibraryGraveyardPutInHandEffect(filter, false, true));
+        this.getSpellAbility().addTarget(new TargetCreaturePermanent());
+    }
+
+    private LilianasScorn(final LilianasScorn card) {
+        super(card);
+    }
+
+    @Override
+    public LilianasScorn copy() {
+        return new LilianasScorn(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 843ea24849..bc12983fba 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -49,6 +49,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Indulging Patrician", 219, Rarity.UNCOMMON, mage.cards.i.IndulgingPatrician.class));
         cards.add(new SetCardInfo("Island", 310, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Jeskai Elder", 53, Rarity.UNCOMMON, mage.cards.j.JeskaiElder.class));
+        cards.add(new SetCardInfo("Liliana's Scorn", 329, Rarity.RARE, mage.cards.l.LilianasScorn.class));
         cards.add(new SetCardInfo("Liliana's Scrounger", 330, Rarity.UNCOMMON, mage.cards.l.LilianasScrounger.class));
         cards.add(new SetCardInfo("Liliana, Death Mage", 328, Rarity.MYTHIC, mage.cards.l.LilianaDeathMage.class));
         cards.add(new SetCardInfo("Llanowar Visionary", 193, Rarity.COMMON, mage.cards.l.LlanowarVisionary.class));

From 52d45b6850c52b1b5c88a33c3be1866e6634c1e1 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sun, 7 Jun 2020 13:15:03 -0400
Subject: [PATCH 130/586] Implemented Predatory Wurm

---
 Mage.Sets/src/mage/cards/p/PredatoryWurm.java | 53 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 54 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/p/PredatoryWurm.java

diff --git a/Mage.Sets/src/mage/cards/p/PredatoryWurm.java b/Mage.Sets/src/mage/cards/p/PredatoryWurm.java
new file mode 100644
index 0000000000..9808b09b7f
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/p/PredatoryWurm.java
@@ -0,0 +1,53 @@
+package mage.cards.p;
+
+import mage.MageInt;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.condition.Condition;
+import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
+import mage.abilities.decorator.ConditionalContinuousEffect;
+import mage.abilities.effects.common.continuous.BoostSourceEffect;
+import mage.abilities.keyword.VigilanceAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+import mage.filter.FilterPermanent;
+import mage.filter.common.FilterPlaneswalkerPermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class PredatoryWurm extends CardImpl {
+
+    private static final FilterPermanent filter = new FilterPlaneswalkerPermanent(SubType.GARRUK);
+    private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter);
+
+    public PredatoryWurm(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}");
+
+        this.subtype.add(SubType.WURM);
+        this.power = new MageInt(4);
+        this.toughness = new MageInt(4);
+
+        // Vigilance
+        this.addAbility(VigilanceAbility.getInstance());
+
+        // Predatory Wurm gets +2/+2 as long as you control a Garruk planeswalker.
+        this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect(
+                new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield),
+                condition, "{this} gets +2/+2 as long as you control a Garruk planeswalker"
+        )));
+    }
+
+    private PredatoryWurm(final PredatoryWurm card) {
+        super(card);
+    }
+
+    @Override
+    public PredatoryWurm copy() {
+        return new PredatoryWurm(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index bc12983fba..75ce0eedab 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -57,6 +57,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Mystic Skyfish", 326, Rarity.COMMON, mage.cards.m.MysticSkyfish.class));
         cards.add(new SetCardInfo("Pack Leader", 29, Rarity.RARE, mage.cards.p.PackLeader.class));
         cards.add(new SetCardInfo("Peer into the Abyss", 117, Rarity.RARE, mage.cards.p.PeerIntoTheAbyss.class));
+        cards.add(new SetCardInfo("Predatory Wurm", 338, Rarity.UNCOMMON, mage.cards.p.PredatoryWurm.class));
         cards.add(new SetCardInfo("Quirion Dryad", 198, Rarity.UNCOMMON, mage.cards.q.QuirionDryad.class));
         cards.add(new SetCardInfo("Rain of Revelation", 61, Rarity.UNCOMMON, mage.cards.r.RainOfRevelation.class));
         cards.add(new SetCardInfo("Rewind", 63, Rarity.UNCOMMON, mage.cards.r.Rewind.class));

From 9ff4412a4afd107cc39dc7d922f17877cd749c6c Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sun, 7 Jun 2020 13:17:33 -0400
Subject: [PATCH 131/586] Implemented Teferi's Wavecaster

---
 .../src/mage/cards/c/ChandrasFiremaw.java     |  2 +-
 .../src/mage/cards/t/TeferisWavecaster.java   | 50 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 3 files changed, 52 insertions(+), 1 deletion(-)
 create mode 100644 Mage.Sets/src/mage/cards/t/TeferisWavecaster.java

diff --git a/Mage.Sets/src/mage/cards/c/ChandrasFiremaw.java b/Mage.Sets/src/mage/cards/c/ChandrasFiremaw.java
index 368e39f919..0a87253ad9 100644
--- a/Mage.Sets/src/mage/cards/c/ChandrasFiremaw.java
+++ b/Mage.Sets/src/mage/cards/c/ChandrasFiremaw.java
@@ -35,7 +35,7 @@ public final class ChandrasFiremaw extends CardImpl {
         this.addAbility(HasteAbility.getInstance());
 
         // When Chandra's Firemaw enters the battlefield, you may search your library and/or graveyard for a card named Chandra, Flame's Catalyst, reveal it, and put it into your hand. If you search your library this way, shuffle it.
-        this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryGraveyardPutInHandEffect(filter)));
+        this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryGraveyardPutInHandEffect(filter),true));
     }
 
     private ChandrasFiremaw(final ChandrasFiremaw card) {
diff --git a/Mage.Sets/src/mage/cards/t/TeferisWavecaster.java b/Mage.Sets/src/mage/cards/t/TeferisWavecaster.java
new file mode 100644
index 0000000000..54725309f8
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/t/TeferisWavecaster.java
@@ -0,0 +1,50 @@
+package mage.cards.t;
+
+import mage.MageInt;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.effects.common.search.SearchLibraryGraveyardPutInHandEffect;
+import mage.abilities.keyword.FlashAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.filter.FilterCard;
+import mage.filter.predicate.mageobject.NamePredicate;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class TeferisWavecaster extends CardImpl {
+
+    private static final FilterCard filter = new FilterCard("Teferi, Timeless Voyager");
+
+    static {
+        filter.add(new NamePredicate("Teferi, Timeless Voyager"));
+    }
+
+    public TeferisWavecaster(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{U}");
+
+        this.subtype.add(SubType.MERFOLK);
+        this.subtype.add(SubType.WIZARD);
+        this.power = new MageInt(3);
+        this.toughness = new MageInt(3);
+
+        // Flash
+        this.addAbility(FlashAbility.getInstance());
+
+        // When Teferi's Wavecaster enters the battlefield, you may search your library and/or graveyard for a card named Teferi, Timeless Voyager, reveal it, and put it into your hand. If you search your library this way, shuffle it.
+        this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryGraveyardPutInHandEffect(filter), true));
+    }
+
+    private TeferisWavecaster(final TeferisWavecaster card) {
+        super(card);
+    }
+
+    @Override
+    public TeferisWavecaster copy() {
+        return new TeferisWavecaster(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 75ce0eedab..abb15a7e70 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -69,6 +69,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Spirit of Malevolence", 331, Rarity.COMMON, mage.cards.s.SpiritOfMalevolence.class));
         cards.add(new SetCardInfo("Storm Caller", 335, Rarity.COMMON, mage.cards.s.StormCaller.class));
         cards.add(new SetCardInfo("Teferi's Protege", 77, Rarity.COMMON, mage.cards.t.TeferisProtege.class));
+        cards.add(new SetCardInfo("Teferi's Wavecaster", 327, Rarity.RARE, mage.cards.t.TeferisWavecaster.class));
         cards.add(new SetCardInfo("Teferi, Master of Time", 75, Rarity.MYTHIC, mage.cards.t.TeferiMasterOfTime.class));
         cards.add(new SetCardInfo("Tormod's Crypt", 241, Rarity.UNCOMMON, mage.cards.t.TormodsCrypt.class));
         cards.add(new SetCardInfo("Ugin, the Spirit Dragon", 1, Rarity.MYTHIC, mage.cards.u.UginTheSpiritDragon.class));

From 36c3db4da184206df14cd0bd58f1fec2679bb17a Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sun, 7 Jun 2020 13:34:17 -0400
Subject: [PATCH 132/586] Implemented Teferi's Ageless Insight

---
 .../src/mage/cards/a/AlhammarretsArchive.java | 36 ++++----
 .../mage/cards/t/TeferisAgelessInsight.java   | 86 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 3 files changed, 103 insertions(+), 20 deletions(-)
 create mode 100644 Mage.Sets/src/mage/cards/t/TeferisAgelessInsight.java

diff --git a/Mage.Sets/src/mage/cards/a/AlhammarretsArchive.java b/Mage.Sets/src/mage/cards/a/AlhammarretsArchive.java
index 383d20fc2c..b410035bf3 100644
--- a/Mage.Sets/src/mage/cards/a/AlhammarretsArchive.java
+++ b/Mage.Sets/src/mage/cards/a/AlhammarretsArchive.java
@@ -1,7 +1,5 @@
-
 package mage.cards.a;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.ReplacementEffectImpl;
@@ -13,14 +11,15 @@ import mage.game.events.GameEvent;
 import mage.players.Player;
 import mage.watchers.common.CardsDrawnDuringDrawStepWatcher;
 
+import java.util.UUID;
+
 /**
- *
  * @author fireshoes
  */
 public final class AlhammarretsArchive extends CardImpl {
 
     public AlhammarretsArchive(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{5}");
+        super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{5}");
         addSuperType(SuperType.LEGENDARY);
 
         // If you would gain life, you gain twice that much life instead.
@@ -30,7 +29,7 @@ public final class AlhammarretsArchive extends CardImpl {
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new AlhammarretsArchiveReplacementEffect()), new CardsDrawnDuringDrawStepWatcher());
     }
 
-    public AlhammarretsArchive(final AlhammarretsArchive card) {
+    private AlhammarretsArchive(final AlhammarretsArchive card) {
         super(card);
     }
 
@@ -42,12 +41,12 @@ public final class AlhammarretsArchive extends CardImpl {
 
 class AlhammarretsArchiveEffect extends ReplacementEffectImpl {
 
-    public AlhammarretsArchiveEffect() {
+    AlhammarretsArchiveEffect() {
         super(Duration.WhileOnBattlefield, Outcome.Benefit);
         staticText = "If you would gain life, you gain twice that much life instead";
     }
 
-    public AlhammarretsArchiveEffect(final AlhammarretsArchiveEffect effect) {
+    private AlhammarretsArchiveEffect(final AlhammarretsArchiveEffect effect) {
         super(effect);
     }
 
@@ -75,12 +74,12 @@ class AlhammarretsArchiveEffect extends ReplacementEffectImpl {
 
 class AlhammarretsArchiveReplacementEffect extends ReplacementEffectImpl {
 
-    public AlhammarretsArchiveReplacementEffect() {
+    AlhammarretsArchiveReplacementEffect() {
         super(Duration.WhileOnBattlefield, Outcome.Neutral);
         staticText = "If you draw a card except the first one you draw in each of your draw steps, draw two cards instead";
     }
 
-    public AlhammarretsArchiveReplacementEffect(final AlhammarretsArchiveReplacementEffect effect) {
+    private AlhammarretsArchiveReplacementEffect(final AlhammarretsArchiveReplacementEffect effect) {
         super(effect);
     }
 
@@ -110,17 +109,14 @@ class AlhammarretsArchiveReplacementEffect extends ReplacementEffectImpl {
 
     @Override
     public boolean applies(GameEvent event, Ability source, Game game) {
-        if (event.getPlayerId().equals(source.getControllerId())) {
-            if (game.isActivePlayer(event.getPlayerId())
-                    && game.getPhase().getStep().getType() == PhaseStep.DRAW) {
-                CardsDrawnDuringDrawStepWatcher watcher = game.getState().getWatcher(CardsDrawnDuringDrawStepWatcher.class);
-                if (watcher != null && watcher.getAmountCardsDrawn(event.getPlayerId()) > 0) {
-                    return true;
-                }
-            } else {
-                return true;
-            }
+        if (!event.getPlayerId().equals(source.getControllerId())) {
+            return false;
         }
-        return false;
+        if (!game.isActivePlayer(event.getPlayerId())
+                || game.getPhase().getStep().getType() != PhaseStep.DRAW) {
+            return true;
+        }
+        CardsDrawnDuringDrawStepWatcher watcher = game.getState().getWatcher(CardsDrawnDuringDrawStepWatcher.class);
+        return watcher != null && watcher.getAmountCardsDrawn(event.getPlayerId()) > 0;
     }
 }
diff --git a/Mage.Sets/src/mage/cards/t/TeferisAgelessInsight.java b/Mage.Sets/src/mage/cards/t/TeferisAgelessInsight.java
new file mode 100644
index 0000000000..4f1d57dc94
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/t/TeferisAgelessInsight.java
@@ -0,0 +1,86 @@
+package mage.cards.t;
+
+import mage.abilities.Ability;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.effects.ReplacementEffectImpl;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.*;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+import mage.players.Player;
+import mage.watchers.common.CardsDrawnDuringDrawStepWatcher;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class TeferisAgelessInsight extends CardImpl {
+
+    public TeferisAgelessInsight(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}{U}");
+        this.addSuperType(SuperType.LEGENDARY);
+
+        // If you would draw a card except the first one you draw in each of your draw steps, draw two cards instead.
+        this.addAbility(new SimpleStaticAbility(new TeferisAgelessInsightEffect()), new CardsDrawnDuringDrawStepWatcher());
+    }
+
+    private TeferisAgelessInsight(final TeferisAgelessInsight card) {
+        super(card);
+    }
+
+    @Override
+    public TeferisAgelessInsight copy() {
+        return new TeferisAgelessInsight(this);
+    }
+}
+
+class TeferisAgelessInsightEffect extends ReplacementEffectImpl {
+
+    TeferisAgelessInsightEffect() {
+        super(Duration.WhileOnBattlefield, Outcome.Neutral);
+        staticText = "If you draw a card except the first one you draw in each of your draw steps, draw two cards instead";
+    }
+
+    private TeferisAgelessInsightEffect(final TeferisAgelessInsightEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public TeferisAgelessInsightEffect copy() {
+        return new TeferisAgelessInsightEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        return true;
+    }
+
+    @Override
+    public boolean replaceEvent(GameEvent event, Ability source, Game game) {
+        Player controller = game.getPlayer(source.getControllerId());
+        if (controller != null) {
+            controller.drawCards(2, event.getSourceId(), game, event.getAppliedEffects());
+        }
+        return true;
+    }
+
+    @Override
+    public boolean checksEventType(GameEvent event, Game game) {
+        return event.getType() == GameEvent.EventType.DRAW_CARD;
+    }
+
+    @Override
+    public boolean applies(GameEvent event, Ability source, Game game) {
+        if (!event.getPlayerId().equals(source.getControllerId())) {
+            return false;
+        }
+        if (!game.isActivePlayer(event.getPlayerId())
+                || game.getPhase().getStep().getType() != PhaseStep.DRAW) {
+            return true;
+        }
+        CardsDrawnDuringDrawStepWatcher watcher = game.getState().getWatcher(CardsDrawnDuringDrawStepWatcher.class);
+        return watcher != null && watcher.getAmountCardsDrawn(event.getPlayerId()) > 0;
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index abb15a7e70..34594138f2 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -68,6 +68,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Sparkhunter Masticore", 240, Rarity.RARE, mage.cards.s.SparkhunterMasticore.class));
         cards.add(new SetCardInfo("Spirit of Malevolence", 331, Rarity.COMMON, mage.cards.s.SpiritOfMalevolence.class));
         cards.add(new SetCardInfo("Storm Caller", 335, Rarity.COMMON, mage.cards.s.StormCaller.class));
+        cards.add(new SetCardInfo("Teferi's Ageless Insight", 76, Rarity.RARE, mage.cards.t.TeferisAgelessInsight.class));
         cards.add(new SetCardInfo("Teferi's Protege", 77, Rarity.COMMON, mage.cards.t.TeferisProtege.class));
         cards.add(new SetCardInfo("Teferi's Wavecaster", 327, Rarity.RARE, mage.cards.t.TeferisWavecaster.class));
         cards.add(new SetCardInfo("Teferi, Master of Time", 75, Rarity.MYTHIC, mage.cards.t.TeferiMasterOfTime.class));

From 65d2f3c9c43250e94fa5dbe7c9cd98cd867c2088 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sun, 7 Jun 2020 13:40:19 -0400
Subject: [PATCH 133/586] Implemented Teferi's Tutelage (mill errata not
 complete)

---
 .../src/mage/cards/t/TeferisTutelage.java     | 40 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 .../PutLibraryIntoGraveTargetEffect.java      | 11 ++---
 3 files changed, 45 insertions(+), 7 deletions(-)
 create mode 100644 Mage.Sets/src/mage/cards/t/TeferisTutelage.java

diff --git a/Mage.Sets/src/mage/cards/t/TeferisTutelage.java b/Mage.Sets/src/mage/cards/t/TeferisTutelage.java
new file mode 100644
index 0000000000..1b31489e79
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/t/TeferisTutelage.java
@@ -0,0 +1,40 @@
+package mage.cards.t;
+
+import mage.abilities.Ability;
+import mage.abilities.common.DrawCardControllerTriggeredAbility;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.effects.common.DrawDiscardControllerEffect;
+import mage.abilities.effects.common.PutLibraryIntoGraveTargetEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.target.common.TargetOpponent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class TeferisTutelage extends CardImpl {
+
+    public TeferisTutelage(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}");
+
+        // When Teferi's Tutelage enters the battlefield, draw a card, then discard a card.
+        this.addAbility(new EntersBattlefieldTriggeredAbility(new DrawDiscardControllerEffect(1, 1)));
+
+        // Whenever you draw a card, target opponent mills two cards.
+        Ability ability = new DrawCardControllerTriggeredAbility(new PutLibraryIntoGraveTargetEffect(2), false);
+        ability.addTarget(new TargetOpponent());
+        this.addAbility(ability);
+    }
+
+    private TeferisTutelage(final TeferisTutelage card) {
+        super(card);
+    }
+
+    @Override
+    public TeferisTutelage copy() {
+        return new TeferisTutelage(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 34594138f2..edf23bc174 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -70,6 +70,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Storm Caller", 335, Rarity.COMMON, mage.cards.s.StormCaller.class));
         cards.add(new SetCardInfo("Teferi's Ageless Insight", 76, Rarity.RARE, mage.cards.t.TeferisAgelessInsight.class));
         cards.add(new SetCardInfo("Teferi's Protege", 77, Rarity.COMMON, mage.cards.t.TeferisProtege.class));
+        cards.add(new SetCardInfo("Teferi's Tutelage", 78, Rarity.UNCOMMON, mage.cards.t.TeferisTutelage.class));
         cards.add(new SetCardInfo("Teferi's Wavecaster", 327, Rarity.RARE, mage.cards.t.TeferisWavecaster.class));
         cards.add(new SetCardInfo("Teferi, Master of Time", 75, Rarity.MYTHIC, mage.cards.t.TeferiMasterOfTime.class));
         cards.add(new SetCardInfo("Tormod's Crypt", 241, Rarity.UNCOMMON, mage.cards.t.TormodsCrypt.class));
diff --git a/Mage/src/main/java/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java
index 665a930257..412e1ae355 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java
@@ -65,23 +65,20 @@ public class PutLibraryIntoGraveTargetEffect extends OneShotEffect {
             sb.append("that target");
         }
 
-        sb.append(" puts the top ");
+        sb.append(" mills ");
         if (message.isEmpty()) {
             if (amount.toString().equals("1")) {
-                sb.append("card ");
+                sb.append("a card");
             } else {
-                sb.append(CardUtil.numberToText(amount.toString())).append(" cards ");
+                sb.append(CardUtil.numberToText(amount.toString())).append(" cards");
             }
         } else {
-            sb.append(" X cards ");
+            sb.append(" X cards, where X is the number of ");
         }
-        sb.append("of their library into their graveyard");
 
         if (!message.isEmpty()) {
-            sb.append(", where X is the number of ");
             sb.append(message);
         }
         return sb.toString();
     }
-
 }

From 47ccb6d07b0cabe7c48e2a9cba51a7f74a391608 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sun, 7 Jun 2020 14:57:56 -0400
Subject: [PATCH 134/586] fixed some test failures

---
 .../cards/copy/LazavDimirMastermindTest.java  | 20 +++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/LazavDimirMastermindTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/LazavDimirMastermindTest.java
index fde9b4d0c7..54dbfd5f01 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/LazavDimirMastermindTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/LazavDimirMastermindTest.java
@@ -32,7 +32,7 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase {
     public void testCopySimpleCreature() {
         addCard(Zone.BATTLEFIELD, playerA, "Lazav, Dimir Mastermind", 1);
         // Codex Shredder - Artifact
-        // {T}: Target player puts the top card of their library into their graveyard.
+        // {T}: Target player mills one card.
         // {5}, {T}, Sacrifice Codex Shredder: Return target card from your graveyard to your hand.
         addCard(Zone.BATTLEFIELD, playerA, "Codex Shredder", 1);
 
@@ -40,7 +40,7 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase {
         addCard(Zone.LIBRARY, playerB, "Assault Griffin",5);
         skipInitShuffling();
 
-        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player puts the top card of their library into their graveyard.", playerB);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills one card.", playerB);
 
         setStopAt(1, PhaseStep.END_TURN);
         execute();
@@ -67,7 +67,7 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase {
         addCard(Zone.LIBRARY, playerB, "Ogre Slumlord",5);
         skipInitShuffling();
 
-        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player puts the top card of their library into their graveyard.", playerB);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills one card.", playerB);
 
         setStopAt(1, PhaseStep.END_TURN);
         execute();
@@ -103,7 +103,7 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase {
         addCard(Zone.LIBRARY, playerB, "Nightveil Specter",1);
         skipInitShuffling();
 
-        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player puts the top card of their library into their graveyard.", playerB);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills one card.", playerB);
 
         attack(3, playerA, "Lazav, Dimir Mastermind");
 
@@ -135,10 +135,10 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase {
         skipInitShuffling();
 
         // Lazav becomes a Nightveil Specter
-        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player puts the top card of their library into their graveyard.", playerB);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills one card.", playerB);
 
         // Lazav becomes a Silvercoat Lion
-        activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player puts the top card of their library into their graveyard.", playerB);
+        activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills one card.", playerB);
 
         setStopAt(3, PhaseStep.END_TURN);
         execute();
@@ -161,7 +161,7 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase {
         addCard(Zone.HAND, playerA ,"Reanimate");        
         addCard(Zone.BATTLEFIELD, playerA, "Lazav, Dimir Mastermind", 1);
         // Codex Shredder - Artifact
-        // {T}: Target player puts the top card of their library into their graveyard.
+        // {T}: Target player mills one card.
         // {5}, {T}, Sacrifice Codex Shredder: Return target card from your graveyard to your hand.
         addCard(Zone.BATTLEFIELD, playerA, "Codex Shredder", 1);
 
@@ -173,7 +173,7 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase {
         
         skipInitShuffling();
 
-        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player puts the top card of their library into their graveyard.", playerB);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills one card.", playerB);
 
         castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Tribute to Hunger");
         
@@ -204,7 +204,7 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase {
     public void testCopyCreatureExiled() {
         addCard(Zone.BATTLEFIELD, playerA, "Lazav, Dimir Mastermind", 1);
         // Codex Shredder - Artifact
-        // {T}: Target player puts the top card of their library into their graveyard.
+        // {T}: Target player mills one card.
         // {5}, {T}, Sacrifice Codex Shredder: Return target card from your graveyard to your hand.
         addCard(Zone.BATTLEFIELD, playerA, "Codex Shredder", 1);
         
@@ -216,7 +216,7 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase {
         addCard(Zone.LIBRARY, playerB, "Assault Griffin",5);
         skipInitShuffling();
 
-        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player puts the top card of their library into their graveyard.", playerB);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills one card.", playerB);
         
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Rest in Peace");
 

From a6a91601398cfa9f7e3286962ab5d3c60f5b45da Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sun, 7 Jun 2020 15:08:22 -0400
Subject: [PATCH 135/586] fixed a fix

---
 .../cards/copy/LazavDimirMastermindTest.java  | 20 +++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/LazavDimirMastermindTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/LazavDimirMastermindTest.java
index 54dbfd5f01..c5b357266a 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/LazavDimirMastermindTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/LazavDimirMastermindTest.java
@@ -32,7 +32,7 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase {
     public void testCopySimpleCreature() {
         addCard(Zone.BATTLEFIELD, playerA, "Lazav, Dimir Mastermind", 1);
         // Codex Shredder - Artifact
-        // {T}: Target player mills one card.
+        // {T}: Target player mills a card.
         // {5}, {T}, Sacrifice Codex Shredder: Return target card from your graveyard to your hand.
         addCard(Zone.BATTLEFIELD, playerA, "Codex Shredder", 1);
 
@@ -40,7 +40,7 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase {
         addCard(Zone.LIBRARY, playerB, "Assault Griffin",5);
         skipInitShuffling();
 
-        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills one card.", playerB);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills a card.", playerB);
 
         setStopAt(1, PhaseStep.END_TURN);
         execute();
@@ -67,7 +67,7 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase {
         addCard(Zone.LIBRARY, playerB, "Ogre Slumlord",5);
         skipInitShuffling();
 
-        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills one card.", playerB);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills a card.", playerB);
 
         setStopAt(1, PhaseStep.END_TURN);
         execute();
@@ -103,7 +103,7 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase {
         addCard(Zone.LIBRARY, playerB, "Nightveil Specter",1);
         skipInitShuffling();
 
-        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills one card.", playerB);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills a card.", playerB);
 
         attack(3, playerA, "Lazav, Dimir Mastermind");
 
@@ -135,10 +135,10 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase {
         skipInitShuffling();
 
         // Lazav becomes a Nightveil Specter
-        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills one card.", playerB);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills a card.", playerB);
 
         // Lazav becomes a Silvercoat Lion
-        activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills one card.", playerB);
+        activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills a card.", playerB);
 
         setStopAt(3, PhaseStep.END_TURN);
         execute();
@@ -161,7 +161,7 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase {
         addCard(Zone.HAND, playerA ,"Reanimate");        
         addCard(Zone.BATTLEFIELD, playerA, "Lazav, Dimir Mastermind", 1);
         // Codex Shredder - Artifact
-        // {T}: Target player mills one card.
+        // {T}: Target player mills a card.
         // {5}, {T}, Sacrifice Codex Shredder: Return target card from your graveyard to your hand.
         addCard(Zone.BATTLEFIELD, playerA, "Codex Shredder", 1);
 
@@ -173,7 +173,7 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase {
         
         skipInitShuffling();
 
-        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills one card.", playerB);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills a card.", playerB);
 
         castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Tribute to Hunger");
         
@@ -204,7 +204,7 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase {
     public void testCopyCreatureExiled() {
         addCard(Zone.BATTLEFIELD, playerA, "Lazav, Dimir Mastermind", 1);
         // Codex Shredder - Artifact
-        // {T}: Target player mills one card.
+        // {T}: Target player mills a card.
         // {5}, {T}, Sacrifice Codex Shredder: Return target card from your graveyard to your hand.
         addCard(Zone.BATTLEFIELD, playerA, "Codex Shredder", 1);
         
@@ -216,7 +216,7 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase {
         addCard(Zone.LIBRARY, playerB, "Assault Griffin",5);
         skipInitShuffling();
 
-        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills one card.", playerB);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills a card.", playerB);
         
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Rest in Peace");
 

From 3b125def9c5b2548687b689251ca19121b56f49f Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sun, 7 Jun 2020 15:17:28 -0400
Subject: [PATCH 136/586] one more test fix

---
 .../org/mage/test/cards/copy/VolrathsShapshifterTest.java     | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/VolrathsShapshifterTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/VolrathsShapshifterTest.java
index c985c48493..9b3a196084 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/VolrathsShapshifterTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/VolrathsShapshifterTest.java
@@ -59,7 +59,7 @@ public class VolrathsShapshifterTest extends CardTestPlayerBase {
     public void testLosingCopy() {
         addCard(Zone.BATTLEFIELD, playerA, "Volrath's Shapeshifter", 1);
         // Codex Shredder - Artifact
-        // {T}: Target player puts the top card of their library into their graveyard.
+        // {T}: Target player mills a card.
         // {5}, {T}, Sacrifice Codex Shredder: Return target card from your graveyard to your hand.
         addCard(Zone.BATTLEFIELD, playerA, "Codex Shredder", 1);
 
@@ -68,7 +68,7 @@ public class VolrathsShapshifterTest extends CardTestPlayerBase {
         addCard(Zone.LIBRARY, playerA, "Forest", 1);
         skipInitShuffling();
 
-        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player puts the top card of their library into their graveyard.", playerA);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills a card.", playerA);
 
         setStopAt(1, PhaseStep.END_TURN);
         setStrictChooseMode(true);

From 8369184cac23fc7cfa3c05718fb34dd62d480907 Mon Sep 17 00:00:00 2001
From: htrajan <htrajan2002@yahoo.com>
Date: Sun, 7 Jun 2020 13:02:07 -0700
Subject: [PATCH 137/586] Implement more m21 cards (#6612)

* init commit

* AdherentOfHope init commit

* BasrisAegis init commit

* don't change test

* FungalRebirth init commit

* GarruksWarsteed init commit

* KeralKeepDisciples init commit

* ChromaticOrrery init commit

* add back filter names

* fix GolgariFindbroker text

* address comments

Co-authored-by: Evan Kranzler <theelk801@gmail.com>
---
 .../src/mage/cards/a/AdherentOfHope.java      | 54 ++++++++++++
 Mage.Sets/src/mage/cards/b/BasrisAegis.java   | 46 ++++++++++
 .../src/mage/cards/c/ChandrasRegulator.java   | 54 +-----------
 .../src/mage/cards/c/ChromaticOrrery.java     | 84 +++++++++++++++++++
 Mage.Sets/src/mage/cards/f/FungalRebirth.java | 46 ++++++++++
 .../src/mage/cards/g/GarruksWarsteed.java     | 50 +++++++++++
 .../src/mage/cards/g/GolgariFindbroker.java   | 16 ++--
 .../src/mage/cards/k/KeralKeepDisciples.java  | 40 +++++++++
 Mage.Sets/src/mage/cards/s/SoulOfRavnica.java | 58 +------------
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  6 ++
 ...swalkerLoyaltyAbilityTriggeredAbility.java | 61 ++++++++++++++
 ...hColorAmongControlledPermanentsEffect.java | 57 +++++++++++++
 .../main/java/mage/filter/StaticFilters.java  |  6 ++
 13 files changed, 464 insertions(+), 114 deletions(-)
 create mode 100644 Mage.Sets/src/mage/cards/a/AdherentOfHope.java
 create mode 100644 Mage.Sets/src/mage/cards/b/BasrisAegis.java
 create mode 100644 Mage.Sets/src/mage/cards/c/ChromaticOrrery.java
 create mode 100644 Mage.Sets/src/mage/cards/f/FungalRebirth.java
 create mode 100644 Mage.Sets/src/mage/cards/g/GarruksWarsteed.java
 create mode 100644 Mage.Sets/src/mage/cards/k/KeralKeepDisciples.java
 create mode 100644 Mage/src/main/java/mage/abilities/common/ActivatePlaneswalkerLoyaltyAbilityTriggeredAbility.java
 create mode 100644 Mage/src/main/java/mage/abilities/effects/common/DrawCardForEachColorAmongControlledPermanentsEffect.java

diff --git a/Mage.Sets/src/mage/cards/a/AdherentOfHope.java b/Mage.Sets/src/mage/cards/a/AdherentOfHope.java
new file mode 100644
index 0000000000..be05c2aaaf
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/a/AdherentOfHope.java
@@ -0,0 +1,54 @@
+package mage.cards.a;
+
+import mage.MageInt;
+import mage.abilities.common.BeginningOfCombatTriggeredAbility;
+import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
+import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
+import mage.abilities.effects.common.counter.AddCountersSourceEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.TargetController;
+import mage.counters.CounterType;
+import mage.filter.common.FilterControlledPermanent;
+
+import java.util.UUID;
+
+/**
+ *
+ * @author htrajan
+ */
+public final class AdherentOfHope extends CardImpl {
+
+    private static final FilterControlledPermanent filter = new FilterControlledPermanent();
+
+    static {
+        filter.add(CardType.PLANESWALKER.getPredicate());
+        filter.add(SubType.BASRI.getPredicate());
+    }
+
+    public AdherentOfHope(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}");
+        
+        this.subtype.add(SubType.HUMAN);
+        this.subtype.add(SubType.SOLDIER);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(1);
+
+        // At the beginning of combat on your turn, if you control a Basri planeswalker, put a +1/+1 counter on Adherent of Hope.
+        this.addAbility(new ConditionalInterveningIfTriggeredAbility(
+                new BeginningOfCombatTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), TargetController.YOU, false),
+                new PermanentsOnTheBattlefieldCondition(filter),
+                "At the beginning of combat on your turn, if you control a Basri planeswalker, put a +1/+1 counter on {this}."));
+    }
+
+    private AdherentOfHope(final AdherentOfHope card) {
+        super(card);
+    }
+
+    @Override
+    public AdherentOfHope copy() {
+        return new AdherentOfHope(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/cards/b/BasrisAegis.java b/Mage.Sets/src/mage/cards/b/BasrisAegis.java
new file mode 100644
index 0000000000..e0dbee48d2
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/b/BasrisAegis.java
@@ -0,0 +1,46 @@
+package mage.cards.b;
+
+import mage.abilities.effects.common.counter.AddCountersTargetEffect;
+import mage.abilities.effects.common.search.SearchLibraryGraveyardPutInHandEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.counters.CounterType;
+import mage.filter.FilterCard;
+import mage.filter.predicate.mageobject.NamePredicate;
+import mage.target.common.TargetCreaturePermanent;
+
+import java.util.UUID;
+
+/**
+ *
+ * @author htrajan
+ */
+public final class BasrisAegis extends CardImpl {
+
+    private static final FilterCard filter = new FilterCard("Basri, Devoted Paladin");
+
+    static {
+        filter.add(new NamePredicate("Basri, Devoted Paladin"));
+    }
+
+    public BasrisAegis(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{W}{W}");
+
+        // Put a +1/+1 counter on each of up to two target creatures. You may search your library and/or graveyard for a card named Basri, Devoted Paladin, reveal it, and put it into your hand. If you search your library this way, shuffle it.
+        this.getSpellAbility().addEffect(new AddCountersTargetEffect(
+                CounterType.P1P1.createInstance()
+        ).setText("Put a +1/+1 counter on each of up to two target creatures"));
+        this.getSpellAbility().addEffect(new SearchLibraryGraveyardPutInHandEffect(filter, false, true));
+        this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, 2));
+    }
+
+    private BasrisAegis(final BasrisAegis card) {
+        super(card);
+    }
+
+    @Override
+    public BasrisAegis copy() {
+        return new BasrisAegis(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/cards/c/ChandrasRegulator.java b/Mage.Sets/src/mage/cards/c/ChandrasRegulator.java
index 29a5a4b730..53010c2af0 100644
--- a/Mage.Sets/src/mage/cards/c/ChandrasRegulator.java
+++ b/Mage.Sets/src/mage/cards/c/ChandrasRegulator.java
@@ -2,14 +2,12 @@ package mage.cards.c;
 
 import mage.ObjectColor;
 import mage.abilities.Ability;
-import mage.abilities.LoyaltyAbility;
-import mage.abilities.TriggeredAbilityImpl;
+import mage.abilities.common.ActivatePlaneswalkerLoyaltyAbilityTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.common.DiscardTargetCost;
 import mage.abilities.costs.common.TapSourceCost;
 import mage.abilities.costs.mana.GenericManaCost;
 import mage.abilities.costs.mana.ManaCostsImpl;
-import mage.abilities.effects.Effect;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.cards.CardImpl;
@@ -19,7 +17,6 @@ import mage.filter.FilterCard;
 import mage.filter.predicate.Predicates;
 import mage.filter.predicate.mageobject.ColorPredicate;
 import mage.game.Game;
-import mage.game.events.GameEvent;
 import mage.game.permanent.Permanent;
 import mage.game.stack.StackAbility;
 import mage.players.Player;
@@ -47,7 +44,7 @@ public final class ChandrasRegulator extends CardImpl {
         this.addSuperType(SuperType.LEGENDARY);
 
         // Whenever you activate a loyalty ability of a Chandra planeswalker, you may pay {1}. If you do, copy that ability. You may choose new targets for the copy.
-        this.addAbility(new ChandrasRegulatorTriggeredAbility());
+        this.addAbility(new ActivatePlaneswalkerLoyaltyAbilityTriggeredAbility(new ChandrasRegulatorEffect(), SubType.CHANDRA));
 
         // {1}, {T}, Discard a Mountain card or a red card: Draw a card.
         Ability ability = new SimpleActivatedAbility(
@@ -68,56 +65,11 @@ public final class ChandrasRegulator extends CardImpl {
     }
 }
 
-class ChandrasRegulatorTriggeredAbility extends TriggeredAbilityImpl {
-
-    ChandrasRegulatorTriggeredAbility() {
-        super(Zone.BATTLEFIELD, new ChandrasRegulatorEffect(), false);
-    }
-
-    private ChandrasRegulatorTriggeredAbility(final ChandrasRegulatorTriggeredAbility ability) {
-        super(ability);
-    }
-
-    @Override
-    public ChandrasRegulatorTriggeredAbility copy() {
-        return new ChandrasRegulatorTriggeredAbility(this);
-    }
-
-    @Override
-    public boolean checkEventType(GameEvent event, Game game) {
-        return event.getType() == GameEvent.EventType.ACTIVATED_ABILITY;
-    }
-
-    @Override
-    public boolean checkTrigger(GameEvent event, Game game) {
-        if (!event.getPlayerId().equals(getControllerId())) {
-            return false;
-        }
-        StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId());
-        if (stackAbility == null || !(stackAbility.getStackAbility() instanceof LoyaltyAbility)) {
-            return false;
-        }
-        Permanent permanent = stackAbility.getSourcePermanentOrLKI(game);
-        if (permanent == null || !permanent.isPlaneswalker()
-                || !permanent.hasSubtype(SubType.CHANDRA, game)) {
-            return false;
-        }
-        Effect effect = this.getEffects().get(0);
-        effect.setValue("stackAbility", stackAbility);
-        return true;
-    }
-
-    @Override
-    public String getRule() {
-        return "Whenever you activate a loyalty ability of a Chandra planeswalker, you may pay {1}. " +
-                "If you do, copy that ability. You may choose new targets for the copy.";
-    }
-}
-
 class ChandrasRegulatorEffect extends OneShotEffect {
 
     ChandrasRegulatorEffect() {
         super(Outcome.Benefit);
+        staticText = "you may pay {1}. If you do, copy that ability. You may choose new targets for the copy";
     }
 
     private ChandrasRegulatorEffect(final ChandrasRegulatorEffect effect) {
diff --git a/Mage.Sets/src/mage/cards/c/ChromaticOrrery.java b/Mage.Sets/src/mage/cards/c/ChromaticOrrery.java
new file mode 100644
index 0000000000..d69cbdcdff
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/c/ChromaticOrrery.java
@@ -0,0 +1,84 @@
+package mage.cards.c;
+
+import mage.Mana;
+import mage.abilities.Ability;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.costs.common.TapSourceCost;
+import mage.abilities.costs.mana.GenericManaCost;
+import mage.abilities.effects.AsThoughEffectImpl;
+import mage.abilities.effects.AsThoughManaEffect;
+import mage.abilities.effects.common.DrawCardForEachColorAmongControlledPermanentsEffect;
+import mage.abilities.mana.SimpleManaAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.*;
+import mage.game.Game;
+import mage.players.ManaPoolItem;
+
+import java.util.UUID;
+
+/**
+ *
+ * @author htrajan
+ */
+public final class ChromaticOrrery extends CardImpl {
+
+    public ChromaticOrrery(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{7}");
+        
+        this.addSuperType(SuperType.LEGENDARY);
+
+        // You may spend mana as though it were mana of any color.
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ChromaticOrreryEffect()));
+
+        // {T}: Add {C}{C}{C}{C}{C}.
+        this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.ColorlessMana(5), new TapSourceCost()));
+
+        // {5}, {T}: Draw a card for each color among permanents you control.
+        Ability ability = new SimpleActivatedAbility(new DrawCardForEachColorAmongControlledPermanentsEffect(), new GenericManaCost(5));
+        ability.addCost(new TapSourceCost());
+        this.addAbility(ability);
+    }
+
+    private ChromaticOrrery(final ChromaticOrrery card) {
+        super(card);
+    }
+
+    @Override
+    public ChromaticOrrery copy() {
+        return new ChromaticOrrery(this);
+    }
+}
+
+class ChromaticOrreryEffect extends AsThoughEffectImpl implements AsThoughManaEffect {
+
+    ChromaticOrreryEffect() {
+        super(AsThoughEffectType.SPEND_OTHER_MANA, Duration.WhileOnBattlefield, Outcome.Benefit);
+        staticText = "You may spend mana as though it were mana of any color";
+    }
+
+    private ChromaticOrreryEffect(ChromaticOrreryEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public ChromaticOrreryEffect copy() {
+        return new ChromaticOrreryEffect(this);
+    }
+
+    @Override
+    public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) {
+        return true;
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        return true;
+    }
+
+    @Override
+    public ManaType getAsThoughManaType(ManaType manaType, ManaPoolItem mana, UUID affectedControllerId, Ability source, Game game) {
+        return mana.getFirstAvailable();
+    }
+}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/f/FungalRebirth.java b/Mage.Sets/src/mage/cards/f/FungalRebirth.java
new file mode 100644
index 0000000000..801d277449
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/f/FungalRebirth.java
@@ -0,0 +1,46 @@
+package mage.cards.f;
+
+import mage.abilities.condition.common.MorbidCondition;
+import mage.abilities.decorator.ConditionalOneShotEffect;
+import mage.abilities.effects.common.CreateTokenEffect;
+import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.filter.StaticFilters;
+import mage.game.permanent.token.SaprolingToken;
+import mage.target.common.TargetCardInYourGraveyard;
+import mage.watchers.common.MorbidWatcher;
+
+import java.util.UUID;
+
+/**
+ *
+ * @author htrajan
+ */
+public final class FungalRebirth extends CardImpl {
+
+    public FungalRebirth(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{G}");
+
+        // Return target permanent card from your graveyard to your hand. If a creature died this turn, create two 1/1 green Saproling creature tokens.
+        getSpellAbility().addEffect(
+                new ReturnFromGraveyardToHandTargetEffect().setText("Return target permanent card from your graveyard to your hand")
+        );
+        getSpellAbility().addWatcher(new MorbidWatcher());
+        getSpellAbility().addEffect(new ConditionalOneShotEffect(
+                new CreateTokenEffect(new SaprolingToken(), 2),
+                MorbidCondition.instance,
+                "If a creature died this turn, create two 1/1 green Saproling creature tokens"));
+        getSpellAbility().addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_PERMANENT));
+    }
+
+    private FungalRebirth(final FungalRebirth card) {
+        super(card);
+    }
+
+    @Override
+    public FungalRebirth copy() {
+        return new FungalRebirth(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/cards/g/GarruksWarsteed.java b/Mage.Sets/src/mage/cards/g/GarruksWarsteed.java
new file mode 100644
index 0000000000..8652a4ad3b
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/g/GarruksWarsteed.java
@@ -0,0 +1,50 @@
+package mage.cards.g;
+
+import mage.MageInt;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.effects.common.search.SearchLibraryGraveyardPutInHandEffect;
+import mage.abilities.keyword.VigilanceAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.filter.FilterCard;
+import mage.filter.predicate.mageobject.NamePredicate;
+
+import java.util.UUID;
+
+/**
+ *
+ * @author htrajan
+ */
+public final class GarruksWarsteed extends CardImpl {
+
+    private static final FilterCard filter = new FilterCard("Garruk, Savage Herald");
+
+    static {
+        filter.add(new NamePredicate("Garruk, Savage Herald"));
+    }
+
+    public GarruksWarsteed(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}{G}");
+        
+        this.subtype.add(SubType.RHINO);
+        this.power = new MageInt(3);
+        this.toughness = new MageInt(5);
+
+        // Vigilance
+        this.addAbility(VigilanceAbility.getInstance());
+
+        // When Garruk's Warsteed enters the battlefield, you may search your library and/or graveyard for a card named Garruk, Savage Herald, reveal it, and put it into your hand. If you search your library this way, shuffle it.
+        this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryGraveyardPutInHandEffect(filter, false, true)));
+    }
+
+    private GarruksWarsteed(final GarruksWarsteed card) {
+        super(card);
+    }
+
+    @Override
+    public GarruksWarsteed copy() {
+        return new GarruksWarsteed(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/cards/g/GolgariFindbroker.java b/Mage.Sets/src/mage/cards/g/GolgariFindbroker.java
index e95d580c61..06ee57f2aa 100644
--- a/Mage.Sets/src/mage/cards/g/GolgariFindbroker.java
+++ b/Mage.Sets/src/mage/cards/g/GolgariFindbroker.java
@@ -1,27 +1,24 @@
 package mage.cards.g;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect;
-import mage.constants.SubType;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.filter.FilterCard;
-import mage.filter.common.FilterPermanentCard;
+import mage.constants.SubType;
+import mage.filter.StaticFilters;
 import mage.target.common.TargetCardInYourGraveyard;
 
+import java.util.UUID;
+
 /**
  *
  * @author TheElk801
  */
 public final class GolgariFindbroker extends CardImpl {
 
-    private static final FilterCard filter
-            = new FilterPermanentCard("permanent card from your graveyard");
-
     public GolgariFindbroker(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}{B}{G}{G}");
 
@@ -32,9 +29,10 @@ public final class GolgariFindbroker extends CardImpl {
 
         // When Golgari Findbroker enters the battlefield, return target permanent card from your graveyard to your hand.
         Ability ability = new EntersBattlefieldTriggeredAbility(
-                new ReturnFromGraveyardToHandTargetEffect(), false
+                new ReturnFromGraveyardToHandTargetEffect().setText("return target permanent card from your graveyard to your hand"),
+                false
         );
-        ability.addTarget(new TargetCardInYourGraveyard(filter));
+        ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_PERMANENT));
         this.addAbility(ability);
     }
 
diff --git a/Mage.Sets/src/mage/cards/k/KeralKeepDisciples.java b/Mage.Sets/src/mage/cards/k/KeralKeepDisciples.java
new file mode 100644
index 0000000000..83fbcf75b1
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/k/KeralKeepDisciples.java
@@ -0,0 +1,40 @@
+package mage.cards.k;
+
+import mage.MageInt;
+import mage.abilities.common.ActivatePlaneswalkerLoyaltyAbilityTriggeredAbility;
+import mage.abilities.effects.common.DamagePlayersEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.TargetController;
+
+import java.util.UUID;
+
+/**
+ *
+ * @author htrajan
+ */
+public final class KeralKeepDisciples extends CardImpl {
+
+    public KeralKeepDisciples(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{R}");
+        
+        this.subtype.add(SubType.HUMAN);
+        this.subtype.add(SubType.MONK);
+        this.power = new MageInt(4);
+        this.toughness = new MageInt(3);
+
+        // Whenever you activate a loyalty ability of a Chandra planeswalker, Keral Keep Disciples deals 1 damage to each opponent.
+        this.addAbility(new ActivatePlaneswalkerLoyaltyAbilityTriggeredAbility(new DamagePlayersEffect(1, TargetController.OPPONENT), SubType.CHANDRA));
+    }
+
+    private KeralKeepDisciples(final KeralKeepDisciples card) {
+        super(card);
+    }
+
+    @Override
+    public KeralKeepDisciples copy() {
+        return new KeralKeepDisciples(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/cards/s/SoulOfRavnica.java b/Mage.Sets/src/mage/cards/s/SoulOfRavnica.java
index 070cac316f..e0ae6486e0 100644
--- a/Mage.Sets/src/mage/cards/s/SoulOfRavnica.java
+++ b/Mage.Sets/src/mage/cards/s/SoulOfRavnica.java
@@ -2,25 +2,18 @@
 package mage.cards.s;
 
 import mage.MageInt;
-import mage.ObjectColor;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.common.ExileSourceFromGraveCost;
 import mage.abilities.costs.mana.ManaCostsImpl;
-import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.DrawCardForEachColorAmongControlledPermanentsEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.SubType;
-import mage.constants.Outcome;
 import mage.constants.Zone;
-import mage.game.Game;
-import mage.game.permanent.Permanent;
-import mage.players.Player;
 
-import java.util.HashSet;
-import java.util.Set;
 import java.util.UUID;
 
 /**
@@ -38,11 +31,12 @@ public final class SoulOfRavnica extends CardImpl {
 
         // Flying
         this.addAbility(FlyingAbility.getInstance());
+
         // {5}{U}{U}: Draw a card for each color among permanents you control.
-        this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new SoulOfRavnicaEffect(), new ManaCostsImpl("{5}{U}{U}")));
+        this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawCardForEachColorAmongControlledPermanentsEffect(), new ManaCostsImpl("{5}{U}{U}")));
 
         // {5}{U}{U}, Exile Soul of Ravnica from your graveyard: Draw a card for each color among permanents you control.
-        Ability ability = new SimpleActivatedAbility(Zone.GRAVEYARD, new SoulOfRavnicaEffect(), new ManaCostsImpl("{5}{U}{U}"));
+        Ability ability = new SimpleActivatedAbility(Zone.GRAVEYARD, new DrawCardForEachColorAmongControlledPermanentsEffect(), new ManaCostsImpl("{5}{U}{U}"));
         ability.addCost(new ExileSourceFromGraveCost());
         this.addAbility(ability);
     }
@@ -57,47 +51,3 @@ public final class SoulOfRavnica extends CardImpl {
     }
 }
 
-class SoulOfRavnicaEffect extends OneShotEffect {
-
-    public SoulOfRavnicaEffect() {
-        super(Outcome.DrawCard);
-        this.staticText = "Draw a card for each color among permanents you control";
-    }
-
-    public SoulOfRavnicaEffect(final SoulOfRavnicaEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public SoulOfRavnicaEffect copy() {
-        return new SoulOfRavnicaEffect(this);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        Player controller = game.getPlayer(source.getControllerId());
-        if (controller != null) {
-            Set<ObjectColor> colors = new HashSet<>();
-            for (Permanent permanent : game.getBattlefield().getAllActivePermanents(controller.getId())) {
-                if (permanent.getColor(game).isBlack()) {
-                    colors.add(ObjectColor.BLACK);
-                }
-                if (permanent.getColor(game).isBlue()) {
-                    colors.add(ObjectColor.BLUE);
-                }
-                if (permanent.getColor(game).isRed()) {
-                    colors.add(ObjectColor.RED);
-                }
-                if (permanent.getColor(game).isGreen()) {
-                    colors.add(ObjectColor.GREEN);
-                }
-                if (permanent.getColor(game).isWhite()) {
-                    colors.add(ObjectColor.WHITE);
-                }
-            }
-            controller.drawCards(colors.size(), source.getSourceId(), game);
-            return true;
-        }
-        return false;
-    }
-}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index edf23bc174..b310e7e8c4 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -33,22 +33,28 @@ public final class CoreSet2021 extends ExpansionSet {
         this.ratioBoosterMythic = 8;
         this.maxCardNumberInBooster = 274;
 
+        cards.add(new SetCardInfo("Adherent of Hope", 321, Rarity.COMMON, mage.cards.a.AdherentOfHope.class));
         cards.add(new SetCardInfo("Azusa, Lost but Seeking", 173, Rarity.RARE, mage.cards.a.AzusaLostButSeeking.class));
         cards.add(new SetCardInfo("Bad Deal", 89, Rarity.UNCOMMON, mage.cards.b.BadDeal.class));
         cards.add(new SetCardInfo("Baneslayer Angel", 4, Rarity.MYTHIC, mage.cards.b.BaneslayerAngel.class));
+        cards.add(new SetCardInfo("Basri's Aegis", 322, Rarity.RARE, mage.cards.b.BasrisAegis.class));
         cards.add(new SetCardInfo("Basri, Devoted Paladin", 320, Rarity.MYTHIC, mage.cards.b.BasriDevotedPaladin.class));
         cards.add(new SetCardInfo("Chandra's Firemaw", 333, Rarity.RARE, mage.cards.c.ChandrasFiremaw.class));
         cards.add(new SetCardInfo("Chandra, Flame's Catalyst", 332, Rarity.MYTHIC, mage.cards.c.ChandraFlamesCatalyst.class));
+        cards.add(new SetCardInfo("Chromatic Orrery", 228, Rarity.MYTHIC, mage.cards.c.ChromaticOrrery.class));
         cards.add(new SetCardInfo("Containment Priest", 314, Rarity.RARE, mage.cards.c.ContainmentPriest.class));
         cards.add(new SetCardInfo("Double Vision", 142, Rarity.RARE, mage.cards.d.DoubleVision.class));
         cards.add(new SetCardInfo("Fierce Empath", 181, Rarity.UNCOMMON, mage.cards.f.FierceEmpath.class));
+        cards.add(new SetCardInfo("Fungal Rebirth", 182, Rarity.UNCOMMON, mage.cards.f.FungalRebirth.class));
         cards.add(new SetCardInfo("Gadrak, the Crown-Scourge", 146, Rarity.RARE, mage.cards.g.GadrakTheCrownScourge.class));
+        cards.add(new SetCardInfo("Garruk's Warsteed", 337, Rarity.RARE, mage.cards.g.GarruksWarsteed.class));
         cards.add(new SetCardInfo("Garruk, Savage Herald", 336, Rarity.MYTHIC, mage.cards.g.GarrukSavageHerald.class));
         cards.add(new SetCardInfo("Grim Tutor", 103, Rarity.MYTHIC, mage.cards.g.GrimTutor.class));
         cards.add(new SetCardInfo("Historian of Zhalfir", 325, Rarity.UNCOMMON, mage.cards.h.HistorianOfZhalfir.class));
         cards.add(new SetCardInfo("Indulging Patrician", 219, Rarity.UNCOMMON, mage.cards.i.IndulgingPatrician.class));
         cards.add(new SetCardInfo("Island", 310, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Jeskai Elder", 53, Rarity.UNCOMMON, mage.cards.j.JeskaiElder.class));
+        cards.add(new SetCardInfo("Keral Keep Disciples", 334, Rarity.UNCOMMON, mage.cards.k.KeralKeepDisciples.class));
         cards.add(new SetCardInfo("Liliana's Scorn", 329, Rarity.RARE, mage.cards.l.LilianasScorn.class));
         cards.add(new SetCardInfo("Liliana's Scrounger", 330, Rarity.UNCOMMON, mage.cards.l.LilianasScrounger.class));
         cards.add(new SetCardInfo("Liliana, Death Mage", 328, Rarity.MYTHIC, mage.cards.l.LilianaDeathMage.class));
diff --git a/Mage/src/main/java/mage/abilities/common/ActivatePlaneswalkerLoyaltyAbilityTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/ActivatePlaneswalkerLoyaltyAbilityTriggeredAbility.java
new file mode 100644
index 0000000000..ba4bd5c48b
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/common/ActivatePlaneswalkerLoyaltyAbilityTriggeredAbility.java
@@ -0,0 +1,61 @@
+package mage.abilities.common;
+
+import mage.abilities.LoyaltyAbility;
+import mage.abilities.TriggeredAbilityImpl;
+import mage.abilities.effects.Effect;
+import mage.constants.SubType;
+import mage.constants.Zone;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+import mage.game.permanent.Permanent;
+import mage.game.stack.StackAbility;
+
+public class ActivatePlaneswalkerLoyaltyAbilityTriggeredAbility extends TriggeredAbilityImpl {
+
+    private final SubType planeswalkerSubType;
+
+    public ActivatePlaneswalkerLoyaltyAbilityTriggeredAbility(Effect effect, SubType planeswalkerSubType) {
+        super(Zone.BATTLEFIELD, effect, false);
+        this.planeswalkerSubType = planeswalkerSubType;
+    }
+
+    private ActivatePlaneswalkerLoyaltyAbilityTriggeredAbility(final ActivatePlaneswalkerLoyaltyAbilityTriggeredAbility ability) {
+        super(ability);
+        this.planeswalkerSubType = ability.planeswalkerSubType;
+    }
+
+    @Override
+    public ActivatePlaneswalkerLoyaltyAbilityTriggeredAbility copy() {
+        return new ActivatePlaneswalkerLoyaltyAbilityTriggeredAbility(this);
+    }
+
+    @Override
+    public boolean checkEventType(GameEvent event, Game game) {
+        return event.getType() == GameEvent.EventType.ACTIVATED_ABILITY;
+    }
+
+    @Override
+    public boolean checkTrigger(GameEvent event, Game game) {
+        if (!event.getPlayerId().equals(getControllerId())) {
+            return false;
+        }
+        StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId());
+        if (stackAbility == null || !(stackAbility.getStackAbility() instanceof LoyaltyAbility)) {
+            return false;
+        }
+        Permanent permanent = stackAbility.getSourcePermanentOrLKI(game);
+        if (permanent == null || !permanent.isPlaneswalker()
+                || !permanent.hasSubtype(planeswalkerSubType, game)) {
+            return false;
+        }
+        Effect effect = this.getEffects().get(0);
+        effect.setValue("stackAbility", stackAbility);
+        return true;
+    }
+
+    @Override
+    public String getRule() {
+        return "Whenever you activate a loyalty ability of a " + planeswalkerSubType.getDescription() + " planeswalker, " +
+                this.getEffects().get(0).getText(getModes().getMode()) + ".";
+    }
+}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/DrawCardForEachColorAmongControlledPermanentsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DrawCardForEachColorAmongControlledPermanentsEffect.java
new file mode 100644
index 0000000000..951d788acb
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/effects/common/DrawCardForEachColorAmongControlledPermanentsEffect.java
@@ -0,0 +1,57 @@
+package mage.abilities.effects.common;
+
+import mage.ObjectColor;
+import mage.abilities.Ability;
+import mage.abilities.effects.OneShotEffect;
+import mage.constants.Outcome;
+import mage.game.Game;
+import mage.game.permanent.Permanent;
+import mage.players.Player;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class DrawCardForEachColorAmongControlledPermanentsEffect extends OneShotEffect {
+
+    public DrawCardForEachColorAmongControlledPermanentsEffect() {
+        super(Outcome.DrawCard);
+        this.staticText = "Draw a card for each color among permanents you control";
+    }
+
+    public DrawCardForEachColorAmongControlledPermanentsEffect(final DrawCardForEachColorAmongControlledPermanentsEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public DrawCardForEachColorAmongControlledPermanentsEffect copy() {
+        return new DrawCardForEachColorAmongControlledPermanentsEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Player controller = game.getPlayer(source.getControllerId());
+        if (controller != null) {
+            Set<ObjectColor> colors = new HashSet<>();
+            for (Permanent permanent : game.getBattlefield().getAllActivePermanents(controller.getId())) {
+                if (permanent.getColor(game).isBlack()) {
+                    colors.add(ObjectColor.BLACK);
+                }
+                if (permanent.getColor(game).isBlue()) {
+                    colors.add(ObjectColor.BLUE);
+                }
+                if (permanent.getColor(game).isRed()) {
+                    colors.add(ObjectColor.RED);
+                }
+                if (permanent.getColor(game).isGreen()) {
+                    colors.add(ObjectColor.GREEN);
+                }
+                if (permanent.getColor(game).isWhite()) {
+                    colors.add(ObjectColor.WHITE);
+                }
+            }
+            controller.drawCards(colors.size(), source.getSourceId(), game);
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/Mage/src/main/java/mage/filter/StaticFilters.java b/Mage/src/main/java/mage/filter/StaticFilters.java
index 2e10475094..2a0dc193e1 100644
--- a/Mage/src/main/java/mage/filter/StaticFilters.java
+++ b/Mage/src/main/java/mage/filter/StaticFilters.java
@@ -191,6 +191,12 @@ public final class StaticFilters {
         FILTER_CARD_INSTANT_AND_SORCERY.setLockedFilter(true);
     }
 
+    public static final FilterPermanentCard FILTER_CARD_PERMANENT = new FilterPermanentCard("permanent card");
+
+    static {
+        FILTER_CARD_PERMANENT.setLockedFilter(true);
+    }
+
     public static final FilterPermanent FILTER_PERMANENT = new FilterPermanent();
 
     static {

From bc9da1d11ff0f730c6344f35b78c4589e3802d31 Mon Sep 17 00:00:00 2001
From: Eric Nelson <1895280+Grath@users.noreply.github.com>
Date: Sun, 7 Jun 2020 17:52:38 -0400
Subject: [PATCH 138/586] Text correction on UntapLandsEffect

UntapLandsEffect asks you to choose untapped lands to untap; by changing the fillter from "untapped lands" to "tapped lands" (technically you can choose already untapped lands but...) it will now ask you to choose tapped lands to untap.
---
 .../java/mage/abilities/effects/common/UntapLandsEffect.java    | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Mage/src/main/java/mage/abilities/effects/common/UntapLandsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/UntapLandsEffect.java
index f1ab28e27a..490973b6d0 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/UntapLandsEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/UntapLandsEffect.java
@@ -17,7 +17,7 @@ import mage.util.CardUtil;
  */
 public class UntapLandsEffect extends OneShotEffect {
 
-    private static final FilterLandPermanent filter = new FilterLandPermanent("untapped lands");
+    private static final FilterLandPermanent filter = new FilterLandPermanent("tapped lands");
 
     static {
         filter.add(TappedPredicate.instance);

From d0de282d2db06bd03c09b2108487f198ff443933 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sun, 7 Jun 2020 19:09:59 -0400
Subject: [PATCH 139/586] updated M21 spoiler

---
 Mage.Sets/src/mage/cards/r/RinAndSeriInseparable.java | 7 ++++---
 Mage.Sets/src/mage/sets/CoreSet2021.java              | 2 +-
 Utils/mtg-cards-data.txt                              | 5 +++--
 3 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/r/RinAndSeriInseparable.java b/Mage.Sets/src/mage/cards/r/RinAndSeriInseparable.java
index 21789c8869..10266f3289 100644
--- a/Mage.Sets/src/mage/cards/r/RinAndSeriInseparable.java
+++ b/Mage.Sets/src/mage/cards/r/RinAndSeriInseparable.java
@@ -4,6 +4,7 @@ import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.common.SpellCastControllerTriggeredAbility;
+import mage.abilities.costs.common.TapSourceCost;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.dynamicvalue.DynamicValue;
 import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
@@ -27,7 +28,6 @@ import mage.target.common.TargetAnyTarget;
 import java.util.UUID;
 
 /**
- *
  * @author htrajan
  */
 public final class RinAndSeriInseparable extends CardImpl {
@@ -48,7 +48,7 @@ public final class RinAndSeriInseparable extends CardImpl {
 
     public RinAndSeriInseparable(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}{G}{W}");
-        
+
         this.addSuperType(SuperType.LEGENDARY);
         this.subtype.add(SubType.DOG);
         this.subtype.add(SubType.CAT);
@@ -65,7 +65,7 @@ public final class RinAndSeriInseparable extends CardImpl {
                 new CreateTokenEffect(new WhiteDogToken()), catSpellFilter, false
         ));
 
-        // {R}{G}{W}: Rin and Seri, Inseparable deals damage to any target equal to the number of Dogs you control. You gain life equal to the number of Cats you control.
+        // {R}{G}{W}, {T}: Rin and Seri, Inseparable deals damage to any target equal to the number of Dogs you control. You gain life equal to the number of Cats you control.
         DynamicValue dogCount = new PermanentsOnBattlefieldCount(dogPermanentFilter);
         Effect damageEffect = new DamageTargetEffect(dogCount);
         damageEffect.setText("{source} deals damage to any target equal to the number of Dogs you control");
@@ -74,6 +74,7 @@ public final class RinAndSeriInseparable extends CardImpl {
         lifeGainEffect.setText("You gain life equal to the number of Cats you control");
         Ability ability = new SimpleActivatedAbility(damageEffect, new ManaCostsImpl("{R}{G}{W}"));
         ability.addEffect(lifeGainEffect);
+        ability.addCost(new TapSourceCost());
         ability.addTarget(new TargetAnyTarget());
         ability.addHint(new ValueHint("Dogs you control", dogCount));
         ability.addHint(new ValueHint("Cats you control", catCount));
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index b310e7e8c4..ec1c53df6e 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -36,7 +36,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Adherent of Hope", 321, Rarity.COMMON, mage.cards.a.AdherentOfHope.class));
         cards.add(new SetCardInfo("Azusa, Lost but Seeking", 173, Rarity.RARE, mage.cards.a.AzusaLostButSeeking.class));
         cards.add(new SetCardInfo("Bad Deal", 89, Rarity.UNCOMMON, mage.cards.b.BadDeal.class));
-        cards.add(new SetCardInfo("Baneslayer Angel", 4, Rarity.MYTHIC, mage.cards.b.BaneslayerAngel.class));
+        cards.add(new SetCardInfo("Baneslayer Angel", 6, Rarity.MYTHIC, mage.cards.b.BaneslayerAngel.class));
         cards.add(new SetCardInfo("Basri's Aegis", 322, Rarity.RARE, mage.cards.b.BasrisAegis.class));
         cards.add(new SetCardInfo("Basri, Devoted Paladin", 320, Rarity.MYTHIC, mage.cards.b.BasriDevotedPaladin.class));
         cards.add(new SetCardInfo("Chandra's Firemaw", 333, Rarity.RARE, mage.cards.c.ChandrasFiremaw.class));
diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt
index 63fcff2db8..d8bd91caf3 100644
--- a/Utils/mtg-cards-data.txt
+++ b/Utils/mtg-cards-data.txt
@@ -37439,7 +37439,7 @@ Mysterious Egg|Ikoria: Lair of Behemoths|385|C|{1}|Creature - Egg|0|2|Whenever t
 Dirge Bat|Ikoria: Lair of Behemoths|386|R|{2}{B}{B}|Creature - Bat|3|3|Mutate {4}{B}{B}$Flash$Flying$Whenever this creature mutates, destroy target creature or planeswalker an opponent controls.|
 Crystalline Giant|Ikoria: Lair of Behemoths|387|R|{3}|Artifact Creature - Giant|3|3|At the beginning of combat on your turn, choose a kind of counter at random that Crystalline Giant doesn't have on it from among flying, first strike, deathtouch, hexproof, lifelink, menace, reach, trample, vigilance, or +1/+1. Put a counter of that kind on Crystalline Giant.|
 Ugin, the Spirit Dragon|Core Set 2021|1|M|{8}|Legendary Planeswalker - Ugin|7|+2: Ugin, the Spirit Dragon deals 3 damage to any target.$−X: Exile each permanent with converted mana cost X or less that's one or more colors.$−10: You gain 7 life, draw seven cards, then put up to seven permanent cards from your hand onto the battlefield.|
-Baneslayer Angel|Core Set 2021|4|M|{3}{W}{W}|Creature - Angel|5|5|Flying, first strike, lifelink, protection from Demons and from Dragons|
+Baneslayer Angel|Core Set 2021|6|M|{3}{W}{W}|Creature - Angel|5|5|Flying, first strike, lifelink, protection from Demons and from Dragons|
 Mangara, the Diplomat|Core Set 2021|27|M|{3}{W}|Legendary Creature - Human Cleric|2|4|Lifelink$Whenever an opponent attacks with creatures, if two or more of those creatures are attacking you and/or a planeswalker you control, draw a card.$Whenever an opponent casts their second spell each turn, draw a card.|
 Pack Leader|Core Set 2021|29|R|{1}{W}|Creature - Dog|2|2|Other Dogs you control get +1/+1.$Whenever Pack Leader attacks, prevent all combat damage that would be dealt this turn to Dogs you control.|
 Runed Halo|Core Set 2021|32|R|{W}{W}|Enchantment|||As Runed Halo enters the battlefield, choose a card name.$You have protection from the chosen name.|
@@ -37452,6 +37452,7 @@ Teferi's Ageless Insight|Core Set 2021|76|R|{2}{U}{U}|Legendary Enchantment|||If
 Teferi's Protege|Core Set 2021|77|C|{2}{U}|Creature - Human Wizard|2|3|{1}{U}, {T}: Draw a card, then discard a card.|
 Teferi's Tutelage|Core Set 2021|78|U|{2}{U}|Enchantment|||When Teferi's Tutelage enters the battlefield, draw a card, then discard a card.$Whenever you draw a card, target opponent mills two cards.|
 Bad Deal|Core Set 2021|89|U|{4}{B}{B}|Sorcery|||You draw two cards and each opponent discards two cards. Each player loses 2 life.|
+Carrion Grub|Core Set 2021|92|U|{3}{B}|Creature - Insect|0|5|Carrion Grub gets +X/+0, where X is the greatest power among creature cards in your graveyard.$When Carrion Grub enters the battlefield, mill four cards.|
 Grim Tutor|Core Set 2021|103|M|{1}{B}{B}|Sorcery|||Search your library for a card and put that card into your hand, then shuffle your library. You lose 3 life.|
 Liliana, Waker of the Dead|Core Set 2021|108|M|{2}{B}{B}|Legendary Planeswalker - Liliana|4|+1: Each player discards a card. Each opponent who can't loses 3 life.$−3: Target creature gets -X/-X until end of turn, where X is the number of cards in your graveyard.$−7: You get an emblem with "At the beginning of combat on your turn, put target creature card from a graveyard onto the battlefield under your control. It gains haste."|
 Necromentia|Core Set 2021|116|R|{1}{B}{B}|Sorcery|||Choose a card name other than a basic land card name. Search target opponent's graveyard, hand, and library for any number of cards with that name and exile them. That player shuffles their library, then creates a 2/2 black Zombie creature token for each card exiled from their hand this way.|
@@ -37469,7 +37470,7 @@ Indulging Patrician|Core Set 2021|219|U|{1}{W}{B}|Creature - Vampire Noble|1|4|F
 Chromatic Orrery|Core Set 2021|228|M|{7}|Legendary Artifact|||You may spend mana as though it were mana of any color.${T}: Add {C}{C}{C}{C}{C}.${5}, {T}: Draw a card for each color among permanents you control.|
 Sparkhunter Masticore|Core Set 2021|240|R|{3}|Artifact Creature - Masticore|3|4|As an additional cost to cast this spell, discard a card.$Protection from planeswalkers${1}: Sparkhunter Masticore deals 1 damage to target planeswalker.${3}: Sparkhunter Masticore gains indestructible until end of turn.|
 Tormod's Crypt|Core Set 2021|241|U|{0}|Artifact|||{T}, Sacrifice Tormod's Crypt: Exile all cards from target player's graveyard.|
-Rin and Seri, Inseparable|Core Set 2021|278|M|{1}{R}{G}{W}|Legendary Creature - Dog Cat|4|4|Whenever you cast a Dog spell, create a 1/1 green Cat creature token.$Whenever you cast a Cat spell, create a 1/1 white Dog creature token.${R}{G}{W}: Rin and Seri, Inseparable deals damage to any target equal to the number of Dogs you control. You gain life equal to the number of Cats you control.|
+Rin and Seri, Inseparable|Core Set 2021|278|M|{1}{R}{G}{W}|Legendary Creature - Dog Cat|4|4|Whenever you cast a Dog spell, create a 1/1 green Cat creature token.$Whenever you cast a Cat spell, create a 1/1 white Dog creature token.${R}{G}{W}, {T}: Rin and Seri, Inseparable deals damage to any target equal to the number of Dogs you control. You gain life equal to the number of Cats you control.|
 Island|Core Set 2021|310|C||Basic Land - Island|||({T}: Add {U}.)|
 Containment Priest|Core Set 2021|314|R|{1}{W}|Creature - Human Cleric|2|2|Flash$If a nontoken creature would enter the battlefield and it wasn't cast, exile it instead.|
 Basri, Devoted Paladin|Core Set 2021|320|M|{4}{W}{W}|Legendary Planeswalker - Basri|4|+1: Put a +1/+1 counter on up to one target creature. It gains vigilance until end of turn.$−1: Whenever a creature attacks this turn, put a +1/+1 counter on it.$−6: Creatures you control get +2/+2 and gain flying until end of turn.|

From c4283cd59146f7be0bd17069b6caf2b8d844e982 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sun, 7 Jun 2020 19:21:19 -0400
Subject: [PATCH 140/586] Implemented Carrion Grub

---
 Mage.Sets/src/mage/cards/c/CarrionGrub.java   | 89 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 ...ardOfLibraryIntoGraveControllerEffect.java | 11 +--
 3 files changed, 91 insertions(+), 10 deletions(-)
 create mode 100644 Mage.Sets/src/mage/cards/c/CarrionGrub.java

diff --git a/Mage.Sets/src/mage/cards/c/CarrionGrub.java b/Mage.Sets/src/mage/cards/c/CarrionGrub.java
new file mode 100644
index 0000000000..b0f52fc791
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/c/CarrionGrub.java
@@ -0,0 +1,89 @@
+package mage.cards.c;
+
+import mage.MageInt;
+import mage.MageObject;
+import mage.abilities.Ability;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.dynamicvalue.DynamicValue;
+import mage.abilities.dynamicvalue.common.StaticValue;
+import mage.abilities.effects.Effect;
+import mage.abilities.effects.common.PutTopCardOfLibraryIntoGraveControllerEffect;
+import mage.abilities.effects.common.continuous.BoostSourceEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+import mage.game.Game;
+import mage.players.Player;
+
+import java.util.Objects;
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class CarrionGrub extends CardImpl {
+
+    public CarrionGrub(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}");
+
+        this.subtype.add(SubType.INSECT);
+        this.power = new MageInt(0);
+        this.toughness = new MageInt(5);
+
+        // Carrion Grub gets +X/+0, where X is the greatest power among creature cards in your graveyard.
+        this.addAbility(new SimpleStaticAbility(new BoostSourceEffect(
+                CarrionGrubValue.instance, StaticValue.get(0), Duration.WhileOnBattlefield
+        )));
+
+        // When Carrion Grub enters the battlefield, mill four cards.
+        this.addAbility(new EntersBattlefieldTriggeredAbility(new PutTopCardOfLibraryIntoGraveControllerEffect(4)));
+    }
+
+    private CarrionGrub(final CarrionGrub card) {
+        super(card);
+    }
+
+    @Override
+    public CarrionGrub copy() {
+        return new CarrionGrub(this);
+    }
+}
+
+enum CarrionGrubValue implements DynamicValue {
+    instance;
+
+    @Override
+    public int calculate(Game game, Ability sourceAbility, Effect effect) {
+        Player player = game.getPlayer(sourceAbility.getControllerId());
+        if (player == null) {
+            return 0;
+        }
+        return player.getGraveyard()
+                .getCards(game)
+                .stream()
+                .filter(Objects::nonNull)
+                .filter(MageObject::isCreature)
+                .map(MageObject::getPower)
+                .mapToInt(MageInt::getValue)
+                .max()
+                .orElse(0);
+    }
+
+    @Override
+    public CarrionGrubValue copy() {
+        return instance;
+    }
+
+    @Override
+    public String getMessage() {
+        return "the greatest power among creature cards in your graveyard";
+    }
+
+    @Override
+    public String toString() {
+        return "X";
+    }
+}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index ec1c53df6e..59484cab56 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -39,6 +39,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Baneslayer Angel", 6, Rarity.MYTHIC, mage.cards.b.BaneslayerAngel.class));
         cards.add(new SetCardInfo("Basri's Aegis", 322, Rarity.RARE, mage.cards.b.BasrisAegis.class));
         cards.add(new SetCardInfo("Basri, Devoted Paladin", 320, Rarity.MYTHIC, mage.cards.b.BasriDevotedPaladin.class));
+        cards.add(new SetCardInfo("Carrion Grub", 92, Rarity.UNCOMMON, mage.cards.c.CarrionGrub.class));
         cards.add(new SetCardInfo("Chandra's Firemaw", 333, Rarity.RARE, mage.cards.c.ChandrasFiremaw.class));
         cards.add(new SetCardInfo("Chandra, Flame's Catalyst", 332, Rarity.MYTHIC, mage.cards.c.ChandraFlamesCatalyst.class));
         cards.add(new SetCardInfo("Chromatic Orrery", 228, Rarity.MYTHIC, mage.cards.c.ChromaticOrrery.class));
diff --git a/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveControllerEffect.java
index 08f6763ab8..b944fba65b 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveControllerEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveControllerEffect.java
@@ -42,15 +42,6 @@ public class PutTopCardOfLibraryIntoGraveControllerEffect extends OneShotEffect
     }
 
     private String setText() {
-        StringBuilder sb = new StringBuilder("put the top");
-        if (numberCards == 1) {
-            sb.append(" card");
-        } else {
-            sb.append(" ");
-            sb.append(CardUtil.numberToText(numberCards));
-            sb.append(" cards");
-        }
-        sb.append(" of your library into your graveyard");
-        return sb.toString();
+        return "mill " + (numberCards == 1 ? "a card" : CardUtil.numberToText(numberCards) + " cards");
     }
 }

From 0590243d6defb1765d669b1e2a4853e4c8306581 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sun, 7 Jun 2020 19:29:00 -0400
Subject: [PATCH 141/586] updated some more of the mill templating

---
 ...utTopCardOfYourLibraryToGraveyardCost.java | 19 ++++++-------------
 ...ardOfLibraryIntoGraveEachPlayerEffect.java | 14 ++++++--------
 2 files changed, 12 insertions(+), 21 deletions(-)

diff --git a/Mage/src/main/java/mage/abilities/costs/common/PutTopCardOfYourLibraryToGraveyardCost.java b/Mage/src/main/java/mage/abilities/costs/common/PutTopCardOfYourLibraryToGraveyardCost.java
index 232961e449..ecefd2df46 100644
--- a/Mage/src/main/java/mage/abilities/costs/common/PutTopCardOfYourLibraryToGraveyardCost.java
+++ b/Mage/src/main/java/mage/abilities/costs/common/PutTopCardOfYourLibraryToGraveyardCost.java
@@ -1,9 +1,5 @@
-
 package mage.abilities.costs.common;
 
-import java.util.LinkedHashSet;
-import java.util.Set;
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.costs.Cost;
 import mage.abilities.costs.CostImpl;
@@ -13,6 +9,10 @@ import mage.game.Game;
 import mage.players.Player;
 import mage.util.CardUtil;
 
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.util.UUID;
+
 public class PutTopCardOfYourLibraryToGraveyardCost extends CostImpl {
 
     private final int numberOfCards;
@@ -27,7 +27,7 @@ public class PutTopCardOfYourLibraryToGraveyardCost extends CostImpl {
         this.text = setText();
     }
 
-    public PutTopCardOfYourLibraryToGraveyardCost(PutTopCardOfYourLibraryToGraveyardCost cost) {
+    private PutTopCardOfYourLibraryToGraveyardCost(final PutTopCardOfYourLibraryToGraveyardCost cost) {
         super(cost);
         this.numberOfCards = cost.numberOfCards;
         this.cardsMovedToGraveyard.addAll(cost.getCardsMovedToGraveyard());
@@ -60,13 +60,6 @@ public class PutTopCardOfYourLibraryToGraveyardCost extends CostImpl {
     }
 
     private String setText() {
-        StringBuilder sb = new StringBuilder("Put the top ");
-        if (numberOfCards == 1) {
-            sb.append("card");
-        } else {
-            sb.append(CardUtil.numberToText(numberOfCards)).append(" cards");
-        }
-        sb.append(" of your library into your graveyard");
-        return sb.toString();
+        return "mill " + (numberOfCards == 1 ? "a card" : CardUtil.numberToText(numberOfCards) + " cards");
     }
 }
diff --git a/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveEachPlayerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveEachPlayerEffect.java
index 9cd5fa8702..0809aedfae 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveEachPlayerEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveEachPlayerEffect.java
@@ -1,7 +1,5 @@
-
 package mage.abilities.effects.common;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.dynamicvalue.DynamicValue;
 import mage.abilities.dynamicvalue.common.StaticValue;
@@ -13,8 +11,9 @@ import mage.game.Game;
 import mage.players.Player;
 import mage.util.CardUtil;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public class PutTopCardOfLibraryIntoGraveEachPlayerEffect extends OneShotEffect {
@@ -33,7 +32,7 @@ public class PutTopCardOfLibraryIntoGraveEachPlayerEffect extends OneShotEffect
         this.staticText = setText();
     }
 
-    public PutTopCardOfLibraryIntoGraveEachPlayerEffect(final PutTopCardOfLibraryIntoGraveEachPlayerEffect effect) {
+    private PutTopCardOfLibraryIntoGraveEachPlayerEffect(final PutTopCardOfLibraryIntoGraveEachPlayerEffect effect) {
         super(effect);
         this.numberCards = effect.numberCards;
         this.targetController = effect.targetController;
@@ -96,14 +95,13 @@ public class PutTopCardOfLibraryIntoGraveEachPlayerEffect extends OneShotEffect
             default:
                 throw new UnsupportedOperationException("TargetController type not supported.");
         }
-        sb.append("puts the top ");
-        if(numberCards.toString().equals("1")) {
-            sb.append("card");
+        sb.append("mills ");
+        if (numberCards.toString().equals("1")) {
+            sb.append("a card");
         } else {
             sb.append(CardUtil.numberToText(numberCards.toString()));
             sb.append(" cards");
         }
-        sb.append(" of their library into their graveyard");
         return sb.toString();
     }
 }

From 64625654f4d8da26ebb37373699a3b5a8feb2040 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sun, 7 Jun 2020 19:37:41 -0400
Subject: [PATCH 142/586] fixed another test failure

---
 .../java/org/mage/test/cards/abilities/curses/CursesTest.java   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/curses/CursesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/curses/CursesTest.java
index a7dffb1a1d..5fc29b5f3d 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/curses/CursesTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/curses/CursesTest.java
@@ -283,7 +283,7 @@ public class CursesTest extends CardTestPlayerBase {
         addTarget(playerB, "Curse of Death's Hold");
         
         // {2}{G/U}{G/U}: Put the top two cards of your library into your graveyard, then return a nonland card of an opponent's choice from your graveyard to your hand.
-        activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{G/U}{G/U}: Put the top two cards");
+        activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{G/U}{G/U}: Mill two cards");
         castSpell(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Curse of Death's Hold", playerB);
                
         setStopAt(3, PhaseStep.END_TURN);

From 5185d71638dca39d5452a9210618268800659039 Mon Sep 17 00:00:00 2001
From: htrajan <htrajan2002@yahoo.com>
Date: Sun, 7 Jun 2020 18:16:53 -0700
Subject: [PATCH 143/586] Implement liliana4 and necromentia (#6616)

* LilianaWakerOfTheDead init commit

* Necromentia init commit

* benefit outcome

* fix text

* fix Liliana

* don't change VerifyCardDataTest

* use hasOpponent

* add hint and fix name chooser

* use positive # for hint

* don't change test
---
 Mage.Sets/src/mage/cards/g/GraveUpheaval.java |  56 +-------
 .../mage/cards/l/LilianaWakerOfTheDead.java   | 119 +++++++++++++++++
 Mage.Sets/src/mage/cards/n/Necromentia.java   | 122 ++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |   2 +
 ...veyardToBattlefieldAndGainHasteEffect.java |  53 ++++++++
 .../emblems/LilianaWakerOfTheDeadEmblem.java  |  30 +++++
 6 files changed, 330 insertions(+), 52 deletions(-)
 create mode 100644 Mage.Sets/src/mage/cards/l/LilianaWakerOfTheDead.java
 create mode 100644 Mage.Sets/src/mage/cards/n/Necromentia.java
 create mode 100644 Mage/src/main/java/mage/abilities/effects/common/ReturnCreatureFromGraveyardToBattlefieldAndGainHasteEffect.java
 create mode 100644 Mage/src/main/java/mage/game/command/emblems/LilianaWakerOfTheDeadEmblem.java

diff --git a/Mage.Sets/src/mage/cards/g/GraveUpheaval.java b/Mage.Sets/src/mage/cards/g/GraveUpheaval.java
index f75d9f3789..3afdacc9c7 100644
--- a/Mage.Sets/src/mage/cards/g/GraveUpheaval.java
+++ b/Mage.Sets/src/mage/cards/g/GraveUpheaval.java
@@ -1,26 +1,15 @@
 package mage.cards.g;
 
-import java.util.UUID;
-import mage.abilities.Ability;
 import mage.abilities.costs.mana.ManaCostsImpl;
-import mage.abilities.effects.ContinuousEffect;
-import mage.abilities.effects.OneShotEffect;
-import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
+import mage.abilities.effects.common.ReturnCreatureFromGraveyardToBattlefieldAndGainHasteEffect;
 import mage.abilities.keyword.BasicLandcyclingAbility;
-import mage.abilities.keyword.HasteAbility;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.Duration;
-import mage.constants.Outcome;
-import mage.constants.Zone;
 import mage.filter.StaticFilters;
-import mage.game.Game;
-import mage.game.permanent.Permanent;
-import mage.players.Player;
 import mage.target.common.TargetCardInGraveyard;
-import mage.target.targetpointer.FixedTarget;
+
+import java.util.UUID;
 
 /**
  *
@@ -32,7 +21,7 @@ public final class GraveUpheaval extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{B}{R}");
 
         // Put target creature card from a graveyard onto the battlefield under your control. It gains haste.
-        this.getSpellAbility().addEffect(new GraveUpheavalEffect());
+        this.getSpellAbility().addEffect(new ReturnCreatureFromGraveyardToBattlefieldAndGainHasteEffect());
         this.getSpellAbility().addTarget(new TargetCardInGraveyard(StaticFilters.FILTER_CARD_CREATURE));
 
         // Basic landcycling {2}
@@ -49,40 +38,3 @@ public final class GraveUpheaval extends CardImpl {
     }
 }
 
-class GraveUpheavalEffect extends OneShotEffect {
-
-    public GraveUpheavalEffect() {
-        super(Outcome.PutCreatureInPlay);
-        this.staticText = "Put target creature card from a graveyard onto the battlefield under your control. It gains haste";
-    }
-
-    public GraveUpheavalEffect(final GraveUpheavalEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public GraveUpheavalEffect copy() {
-        return new GraveUpheavalEffect(this);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        Player controller = game.getPlayer(source.getControllerId());
-        if (controller == null) {
-            return false;
-        }
-        Card card = game.getCard(source.getFirstTarget());
-        if (card != null) {
-            controller.moveCards(card, Zone.BATTLEFIELD, source, game);
-            Permanent permanent = game.getPermanent(card.getId());
-            if (permanent != null) {
-                ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.Custom);
-                effect.setTargetPointer(new FixedTarget(permanent, game));
-                game.addEffect(effect, source);
-            }
-            return true;
-        }
-
-        return false;
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/l/LilianaWakerOfTheDead.java b/Mage.Sets/src/mage/cards/l/LilianaWakerOfTheDead.java
new file mode 100644
index 0000000000..56a8048702
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/l/LilianaWakerOfTheDead.java
@@ -0,0 +1,119 @@
+package mage.cards.l;
+
+import mage.abilities.Ability;
+import mage.abilities.LoyaltyAbility;
+import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
+import mage.abilities.dynamicvalue.DynamicValue;
+import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.GetEmblemEffect;
+import mage.abilities.effects.common.continuous.BoostTargetEffect;
+import mage.abilities.hint.ValueHint;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.cards.Cards;
+import mage.cards.CardsImpl;
+import mage.constants.*;
+import mage.filter.StaticFilters;
+import mage.game.Game;
+import mage.game.command.emblems.LilianaWakerOfTheDeadEmblem;
+import mage.players.Player;
+import mage.target.Target;
+import mage.target.common.TargetCreaturePermanent;
+import mage.target.common.TargetDiscard;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ *
+ * @author htrajan
+ */
+public final class LilianaWakerOfTheDead extends CardImpl {
+
+    private static final DynamicValue xValue = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD, -1);
+    private static final DynamicValue xValue_hint = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD);
+
+    public LilianaWakerOfTheDead(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{2}{B}{B}");
+        
+        this.addSuperType(SuperType.LEGENDARY);
+        this.subtype.add(SubType.LILIANA);
+        this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4));
+
+        // +1: Each player discards a card. Each opponent who can't loses 3 life.
+        this.addAbility(new LoyaltyAbility(new LilianaWakerOfTheDeadDiscardEffect(), 1));
+
+        // −3: Target creature gets -X/-X until end of turn, where X is the number of cards in your graveyard.
+        Ability ability = new LoyaltyAbility(new BoostTargetEffect(
+                xValue, xValue, Duration.EndOfTurn, true
+        ).setText("target creature gets -X/-X until end of turn, where X is the number of cards in your graveyard"), -3)
+        .addHint(new ValueHint("Cards in your graveyard", xValue_hint));
+        ability.addTarget(new TargetCreaturePermanent());
+        this.addAbility(ability);
+
+        // −7: You get an emblem with "At the beginning of combat on your turn, put target creature card from a graveyard onto the battlefield under your control. It gains haste."
+        this.addAbility(new LoyaltyAbility(new GetEmblemEffect(new LilianaWakerOfTheDeadEmblem()), -7));
+    }
+
+    private LilianaWakerOfTheDead(final LilianaWakerOfTheDead card) {
+        super(card);
+    }
+
+    @Override
+    public LilianaWakerOfTheDead copy() {
+        return new LilianaWakerOfTheDead(this);
+    }
+}
+
+class LilianaWakerOfTheDeadDiscardEffect extends OneShotEffect {
+
+    LilianaWakerOfTheDeadDiscardEffect() {
+        super(Outcome.Discard);
+        staticText = "Each player discards a card. Each opponent who can't loses 3 life";
+    }
+
+    private LilianaWakerOfTheDeadDiscardEffect(LilianaWakerOfTheDeadDiscardEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public LilianaWakerOfTheDeadDiscardEffect copy() {
+        return new LilianaWakerOfTheDeadDiscardEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Player controller = game.getPlayer(source.getControllerId());
+        Map<UUID, Cards> cardsToDiscard = new HashMap<>();
+        if (controller == null) {
+            return true;
+        }
+        // choose cards to discard
+        for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
+            Player player = game.getPlayer(playerId);
+            if (player == null) {
+                continue;
+            }
+            int numberOfCardsToDiscard = Math.min(1, player.getHand().size());
+            Cards cards = new CardsImpl();
+            Target target = new TargetDiscard(numberOfCardsToDiscard, numberOfCardsToDiscard, StaticFilters.FILTER_CARD, playerId);
+            player.chooseTarget(outcome, target, source, game);
+            cards.addAll(target.getTargets());
+            cardsToDiscard.put(playerId, cards);
+        }
+        // discard all choosen cards
+        for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
+            Player player = game.getPlayer(playerId);
+            if (player == null) {
+                continue;
+            }
+            int amountDiscarded = player.discard(cardsToDiscard.get(playerId), source, game).size();
+            if (controller.hasOpponent(playerId, game) && amountDiscarded == 0) {
+                player.loseLife(3, game, false);
+            }
+        }
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/n/Necromentia.java b/Mage.Sets/src/mage/cards/n/Necromentia.java
new file mode 100644
index 0000000000..985e0fc22e
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/n/Necromentia.java
@@ -0,0 +1,122 @@
+package mage.cards.n;
+
+import mage.abilities.Ability;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.ChooseACardNameEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.cards.Cards;
+import mage.cards.CardsImpl;
+import mage.constants.CardType;
+import mage.constants.Outcome;
+import mage.constants.Zone;
+import mage.filter.FilterCard;
+import mage.filter.predicate.mageobject.NamePredicate;
+import mage.game.Game;
+import mage.game.permanent.token.Token;
+import mage.game.permanent.token.ZombieToken;
+import mage.players.Player;
+import mage.target.TargetCard;
+import mage.target.common.TargetCardInLibrary;
+import mage.target.common.TargetOpponent;
+
+import java.util.UUID;
+
+/**
+ *
+ * @author htrajan
+ */
+public final class Necromentia extends CardImpl {
+
+    public Necromentia(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}{B}");
+
+        // Choose a card name other than a basic land card name. Search target opponent's graveyard, hand, and library for any number of cards with that name and exile them. That player shuffles their library, then creates a 2/2 black Zombie creature token for each card exiled from their hand this way.
+        this.getSpellAbility().addEffect(
+                new ChooseACardNameEffect(ChooseACardNameEffect.TypeOfName.NOT_BASIC_LAND_NAME)
+        );
+        this.getSpellAbility().addEffect(new NecromentiaEffect());
+        this.getSpellAbility().addTarget(new TargetOpponent());
+    }
+
+    private Necromentia(final Necromentia card) {
+        super(card);
+    }
+
+    @Override
+    public Necromentia copy() {
+        return new Necromentia(this);
+    }
+}
+
+class NecromentiaEffect extends OneShotEffect {
+
+    NecromentiaEffect() {
+        super(Outcome.Benefit);
+        staticText = "Search target opponent's graveyard, hand, and library for any number of cards with that name and exile them. That player shuffles their library, then creates a 2/2 black Zombie creature token for each card exiled from their hand this way";
+    }
+
+    private NecromentiaEffect(NecromentiaEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public NecromentiaEffect copy() {
+        return new NecromentiaEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY);
+        Player controller = game.getPlayer(source.getControllerId());
+        if (cardName != null && controller != null) {
+            FilterCard filter = new FilterCard("card named " + cardName);
+            filter.add(new NamePredicate(cardName));
+            Player targetPlayer = game.getPlayer(getTargetPointer().getFirst(game, source));
+
+            // cards in Graveyard
+            int cardsCount = (cardName.isEmpty() ? 0 : targetPlayer.getGraveyard().count(filter, game));
+            if (cardsCount > 0) {
+                filter.setMessage("card named " + cardName + " in the graveyard of " + targetPlayer.getName());
+                TargetCard target = new TargetCard(0, cardsCount, Zone.GRAVEYARD, filter);
+                if (controller.choose(Outcome.Exile, targetPlayer.getGraveyard(), target, game)) {
+                    controller.moveCards(new CardsImpl(target.getTargets()), Zone.EXILED, source, game);
+                }
+            }
+
+            // cards in Hand
+            int numberOfCardsExiledFromHand = 0;
+            cardsCount = (cardName.isEmpty() ? 0 : targetPlayer.getHand().count(filter, game));
+            if (cardsCount > 0) {
+                filter.setMessage("card named " + cardName + " in the hand of " + targetPlayer.getName());
+                TargetCard target = new TargetCard(0, cardsCount, Zone.HAND, filter);
+                if (controller.choose(Outcome.Exile, targetPlayer.getHand(), target, game)) {
+                    numberOfCardsExiledFromHand = target.getTargets().size();
+                    controller.moveCards(new CardsImpl(target.getTargets()), Zone.EXILED, source, game);
+                }
+            }
+
+            // cards in Library
+            Cards cardsInLibrary = new CardsImpl();
+            cardsInLibrary.addAll(targetPlayer.getLibrary().getCards(game));
+            cardsCount = (cardName.isEmpty() ? 0 : cardsInLibrary.count(filter, game));
+            if (cardsCount > 0) {
+                filter.setMessage("card named " + cardName + " in the library of " + targetPlayer.getLogName());
+                TargetCardInLibrary targetLib = new TargetCardInLibrary(0, cardsCount, filter);
+                if (controller.choose(Outcome.Exile, cardsInLibrary, targetLib, game)) {
+                    controller.moveCards(new CardsImpl(targetLib.getTargets()), Zone.EXILED, source, game);
+                }
+            }
+
+            targetPlayer.shuffleLibrary(source, game);
+
+            if (numberOfCardsExiledFromHand > 0) {
+                game.getState().applyEffects(game);
+                Token zombieToken = new ZombieToken();
+                zombieToken.putOntoBattlefield(numberOfCardsExiledFromHand, game, source.getId(), targetPlayer.getId());
+            }
+            return true;
+        }
+        return false;
+    }
+}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 59484cab56..f9349d2989 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -59,9 +59,11 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Liliana's Scorn", 329, Rarity.RARE, mage.cards.l.LilianasScorn.class));
         cards.add(new SetCardInfo("Liliana's Scrounger", 330, Rarity.UNCOMMON, mage.cards.l.LilianasScrounger.class));
         cards.add(new SetCardInfo("Liliana, Death Mage", 328, Rarity.MYTHIC, mage.cards.l.LilianaDeathMage.class));
+        cards.add(new SetCardInfo("Liliana, Waker of the Dead", 108, Rarity.MYTHIC, mage.cards.l.LilianaWakerOfTheDead.class));
         cards.add(new SetCardInfo("Llanowar Visionary", 193, Rarity.COMMON, mage.cards.l.LlanowarVisionary.class));
         cards.add(new SetCardInfo("Mangara, the Diplomat", 27, Rarity.MYTHIC, mage.cards.m.MangaraTheDiplomat.class));
         cards.add(new SetCardInfo("Mystic Skyfish", 326, Rarity.COMMON, mage.cards.m.MysticSkyfish.class));
+        cards.add(new SetCardInfo("Necromentia", 116, Rarity.RARE, mage.cards.n.Necromentia.class));
         cards.add(new SetCardInfo("Pack Leader", 29, Rarity.RARE, mage.cards.p.PackLeader.class));
         cards.add(new SetCardInfo("Peer into the Abyss", 117, Rarity.RARE, mage.cards.p.PeerIntoTheAbyss.class));
         cards.add(new SetCardInfo("Predatory Wurm", 338, Rarity.UNCOMMON, mage.cards.p.PredatoryWurm.class));
diff --git a/Mage/src/main/java/mage/abilities/effects/common/ReturnCreatureFromGraveyardToBattlefieldAndGainHasteEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ReturnCreatureFromGraveyardToBattlefieldAndGainHasteEffect.java
new file mode 100644
index 0000000000..fb3075a7fa
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/effects/common/ReturnCreatureFromGraveyardToBattlefieldAndGainHasteEffect.java
@@ -0,0 +1,53 @@
+package mage.abilities.effects.common;
+
+import mage.abilities.Ability;
+import mage.abilities.effects.ContinuousEffect;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
+import mage.abilities.keyword.HasteAbility;
+import mage.cards.Card;
+import mage.constants.Duration;
+import mage.constants.Outcome;
+import mage.constants.Zone;
+import mage.game.Game;
+import mage.game.permanent.Permanent;
+import mage.players.Player;
+import mage.target.targetpointer.FixedTarget;
+
+public class ReturnCreatureFromGraveyardToBattlefieldAndGainHasteEffect extends OneShotEffect {
+
+    public ReturnCreatureFromGraveyardToBattlefieldAndGainHasteEffect() {
+        super(Outcome.PutCreatureInPlay);
+        this.staticText = "put target creature card from a graveyard onto the battlefield under your control. It gains haste";
+    }
+
+    public ReturnCreatureFromGraveyardToBattlefieldAndGainHasteEffect(final ReturnCreatureFromGraveyardToBattlefieldAndGainHasteEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public ReturnCreatureFromGraveyardToBattlefieldAndGainHasteEffect copy() {
+        return new ReturnCreatureFromGraveyardToBattlefieldAndGainHasteEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Player controller = game.getPlayer(source.getControllerId());
+        if (controller == null) {
+            return false;
+        }
+        Card card = game.getCard(source.getFirstTarget());
+        if (card != null) {
+            controller.moveCards(card, Zone.BATTLEFIELD, source, game);
+            Permanent permanent = game.getPermanent(card.getId());
+            if (permanent != null) {
+                ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.Custom);
+                effect.setTargetPointer(new FixedTarget(permanent, game));
+                game.addEffect(effect, source);
+            }
+            return true;
+        }
+
+        return false;
+    }
+}
diff --git a/Mage/src/main/java/mage/game/command/emblems/LilianaWakerOfTheDeadEmblem.java b/Mage/src/main/java/mage/game/command/emblems/LilianaWakerOfTheDeadEmblem.java
new file mode 100644
index 0000000000..7ae6e94fe4
--- /dev/null
+++ b/Mage/src/main/java/mage/game/command/emblems/LilianaWakerOfTheDeadEmblem.java
@@ -0,0 +1,30 @@
+package mage.game.command.emblems;
+
+import mage.abilities.Ability;
+import mage.abilities.common.BeginningOfCombatTriggeredAbility;
+import mage.abilities.effects.common.ReturnCreatureFromGraveyardToBattlefieldAndGainHasteEffect;
+import mage.constants.TargetController;
+import mage.constants.Zone;
+import mage.filter.FilterCard;
+import mage.filter.StaticFilters;
+import mage.filter.common.FilterCreatureCard;
+import mage.game.command.Emblem;
+import mage.target.common.TargetCardInGraveyard;
+
+public final class LilianaWakerOfTheDeadEmblem extends Emblem {
+    /**
+     * Emblem with "At the beginning of combat on your turn, put target creature card from a graveyard onto the battlefield under your control. It gains haste."
+     */
+
+    private static final FilterCard filter = new FilterCreatureCard("creature card from a graveyard");
+
+    public LilianaWakerOfTheDeadEmblem() {
+        setName("Emblem Liliana");
+        Ability ability = new BeginningOfCombatTriggeredAbility(
+                Zone.COMMAND,
+                new ReturnCreatureFromGraveyardToBattlefieldAndGainHasteEffect(),
+                TargetController.YOU, false, false);
+        ability.addTarget(new TargetCardInGraveyard(StaticFilters.FILTER_CARD_CREATURE));
+        this.getAbilities().add(ability);
+    }
+}

From 623b23d140d146fa11f484e601656fba39858870 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Mon, 8 Jun 2020 08:53:47 +0200
Subject: [PATCH 144/586] * Code clean up.

---
 Mage.Sets/src/mage/cards/m/MarrowGnawer.java | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/m/MarrowGnawer.java b/Mage.Sets/src/mage/cards/m/MarrowGnawer.java
index 598c47ec00..487d5f3e4c 100644
--- a/Mage.Sets/src/mage/cards/m/MarrowGnawer.java
+++ b/Mage.Sets/src/mage/cards/m/MarrowGnawer.java
@@ -52,8 +52,7 @@ public final class MarrowGnawer extends CardImpl {
 
         // {T}, Sacrifice a Rat: create X 1/1 black Rat creature tokens, where X is the number of Rats you control.
         Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, 
-                new CreateTokenEffect(new RatToken(), new PermanentsOnBattlefieldCount(filter3, null))
-                        /*.setText("create X 1/1 black Rat creature tokens, where X is the number of Rats you control")*/, 
+                new CreateTokenEffect(new RatToken(), new PermanentsOnBattlefieldCount(filter3, null)),
                 new TapSourceCost());
         ability.addCost( new SacrificeTargetCost(new TargetControlledPermanent(filterSacrifice)));
         this.addAbility(ability);

From 7576a14bed8b62ca5b25305354eb44931c2622b9 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 8 Jun 2020 07:23:42 -0400
Subject: [PATCH 145/586] updated M21 spoiler and reprints

---
 Mage.Sets/src/mage/sets/CoreSet2021.java | 1 +
 Utils/mtg-cards-data.txt                 | 3 +++
 2 files changed, 4 insertions(+)

diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index f9349d2989..f520193545 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -45,6 +45,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Chromatic Orrery", 228, Rarity.MYTHIC, mage.cards.c.ChromaticOrrery.class));
         cards.add(new SetCardInfo("Containment Priest", 314, Rarity.RARE, mage.cards.c.ContainmentPriest.class));
         cards.add(new SetCardInfo("Double Vision", 142, Rarity.RARE, mage.cards.d.DoubleVision.class));
+        cards.add(new SetCardInfo("Faith's Fetters", 17, Rarity.UNCOMMON, mage.cards.f.FaithsFetters.class));
         cards.add(new SetCardInfo("Fierce Empath", 181, Rarity.UNCOMMON, mage.cards.f.FierceEmpath.class));
         cards.add(new SetCardInfo("Fungal Rebirth", 182, Rarity.UNCOMMON, mage.cards.f.FungalRebirth.class));
         cards.add(new SetCardInfo("Gadrak, the Crown-Scourge", 146, Rarity.RARE, mage.cards.g.GadrakTheCrownScourge.class));
diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt
index d8bd91caf3..ebb0d9f1fb 100644
--- a/Utils/mtg-cards-data.txt
+++ b/Utils/mtg-cards-data.txt
@@ -37440,6 +37440,7 @@ Dirge Bat|Ikoria: Lair of Behemoths|386|R|{2}{B}{B}|Creature - Bat|3|3|Mutate {4
 Crystalline Giant|Ikoria: Lair of Behemoths|387|R|{3}|Artifact Creature - Giant|3|3|At the beginning of combat on your turn, choose a kind of counter at random that Crystalline Giant doesn't have on it from among flying, first strike, deathtouch, hexproof, lifelink, menace, reach, trample, vigilance, or +1/+1. Put a counter of that kind on Crystalline Giant.|
 Ugin, the Spirit Dragon|Core Set 2021|1|M|{8}|Legendary Planeswalker - Ugin|7|+2: Ugin, the Spirit Dragon deals 3 damage to any target.$−X: Exile each permanent with converted mana cost X or less that's one or more colors.$−10: You gain 7 life, draw seven cards, then put up to seven permanent cards from your hand onto the battlefield.|
 Baneslayer Angel|Core Set 2021|6|M|{3}{W}{W}|Creature - Angel|5|5|Flying, first strike, lifelink, protection from Demons and from Dragons|
+Faith's Fetters|Core Set 2021|17|U|{3}{W}|Enchantment - Aura|||Enchant permanent$When Faith's Fetters enters the battlefield, you gain 4 life.$Enchanted permanent can't attack or block, and its activated abilities can't be activated unless they're mana abilities.|
 Mangara, the Diplomat|Core Set 2021|27|M|{3}{W}|Legendary Creature - Human Cleric|2|4|Lifelink$Whenever an opponent attacks with creatures, if two or more of those creatures are attacking you and/or a planeswalker you control, draw a card.$Whenever an opponent casts their second spell each turn, draw a card.|
 Pack Leader|Core Set 2021|29|R|{1}{W}|Creature - Dog|2|2|Other Dogs you control get +1/+1.$Whenever Pack Leader attacks, prevent all combat damage that would be dealt this turn to Dogs you control.|
 Runed Halo|Core Set 2021|32|R|{W}{W}|Enchantment|||As Runed Halo enters the battlefield, choose a card name.$You have protection from the chosen name.|
@@ -37451,6 +37452,7 @@ Teferi, Master of Time|Core Set 2021|75|M|{2}{U}{U}|Legendary Planeswalker - Tef
 Teferi's Ageless Insight|Core Set 2021|76|R|{2}{U}{U}|Legendary Enchantment|||If you would draw a card except the first one you draw in each of your draw steps, draw two cards instead.|
 Teferi's Protege|Core Set 2021|77|C|{2}{U}|Creature - Human Wizard|2|3|{1}{U}, {T}: Draw a card, then discard a card.|
 Teferi's Tutelage|Core Set 2021|78|U|{2}{U}|Enchantment|||When Teferi's Tutelage enters the battlefield, draw a card, then discard a card.$Whenever you draw a card, target opponent mills two cards.|
+Tide Skimmer|Core Set 2021|79|U|{3}{U}|Creature - Drake|2|3|Flying$Whenever you attack with two or more creatures with flying, draw a card.|
 Bad Deal|Core Set 2021|89|U|{4}{B}{B}|Sorcery|||You draw two cards and each opponent discards two cards. Each player loses 2 life.|
 Carrion Grub|Core Set 2021|92|U|{3}{B}|Creature - Insect|0|5|Carrion Grub gets +X/+0, where X is the greatest power among creature cards in your graveyard.$When Carrion Grub enters the battlefield, mill four cards.|
 Grim Tutor|Core Set 2021|103|M|{1}{B}{B}|Sorcery|||Search your library for a card and put that card into your hand, then shuffle your library. You lose 3 life.|
@@ -37461,6 +37463,7 @@ Vito, Thorn of the Dusk Rose|Core Set 2021|127|R|{2}{B}|Legendary Creature - Vam
 Double Vision|Core Set 2021|142|R|{3}{R}{R}|Enchantment|||Whenever you cast your first instant or sorcery spell each turn, copy that spell. You may choose new targets for the copy.|
 Gadrak, the Crown-Scourge|Core Set 2021|146|R|{2}{R}|Legendary Creature - Dragon|5|4|Flying$Gadrak, the Crown-Scourge can't attack unless you control four or more artifacts.$At the beginning of your end step, create a Treasure token for each nontoken creature that died this turn.|
 Azusa, Lost but Seeking|Core Set 2021|173|R|{2}{G}|Legendary Creature - Human Monk|1|2|You may play two additional lands on each of your turns.|
+Elder Gargaroth|Core Set 2021|179|M|{3}{G}{G}|Creature - Beast|6|6|Vigilance, reach, trample$Whenever Elder Gargaroth attacks or blocks, choose one —$• Create a 3/3 green Beast creature token.$• You gain 3 life.$• Draw a card.|
 Fierce Empath|Core Set 2021|181|U|{2}{G}|Creature - Elf|1|1|When Fierce Empath enters the battlefield, you may search your library for a creature card with converted mana cost 6 or greater, reveal it, put it into your hand, then shuffle your library.|
 Fungal Rebirth|Core Set 2021|182|U|{2}{G}|Instant|||Return target permanent card from your graveyard to your hand. If a creature died this turn, create two 1/1 green Saproling creature tokens.|
 Llanowar Visionary|Core Set 2021|193|C|{2}{G}|Creature - Elf Druid|2|2|When Llanowar Visionary enters the battlefield, draw a card.${T}: Add {G}.|

From b35c6c2733682234570628caca7f53cf9235eb9a Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 8 Jun 2020 07:28:08 -0400
Subject: [PATCH 146/586] Implemented Elder Gargaroth

---
 .../src/mage/cards/e/ElderGargaroth.java      | 62 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 63 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/e/ElderGargaroth.java

diff --git a/Mage.Sets/src/mage/cards/e/ElderGargaroth.java b/Mage.Sets/src/mage/cards/e/ElderGargaroth.java
new file mode 100644
index 0000000000..3722172846
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/e/ElderGargaroth.java
@@ -0,0 +1,62 @@
+package mage.cards.e;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.Mode;
+import mage.abilities.common.AttacksOrBlocksTriggeredAbility;
+import mage.abilities.effects.common.CreateTokenEffect;
+import mage.abilities.effects.common.DrawCardSourceControllerEffect;
+import mage.abilities.effects.common.GainLifeEffect;
+import mage.abilities.keyword.ReachAbility;
+import mage.abilities.keyword.TrampleAbility;
+import mage.abilities.keyword.VigilanceAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.game.permanent.token.BeastToken;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class ElderGargaroth extends CardImpl {
+
+    public ElderGargaroth(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}{G}");
+
+        this.subtype.add(SubType.BEAST);
+        this.power = new MageInt(6);
+        this.toughness = new MageInt(6);
+
+        // Vigilance
+        this.addAbility(VigilanceAbility.getInstance());
+
+        // Reach
+        this.addAbility(ReachAbility.getInstance());
+
+        // Trample
+        this.addAbility(TrampleAbility.getInstance());
+
+        // Whenever Elder Gargaroth attacks or blocks, choose one —
+        // • Create a 3/3 green Beast creature token.
+        Ability ability = new AttacksOrBlocksTriggeredAbility(new CreateTokenEffect(new BeastToken()), false);
+
+        // • You gain 3 life.
+        ability.addMode(new Mode(new GainLifeEffect(3)));
+
+        // • Draw a card.
+        ability.addMode(new Mode(new DrawCardSourceControllerEffect(1)));
+        this.addAbility(ability);
+    }
+
+    private ElderGargaroth(final ElderGargaroth card) {
+        super(card);
+    }
+
+    @Override
+    public ElderGargaroth copy() {
+        return new ElderGargaroth(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index f520193545..9efd7e9ae7 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -45,6 +45,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Chromatic Orrery", 228, Rarity.MYTHIC, mage.cards.c.ChromaticOrrery.class));
         cards.add(new SetCardInfo("Containment Priest", 314, Rarity.RARE, mage.cards.c.ContainmentPriest.class));
         cards.add(new SetCardInfo("Double Vision", 142, Rarity.RARE, mage.cards.d.DoubleVision.class));
+        cards.add(new SetCardInfo("Elder Gargaroth", 179, Rarity.MYTHIC, mage.cards.e.ElderGargaroth.class));
         cards.add(new SetCardInfo("Faith's Fetters", 17, Rarity.UNCOMMON, mage.cards.f.FaithsFetters.class));
         cards.add(new SetCardInfo("Fierce Empath", 181, Rarity.UNCOMMON, mage.cards.f.FierceEmpath.class));
         cards.add(new SetCardInfo("Fungal Rebirth", 182, Rarity.UNCOMMON, mage.cards.f.FungalRebirth.class));

From 8c63c3a7058fd0dce438f33f1d601bca274b23ac Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 8 Jun 2020 07:34:28 -0400
Subject: [PATCH 147/586] Implemented Tide Skimmer

---
 Mage.Sets/src/mage/cards/t/TideSkimmer.java | 51 +++++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java    |  1 +
 2 files changed, 52 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/t/TideSkimmer.java

diff --git a/Mage.Sets/src/mage/cards/t/TideSkimmer.java b/Mage.Sets/src/mage/cards/t/TideSkimmer.java
new file mode 100644
index 0000000000..c46364b2cb
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/t/TideSkimmer.java
@@ -0,0 +1,51 @@
+package mage.cards.t;
+
+import mage.MageInt;
+import mage.abilities.common.AttacksWithCreaturesTriggeredAbility;
+import mage.abilities.effects.common.DrawCardSourceControllerEffect;
+import mage.abilities.keyword.FlyingAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.filter.common.FilterCreaturePermanent;
+import mage.filter.predicate.mageobject.AbilityPredicate;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class TideSkimmer extends CardImpl {
+
+    private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures with flying");
+
+    static {
+        filter.add(new AbilityPredicate(FlyingAbility.class));
+    }
+
+    public TideSkimmer(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}");
+
+        this.subtype.add(SubType.DRAKE);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(3);
+
+        // Flying
+        this.addAbility(FlyingAbility.getInstance());
+
+        // Whenever you attack with two or more creatures with flying, draw a card.
+        this.addAbility(new AttacksWithCreaturesTriggeredAbility(
+                new DrawCardSourceControllerEffect(1), 2, filter
+        ));
+    }
+
+    private TideSkimmer(final TideSkimmer card) {
+        super(card);
+    }
+
+    @Override
+    public TideSkimmer copy() {
+        return new TideSkimmer(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 9efd7e9ae7..0a39c2036a 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -84,6 +84,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Teferi's Tutelage", 78, Rarity.UNCOMMON, mage.cards.t.TeferisTutelage.class));
         cards.add(new SetCardInfo("Teferi's Wavecaster", 327, Rarity.RARE, mage.cards.t.TeferisWavecaster.class));
         cards.add(new SetCardInfo("Teferi, Master of Time", 75, Rarity.MYTHIC, mage.cards.t.TeferiMasterOfTime.class));
+        cards.add(new SetCardInfo("Tide Skimmer", 79, Rarity.UNCOMMON, mage.cards.t.TideSkimmer.class));
         cards.add(new SetCardInfo("Tormod's Crypt", 241, Rarity.UNCOMMON, mage.cards.t.TormodsCrypt.class));
         cards.add(new SetCardInfo("Ugin, the Spirit Dragon", 1, Rarity.MYTHIC, mage.cards.u.UginTheSpiritDragon.class));
         cards.add(new SetCardInfo("Vito, Thorn of the Dusk Rose", 127, Rarity.RARE, mage.cards.v.VitoThornOfTheDuskRose.class));

From 919fa282ce2ff841f51b34e5a1e5ef90b698e498 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Mon, 8 Jun 2020 15:28:29 +0200
Subject: [PATCH 148/586] * Desertion - Fixed that the replacement effect part
 of it has not been implemented as replacement effect.

---
 Mage.Sets/src/mage/cards/d/Desertion.java | 70 ++++++++++++++---------
 1 file changed, 43 insertions(+), 27 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/d/Desertion.java b/Mage.Sets/src/mage/cards/d/Desertion.java
index e7ef89c2a1..5758bc61f3 100644
--- a/Mage.Sets/src/mage/cards/d/Desertion.java
+++ b/Mage.Sets/src/mage/cards/d/Desertion.java
@@ -2,17 +2,20 @@
 package mage.cards.d;
 
 import java.util.UUID;
+import mage.MageObject;
 import mage.abilities.Ability;
-import mage.abilities.effects.OneShotEffect;
-import mage.cards.Card;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.effects.ReplacementEffectImpl;
+import mage.abilities.effects.common.CounterTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
+import mage.constants.Duration;
 import mage.constants.Outcome;
 import mage.constants.Zone;
 import mage.game.Game;
-import mage.game.stack.Spell;
-import mage.players.Player;
+import mage.game.events.GameEvent;
+import mage.game.events.ZoneChangeEvent;
 import mage.target.TargetSpell;
 
 /**
@@ -24,9 +27,12 @@ public final class Desertion extends CardImpl {
     public Desertion(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{U}{U}");
 
-        // Counter target spell. If an artifact or creature spell is countered this way, put that card onto the battlefield under your control instead of into its owner's graveyard.
-        this.getSpellAbility().addEffect(new DesertionEffect());
+        // Counter target spell.
+        this.getSpellAbility().addEffect(new CounterTargetEffect());
         this.getSpellAbility().addTarget(new TargetSpell());
+        
+        // If an artifact or creature spell is countered this way, put that card onto the battlefield under your control instead of into its owner's graveyard.
+        this.addAbility(new SimpleStaticAbility(Zone.STACK, new DesertionReplacementEffect()));
     }
 
     public Desertion(final Desertion card) {
@@ -39,38 +45,48 @@ public final class Desertion extends CardImpl {
     }
 }
 
-class DesertionEffect extends OneShotEffect {
+class DesertionReplacementEffect extends ReplacementEffectImpl {
 
-    public DesertionEffect() {
-        super(Outcome.Detriment);
-        this.staticText = "Counter target spell. If an artifact or creature spell is countered this way, put that card onto the battlefield under your control instead of into its owner's graveyard";
+    DesertionReplacementEffect() {
+        super(Duration.WhileOnStack, Outcome.PutCardInPlay);
+        staticText = "If an artifact or creature spell is countered this way, put that card onto the battlefield under your control instead of into its owner's graveyard";
     }
 
-    public DesertionEffect(final DesertionEffect effect) {
+    private DesertionReplacementEffect(final DesertionReplacementEffect effect) {
         super(effect);
     }
 
     @Override
-    public DesertionEffect copy() {
-        return new DesertionEffect(this);
+    public DesertionReplacementEffect copy() {
+        return new DesertionReplacementEffect(this);
     }
 
     @Override
     public boolean apply(Game game, Ability source) {
-        Player controller = game.getPlayer(source.getControllerId());
-        if (controller != null) {
-            Spell targetSpell = game.getStack().getSpell(getTargetPointer().getFirst(game, source));
-            if (targetSpell != null) {
-                if (game.getStack().counter(targetSpell.getId(), source.getSourceId(), game)) {
-                    game.applyEffects();
-                    if (targetSpell.isArtifact() || targetSpell.isCreature()) {
-                        Card card = game.getCard(targetSpell.getSourceId());
-                        controller.moveCards(card, Zone.BATTLEFIELD, source, game);
-                    }
-                }
-            }
-            return true;
-        }
+        return true;
+    }
+
+    @Override
+    public boolean replaceEvent(GameEvent event, Ability source, Game game) {
+        ZoneChangeEvent zce = (ZoneChangeEvent) event;
+        zce.setToZone(Zone.BATTLEFIELD);
+        zce.setPlayerId(source.getControllerId());
         return false;
     }
+
+    @Override
+    public boolean checksEventType(GameEvent event, Game game) {
+        return event.getType() == GameEvent.EventType.ZONE_CHANGE;
+    }
+
+    @Override
+    public boolean applies(GameEvent event, Ability source, Game game) {        
+        if (!event.getSourceId().equals(source.getSourceId())               
+                || !(((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD)) {
+            return false;
+        }
+        MageObject mageObject =  game.getObject(event.getTargetId());
+        return mageObject != null 
+                && (mageObject.isArtifact() || mageObject.isCreature());
+    }
 }

From d8fb962030febe3834810702d120943ce1ff69ca Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Mon, 8 Jun 2020 15:40:13 +0200
Subject: [PATCH 149/586] * Changed the method for step end processing of
 spells with multiple steps (608.2) from game.applyEffects() to
 game.getState().processAction(game);

---
 Mage.Sets/src/mage/cards/a/AladdinsLamp.java           | 2 +-
 Mage.Sets/src/mage/cards/a/AngrathMinotaurPirate.java  | 2 +-
 Mage.Sets/src/mage/cards/b/BlessedReincarnation.java   | 2 +-
 Mage.Sets/src/mage/cards/b/BoundDetermined.java        | 2 +-
 Mage.Sets/src/mage/cards/b/BuildersBane.java           | 2 +-
 Mage.Sets/src/mage/cards/c/CinderCloud.java            | 2 +-
 Mage.Sets/src/mage/cards/c/CompellingDeterrence.java   | 2 +-
 Mage.Sets/src/mage/cards/d/DeclarationInStone.java     | 2 +-
 Mage.Sets/src/mage/cards/d/DevourFlesh.java            | 2 +-
 Mage.Sets/src/mage/cards/d/DomrisAmbush.java           | 2 +-
 Mage.Sets/src/mage/cards/e/EmpoweredAutogenerator.java | 2 +-
 Mage.Sets/src/mage/cards/f/FiligreeFracture.java       | 2 +-
 Mage.Sets/src/mage/cards/f/FracturingGust.java         | 2 +-
 Mage.Sets/src/mage/cards/f/FromTheAshes.java           | 4 ++--
 Mage.Sets/src/mage/cards/f/Fumigate.java               | 2 +-
 Mage.Sets/src/mage/cards/g/GhostlyFlicker.java         | 2 +-
 Mage.Sets/src/mage/cards/g/GideonsDefeat.java          | 2 +-
 Mage.Sets/src/mage/cards/h/HeraldOfLeshrac.java        | 2 +-
 Mage.Sets/src/mage/cards/h/HourOfGlory.java            | 2 +-
 Mage.Sets/src/mage/cards/i/InducedAmnesia.java         | 2 +-
 Mage.Sets/src/mage/cards/i/InfernalReckoning.java      | 2 +-
 Mage.Sets/src/mage/cards/j/Joust.java                  | 2 +-
 Mage.Sets/src/mage/cards/k/KaerveksPurge.java          | 2 +-
 Mage.Sets/src/mage/cards/k/KraulHarpooner.java         | 2 +-
 Mage.Sets/src/mage/cards/k/KrenkoTinStreetKingpin.java | 2 +-
 Mage.Sets/src/mage/cards/l/LilianasDefeat.java         | 2 +-
 Mage.Sets/src/mage/cards/l/LivingDeath.java            | 4 ++--
 Mage.Sets/src/mage/cards/m/MayaelsAria.java            | 2 +-
 Mage.Sets/src/mage/cards/m/MoltenPsyche.java           | 2 +-
 Mage.Sets/src/mage/cards/o/Oblation.java               | 2 +-
 Mage.Sets/src/mage/cards/o/Outmuscle.java              | 2 +-
 Mage.Sets/src/mage/cards/p/ProfaneProcession.java      | 2 +-
 Mage.Sets/src/mage/cards/p/PureReflection.java         | 2 +-
 Mage.Sets/src/mage/cards/s/SavageSwipe.java            | 2 +-
 Mage.Sets/src/mage/cards/s/SorinLordOfInnistrad.java   | 2 +-
 Mage.Sets/src/mage/cards/t/TahngarthFirstMate.java     | 2 +-
 Mage.Sets/src/mage/cards/t/TeleminPerformance.java     | 2 +-
 Mage.Sets/src/mage/cards/t/Terastodon.java             | 2 +-
 Mage.Sets/src/mage/cards/t/TheGreatAurora.java         | 2 +-
 Mage.Sets/src/mage/cards/t/ThoughtDissector.java       | 2 +-
 Mage.Sets/src/mage/cards/t/TwilightProphet.java        | 2 +-
 Mage.Sets/src/mage/cards/v/VesuvanShapeshifter.java    | 2 +-
 Mage.Sets/src/mage/cards/v/Victimize.java              | 2 +-
 Mage.Sets/src/mage/cards/w/WarpWorld.java              | 6 +++---
 Mage.Sets/src/mage/cards/w/WaveOfVitriol.java          | 2 +-
 Mage.Sets/src/mage/cards/w/WidespreadBrutality.java    | 2 +-
 Mage.Sets/src/mage/cards/y/YorvoLordOfGarenbrig.java   | 2 +-
 Mage.Sets/src/mage/cards/z/ZombieApocalypse.java       | 2 +-
 48 files changed, 52 insertions(+), 52 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/a/AladdinsLamp.java b/Mage.Sets/src/mage/cards/a/AladdinsLamp.java
index e020079b68..5fb6e9b43c 100644
--- a/Mage.Sets/src/mage/cards/a/AladdinsLamp.java
+++ b/Mage.Sets/src/mage/cards/a/AladdinsLamp.java
@@ -74,7 +74,7 @@ class AladdinsLampEffect extends ReplacementEffectImpl {
             cards.remove(target.getFirstTarget());
         }
         controller.putCardsOnBottomOfLibrary(cards, game, source, false);
-        game.applyEffects();
+        game.getState().processAction(game);
         controller.drawCards(1, event.getSourceId(), game, event.getAppliedEffects());
         discard();
         return true;
diff --git a/Mage.Sets/src/mage/cards/a/AngrathMinotaurPirate.java b/Mage.Sets/src/mage/cards/a/AngrathMinotaurPirate.java
index 2c5a6aef46..d9eff6ea19 100644
--- a/Mage.Sets/src/mage/cards/a/AngrathMinotaurPirate.java
+++ b/Mage.Sets/src/mage/cards/a/AngrathMinotaurPirate.java
@@ -98,7 +98,7 @@ class AngrathMinotaurPirateThirdAbilityEffect extends OneShotEffect {
                 permanent.destroy(source.getSourceId(), game, false);
                 powerSum += permanent.getPower().getValue();
             }
-            game.applyEffects();
+            game.getState().processAction(game);
             targetOpponent.damage(powerSum, source.getSourceId(), game);
         }
         return true;
diff --git a/Mage.Sets/src/mage/cards/b/BlessedReincarnation.java b/Mage.Sets/src/mage/cards/b/BlessedReincarnation.java
index ac0276b9c5..5f4770a104 100644
--- a/Mage.Sets/src/mage/cards/b/BlessedReincarnation.java
+++ b/Mage.Sets/src/mage/cards/b/BlessedReincarnation.java
@@ -74,7 +74,7 @@ class BlessedReincarnationEffect extends OneShotEffect {
         Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
         if (permanent != null && controller != null) {
             controller.moveCards(permanent, Zone.EXILED, source, game);
-            game.applyEffects();
+            game.getState().processAction(game);
 
             Player permanentController = game.getPlayer(permanent.getControllerId());
             if (permanentController != null) {
diff --git a/Mage.Sets/src/mage/cards/b/BoundDetermined.java b/Mage.Sets/src/mage/cards/b/BoundDetermined.java
index cfb16ba517..86d2018d82 100644
--- a/Mage.Sets/src/mage/cards/b/BoundDetermined.java
+++ b/Mage.Sets/src/mage/cards/b/BoundDetermined.java
@@ -86,7 +86,7 @@ class BoundEffect extends OneShotEffect {
                     Permanent toSacrifice = game.getPermanent(target.getFirstTarget());
                     if (toSacrifice != null) {
                         toSacrifice.sacrifice(source.getSourceId(), game);
-                        game.applyEffects();
+                        game.getState().processAction(game);
                         int colors = toSacrifice.getColor(game).getColorCount();
                         if (colors > 0) {
                             TargetCardInYourGraveyard targetCard = new TargetCardInYourGraveyard(0, colors,
diff --git a/Mage.Sets/src/mage/cards/b/BuildersBane.java b/Mage.Sets/src/mage/cards/b/BuildersBane.java
index 98b8621a8d..d29d7db605 100644
--- a/Mage.Sets/src/mage/cards/b/BuildersBane.java
+++ b/Mage.Sets/src/mage/cards/b/BuildersBane.java
@@ -77,7 +77,7 @@ class BuildersBaneEffect extends OneShotEffect {
             Permanent permanent = game.getPermanent(targetID);
             if (permanent != null) {
                 if (permanent.destroy(source.getSourceId(), game, false)) {
-                    game.applyEffects();
+                    game.getState().processAction(game);
                     if (permanent.getZoneChangeCounter(game) + 1 == game.getState().getZoneChangeCounter(permanent.getId())
                             && game.getState().getZone(permanent.getId()) != Zone.GRAVEYARD) {
                         // A replacement effect has moved the card to another zone as grvayard
diff --git a/Mage.Sets/src/mage/cards/c/CinderCloud.java b/Mage.Sets/src/mage/cards/c/CinderCloud.java
index 95a401befd..a2208fc8ad 100644
--- a/Mage.Sets/src/mage/cards/c/CinderCloud.java
+++ b/Mage.Sets/src/mage/cards/c/CinderCloud.java
@@ -59,7 +59,7 @@ class CinderCloudEffect extends OneShotEffect {
     public boolean apply(Game game, Ability source) {
         Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
         if (permanent != null && permanent.destroy(source.getSourceId(), game, false) && permanent.getColor(game).equals(ObjectColor.WHITE)) {
-            game.applyEffects();
+            game.getState().processAction(game);
             if (permanent.getZoneChangeCounter(game) + 1 == game.getState().getZoneChangeCounter(permanent.getId())
                     && game.getState().getZone(permanent.getId()) != Zone.GRAVEYARD) {
                 // A replacement effect has moved the card to another zone as grvayard
diff --git a/Mage.Sets/src/mage/cards/c/CompellingDeterrence.java b/Mage.Sets/src/mage/cards/c/CompellingDeterrence.java
index 4fb628199e..246c434497 100644
--- a/Mage.Sets/src/mage/cards/c/CompellingDeterrence.java
+++ b/Mage.Sets/src/mage/cards/c/CompellingDeterrence.java
@@ -63,7 +63,7 @@ class CompellingDeterrenceEffect extends OneShotEffect {
         Player controller = game.getPlayer(source.getControllerId());
         if (controller != null && player != null) {
             player.moveCards(target, Zone.HAND, source, game);
-            game.applyEffects();
+            game.getState().processAction(game);
             FilterPermanent zombieFilter = new FilterPermanent();
             zombieFilter.add(SubType.ZOMBIE.getPredicate());
             if (game.getState().getBattlefield().countAll(zombieFilter, controller.getId(), game) > 0) {
diff --git a/Mage.Sets/src/mage/cards/d/DeclarationInStone.java b/Mage.Sets/src/mage/cards/d/DeclarationInStone.java
index 45cfb5b117..c7edeb0cca 100644
--- a/Mage.Sets/src/mage/cards/d/DeclarationInStone.java
+++ b/Mage.Sets/src/mage/cards/d/DeclarationInStone.java
@@ -88,7 +88,7 @@ class DeclarationInStoneEffect extends OneShotEffect {
                     }
                 }
                 controller.moveCards(cardsToExile, Zone.EXILED, source, game);
-                game.applyEffects();
+                game.getState().processAction(game);
                 if (nonTokenCount > 0) {
                     new ClueArtifactToken().putOntoBattlefield(nonTokenCount, game, source.getSourceId(), targetPermanent.getControllerId(), false, false);
                 }
diff --git a/Mage.Sets/src/mage/cards/d/DevourFlesh.java b/Mage.Sets/src/mage/cards/d/DevourFlesh.java
index 985f35f554..c8b64b6004 100644
--- a/Mage.Sets/src/mage/cards/d/DevourFlesh.java
+++ b/Mage.Sets/src/mage/cards/d/DevourFlesh.java
@@ -73,7 +73,7 @@ class DevourFleshSacrificeEffect extends OneShotEffect {
             if (permanent != null) {
                 int gainLife = permanent.getToughness().getValue();
                 permanent.sacrifice(source.getSourceId(), game);
-                game.applyEffects();
+                game.getState().processAction(game);
                 player.gainLife(gainLife, game, source);
             } else {
                 return false;
diff --git a/Mage.Sets/src/mage/cards/d/DomrisAmbush.java b/Mage.Sets/src/mage/cards/d/DomrisAmbush.java
index f1c177cb97..b598f9442e 100644
--- a/Mage.Sets/src/mage/cards/d/DomrisAmbush.java
+++ b/Mage.Sets/src/mage/cards/d/DomrisAmbush.java
@@ -78,7 +78,7 @@ class DomrisAmbushEffect extends OneShotEffect {
             return false;
         }
         permanent.addCounters(CounterType.P1P1.createInstance(), source, game);
-        game.applyEffects();
+        game.getState().processAction(game);
         return new DamageWithPowerFromOneToAnotherTargetEffect().apply(game, source);
     }
 }
diff --git a/Mage.Sets/src/mage/cards/e/EmpoweredAutogenerator.java b/Mage.Sets/src/mage/cards/e/EmpoweredAutogenerator.java
index e8560d4270..a53fb3e2ca 100644
--- a/Mage.Sets/src/mage/cards/e/EmpoweredAutogenerator.java
+++ b/Mage.Sets/src/mage/cards/e/EmpoweredAutogenerator.java
@@ -88,7 +88,7 @@ class EmpoweredAutogeneratorManaEffect extends ManaEffect {
         if (game == null) {
             return mana;
         }
-        game.applyEffects();
+        game.getState().processAction(game);
         Permanent sourcePermanent = game.getState().getPermanent(source.getSourceId());
         if (sourcePermanent == null) {
             return mana;
diff --git a/Mage.Sets/src/mage/cards/f/FiligreeFracture.java b/Mage.Sets/src/mage/cards/f/FiligreeFracture.java
index efcf412747..98eb2eb6ed 100644
--- a/Mage.Sets/src/mage/cards/f/FiligreeFracture.java
+++ b/Mage.Sets/src/mage/cards/f/FiligreeFracture.java
@@ -61,7 +61,7 @@ class FiligreeFractureEffect extends OneShotEffect {
         Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
         if (player != null && permanent != null) {
             permanent.destroy(source.getSourceId(), game, true);
-            game.applyEffects();
+            game.getState().processAction(game);
             if (permanent.getColor(game).isBlack() || permanent.getColor(game).isBlue()) {
                 player.drawCards(1, source.getSourceId(), game);
             }
diff --git a/Mage.Sets/src/mage/cards/f/FracturingGust.java b/Mage.Sets/src/mage/cards/f/FracturingGust.java
index c54bb8b1f1..4da56870ae 100644
--- a/Mage.Sets/src/mage/cards/f/FracturingGust.java
+++ b/Mage.Sets/src/mage/cards/f/FracturingGust.java
@@ -67,7 +67,7 @@ class FracturingGustDestroyEffect extends OneShotEffect {
                     ++destroyedPermanents;
                 }
             }
-            game.applyEffects(); // needed in case a destroyed permanent did prevent life gain
+            game.getState().processAction(game); // needed in case a destroyed permanent did prevent life gain
             if (destroyedPermanents > 0) {
                 controller.gainLife(2 * destroyedPermanents, game, source);
             }
diff --git a/Mage.Sets/src/mage/cards/f/FromTheAshes.java b/Mage.Sets/src/mage/cards/f/FromTheAshes.java
index 77d8390df9..8dd0b42910 100644
--- a/Mage.Sets/src/mage/cards/f/FromTheAshes.java
+++ b/Mage.Sets/src/mage/cards/f/FromTheAshes.java
@@ -76,7 +76,7 @@ class FromTheAshesEffect extends OneShotEffect {
                     playerAmount.put(playerId, amount);
                 }
             }
-            game.applyEffects();
+            game.getState().processAction(game);
             for (Map.Entry<UUID, Integer> entry : playerAmount.entrySet()) {
                 Player player = game.getPlayer(entry.getKey());
                 if (player != null && player.chooseUse(outcome, "Search your library for up to " + entry.getValue() + " basic land card(s) to put it onto the battlefield?", source, game)) {
@@ -90,7 +90,7 @@ class FromTheAshesEffect extends OneShotEffect {
                     entry.setValue(0); // no search no shuffling
                 }
             }
-            game.applyEffects();
+            game.getState().processAction(game);
             for (Map.Entry<UUID, Integer> entry : playerAmount.entrySet()) {
                 Player player = game.getPlayer(entry.getKey());
                 if (player != null && entry.getValue() > 0) {
diff --git a/Mage.Sets/src/mage/cards/f/Fumigate.java b/Mage.Sets/src/mage/cards/f/Fumigate.java
index 54a1d5cfee..b74ef2339e 100644
--- a/Mage.Sets/src/mage/cards/f/Fumigate.java
+++ b/Mage.Sets/src/mage/cards/f/Fumigate.java
@@ -62,7 +62,7 @@ class FumigateEffect extends OneShotEffect {
                     destroyedCreature++;
                 }
             }
-            game.applyEffects();
+            game.getState().processAction(game);
             if (destroyedCreature > 0) {
                 controller.gainLife(destroyedCreature, game, source);
             }
diff --git a/Mage.Sets/src/mage/cards/g/GhostlyFlicker.java b/Mage.Sets/src/mage/cards/g/GhostlyFlicker.java
index e69fb17a01..cf35f53a13 100644
--- a/Mage.Sets/src/mage/cards/g/GhostlyFlicker.java
+++ b/Mage.Sets/src/mage/cards/g/GhostlyFlicker.java
@@ -81,7 +81,7 @@ class GhostlyFlickerEffect extends OneShotEffect {
                 }
             }
             controller.moveCards(toExile, Zone.EXILED, source, game);
-            game.applyEffects();
+            game.getState().processAction(game);
             Set<Card> toBattlefield = new HashSet<>();
             for (Card card : toExile) {
                 Zone currentZone = game.getState().getZone(card.getId());
diff --git a/Mage.Sets/src/mage/cards/g/GideonsDefeat.java b/Mage.Sets/src/mage/cards/g/GideonsDefeat.java
index b73f9f2dec..de6db2d921 100644
--- a/Mage.Sets/src/mage/cards/g/GideonsDefeat.java
+++ b/Mage.Sets/src/mage/cards/g/GideonsDefeat.java
@@ -71,7 +71,7 @@ class GideonsDefeatEffect extends OneShotEffect {
         Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
         if (controller != null && permanent != null) {
             controller.moveCards(permanent, Zone.EXILED, source, game);
-            game.applyEffects();
+            game.getState().processAction(game);
             if (permanent.isPlaneswalker() && permanent.hasSubtype(SubType.GIDEON, game)) {
                 controller.gainLife(5, game, source);
             }
diff --git a/Mage.Sets/src/mage/cards/h/HeraldOfLeshrac.java b/Mage.Sets/src/mage/cards/h/HeraldOfLeshrac.java
index 1ae97254ff..ec94e92c5e 100644
--- a/Mage.Sets/src/mage/cards/h/HeraldOfLeshrac.java
+++ b/Mage.Sets/src/mage/cards/h/HeraldOfLeshrac.java
@@ -97,7 +97,7 @@ class HeraldOfLeshracCumulativeCost extends CostImpl {
             ContinuousEffect effect = new GainControlTargetEffect(Duration.EndOfGame);
             effect.setTargetPointer(new FixedTarget(target.getFirstTarget()));
             game.addEffect(effect, ability);
-            game.applyEffects();
+            game.getState().processAction(game);
             paid = true;
         }
         return paid;
diff --git a/Mage.Sets/src/mage/cards/h/HourOfGlory.java b/Mage.Sets/src/mage/cards/h/HourOfGlory.java
index 5be4f80dcb..4f4dfb2737 100644
--- a/Mage.Sets/src/mage/cards/h/HourOfGlory.java
+++ b/Mage.Sets/src/mage/cards/h/HourOfGlory.java
@@ -69,7 +69,7 @@ class HourOfGloryEffect extends OneShotEffect {
             if (targetCreature != null) {
                 controller.moveCards(targetCreature, Zone.EXILED, source, game);
                 if (targetCreature.hasSubtype(SubType.GOD, game)) {
-                    game.applyEffects();
+                    game.getState().processAction(game);
                     Player targetController = game.getPlayer(targetCreature.getControllerId());
                     if (targetController != null) {
                         targetController.revealCards(sourceObject.getIdName(), targetController.getHand(), game);
diff --git a/Mage.Sets/src/mage/cards/i/InducedAmnesia.java b/Mage.Sets/src/mage/cards/i/InducedAmnesia.java
index dbdac63fb7..9f2e5d158b 100644
--- a/Mage.Sets/src/mage/cards/i/InducedAmnesia.java
+++ b/Mage.Sets/src/mage/cards/i/InducedAmnesia.java
@@ -76,7 +76,7 @@ class InducedAmnesiaExileEffect extends OneShotEffect {
                     card.setFaceDown(true, game);
                 }
                 game.informPlayers(sourcePermanent.getLogName() + ": " + targetPlayer.getLogName() + " exiles their hand face down (" + numberOfCards + "card" + (numberOfCards > 1 ? "s" : "") + ')');
-                game.applyEffects();
+                game.getState().processAction(game);
                 targetPlayer.drawCards(numberOfCards, source.getSourceId(), game);
             }
             return true;
diff --git a/Mage.Sets/src/mage/cards/i/InfernalReckoning.java b/Mage.Sets/src/mage/cards/i/InfernalReckoning.java
index 1d1cc6ded2..de48593fe5 100644
--- a/Mage.Sets/src/mage/cards/i/InfernalReckoning.java
+++ b/Mage.Sets/src/mage/cards/i/InfernalReckoning.java
@@ -69,7 +69,7 @@ class InfernalJudgmentEffect extends OneShotEffect {
         }
         int creaturePower = permanent.getPower().getValue();
         permanent.moveToExile(null, null, source.getSourceId(), game);
-        game.applyEffects();
+        game.getState().processAction(game);
         player.gainLife(creaturePower, game, source);
         return true;
     }
diff --git a/Mage.Sets/src/mage/cards/j/Joust.java b/Mage.Sets/src/mage/cards/j/Joust.java
index cddf0ea93a..77b8d33df2 100644
--- a/Mage.Sets/src/mage/cards/j/Joust.java
+++ b/Mage.Sets/src/mage/cards/j/Joust.java
@@ -76,7 +76,7 @@ class JoustEffect extends OneShotEffect {
         if (creature2 == null) {
             return true;
         }
-        game.applyEffects();
+        game.getState().processAction(game);
         return creature1.fight(creature2, source, game);
     }
 }
diff --git a/Mage.Sets/src/mage/cards/k/KaerveksPurge.java b/Mage.Sets/src/mage/cards/k/KaerveksPurge.java
index 19b5c69c3e..4eabe29bd9 100644
--- a/Mage.Sets/src/mage/cards/k/KaerveksPurge.java
+++ b/Mage.Sets/src/mage/cards/k/KaerveksPurge.java
@@ -78,7 +78,7 @@ class KaerveksPurgeEffect extends OneShotEffect {
         // Destroy target creature with converted mana cost X.
         Permanent targetCreature = game.getPermanent(getTargetPointer().getFirst(game, source));
         if (targetCreature != null && targetCreature.destroy(source.getSourceId(), game, false)) {
-            game.applyEffects();
+            game.getState().processAction(game);
             if (targetCreature.getZoneChangeCounter(game) + 1 == game.getState().getZoneChangeCounter(targetCreature.getId())
                     && game.getState().getZone(targetCreature.getId()) != Zone.GRAVEYARD) {
                 // A replacement effect has moved the card to another zone as graveyard
diff --git a/Mage.Sets/src/mage/cards/k/KraulHarpooner.java b/Mage.Sets/src/mage/cards/k/KraulHarpooner.java
index 13c16e28cb..dbb43938a5 100644
--- a/Mage.Sets/src/mage/cards/k/KraulHarpooner.java
+++ b/Mage.Sets/src/mage/cards/k/KraulHarpooner.java
@@ -95,7 +95,7 @@ class KraulHarpoonerEffect extends OneShotEffect {
         }
         int xValue = player.getGraveyard().count(StaticFilters.FILTER_CARD_CREATURE, game);
         game.addEffect(new BoostSourceEffect(xValue, 0, Duration.EndOfTurn), source);
-        game.applyEffects();
+        game.getState().processAction(game);
         Permanent creature = game.getPermanent(source.getFirstTarget());
         if (creature == null || !player.chooseUse(outcome, "Have " + sourcePerm.getLogName() + " fight " + creature.getLogName() + "?", source, game)) {
             return true;
diff --git a/Mage.Sets/src/mage/cards/k/KrenkoTinStreetKingpin.java b/Mage.Sets/src/mage/cards/k/KrenkoTinStreetKingpin.java
index 7ba8079a32..10b4d902e5 100644
--- a/Mage.Sets/src/mage/cards/k/KrenkoTinStreetKingpin.java
+++ b/Mage.Sets/src/mage/cards/k/KrenkoTinStreetKingpin.java
@@ -70,7 +70,7 @@ class KrenkoTinStreetKingpinEffect extends OneShotEffect {
             return false;
         }
         new AddCountersSourceEffect(CounterType.P1P1.createInstance()).apply(game, source);
-        game.applyEffects();
+        game.getState().processAction(game);
         int xValue = permanent.getPower().getValue();
         return new CreateTokenEffect(new GoblinToken("WAR"), xValue).apply(game, source);
     }
diff --git a/Mage.Sets/src/mage/cards/l/LilianasDefeat.java b/Mage.Sets/src/mage/cards/l/LilianasDefeat.java
index c39d3aaf27..02bedc3b11 100644
--- a/Mage.Sets/src/mage/cards/l/LilianasDefeat.java
+++ b/Mage.Sets/src/mage/cards/l/LilianasDefeat.java
@@ -67,7 +67,7 @@ class LilianasDefeatEffect extends OneShotEffect {
         Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
         if (player != null && permanent != null) {
             permanent.destroy(source.getSourceId(), game, true);
-            game.applyEffects();
+            game.getState().processAction(game);
             if (permanent.isPlaneswalker() && permanent.hasSubtype(SubType.LILIANA, game)) {
                 Player permanentController = game.getPlayer(permanent.getControllerId());
                 if (permanentController != null) {
diff --git a/Mage.Sets/src/mage/cards/l/LivingDeath.java b/Mage.Sets/src/mage/cards/l/LivingDeath.java
index 904c560c38..bfe880cbca 100644
--- a/Mage.Sets/src/mage/cards/l/LivingDeath.java
+++ b/Mage.Sets/src/mage/cards/l/LivingDeath.java
@@ -76,13 +76,13 @@ class LivingDeathEffect extends OneShotEffect {
                     }
                 }
             }
-            game.applyEffects();
+            game.getState().processAction(game);
 
             // Sacrifice all creatures
             for (Permanent permanent : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), game)) {
                 permanent.sacrifice(source.getSourceId(), game);
             }
-            game.applyEffects();
+            game.getState().processAction(game);
 
             // Exiled cards are put onto the battlefield at the same time under their owner's control
             Set<Card> cardsToReturnFromExile = new HashSet<>();
diff --git a/Mage.Sets/src/mage/cards/m/MayaelsAria.java b/Mage.Sets/src/mage/cards/m/MayaelsAria.java
index 027b039b67..2344735fc1 100644
--- a/Mage.Sets/src/mage/cards/m/MayaelsAria.java
+++ b/Mage.Sets/src/mage/cards/m/MayaelsAria.java
@@ -75,7 +75,7 @@ class MayaelsAriaEffect extends OneShotEffect {
                 creature.addCounters(CounterType.P1P1.createInstance(), source, game);
             }
         }
-        game.applyEffects(); // needed because otehrwise the +1/+1 counters wouldn't be taken into account
+        game.getState().processAction(game); // needed because otehrwise the +1/+1 counters wouldn't be taken into account
 
         // Then you gain 10 life if you control a creature with power 10 or greater.
         filter = new FilterCreaturePermanent();
diff --git a/Mage.Sets/src/mage/cards/m/MoltenPsyche.java b/Mage.Sets/src/mage/cards/m/MoltenPsyche.java
index 7d80268bf9..b3ef8785c6 100644
--- a/Mage.Sets/src/mage/cards/m/MoltenPsyche.java
+++ b/Mage.Sets/src/mage/cards/m/MoltenPsyche.java
@@ -73,7 +73,7 @@ class MoltenPsycheEffect extends OneShotEffect {
                 }
             }
 
-            game.applyEffects(); // so effects from creatures that were on the battlefield won't trigger from draw action
+            game.getState().processAction(game); // so effects from creatures that were on the battlefield won't trigger from draw action
 
             for (UUID playerId : cardsToDraw.keySet()) {
                 Player player = game.getPlayer(playerId);
diff --git a/Mage.Sets/src/mage/cards/o/Oblation.java b/Mage.Sets/src/mage/cards/o/Oblation.java
index 5e84b814d1..85af435e69 100644
--- a/Mage.Sets/src/mage/cards/o/Oblation.java
+++ b/Mage.Sets/src/mage/cards/o/Oblation.java
@@ -63,7 +63,7 @@ class OblationEffect extends OneShotEffect {
                 player.moveCardToLibraryWithInfo(permanent, source.getSourceId(), game, Zone.BATTLEFIELD, true, true);
                 player.shuffleLibrary(source, game);
 
-                game.applyEffects(); // so effects from creatures that were on the battlefield won't trigger from draw 
+                game.getState().processAction(game); // so effects from creatures that were on the battlefield won't trigger from draw 
 
                 player.drawCards(2, source.getSourceId(), game);
                 return true;
diff --git a/Mage.Sets/src/mage/cards/o/Outmuscle.java b/Mage.Sets/src/mage/cards/o/Outmuscle.java
index d64ecb8b25..c35df58cd9 100644
--- a/Mage.Sets/src/mage/cards/o/Outmuscle.java
+++ b/Mage.Sets/src/mage/cards/o/Outmuscle.java
@@ -83,7 +83,7 @@ class OutmuscleEffect extends OneShotEffect {
         if (creature == null) {
             return true;
         }
-        game.applyEffects();
+        game.getState().processAction(game);
         return creature.fight(permanent, source, game);
     }
 }
diff --git a/Mage.Sets/src/mage/cards/p/ProfaneProcession.java b/Mage.Sets/src/mage/cards/p/ProfaneProcession.java
index 67d0fa8434..8008c8eabb 100644
--- a/Mage.Sets/src/mage/cards/p/ProfaneProcession.java
+++ b/Mage.Sets/src/mage/cards/p/ProfaneProcession.java
@@ -76,7 +76,7 @@ class ProfaneProcessionEffect extends OneShotEffect {
         MageObject sourceObject = source.getSourceObject(game);
         if (controller != null && exileId != null && sourceObject != null) {
             new ExileTargetEffect(exileId, sourceObject.getIdName()).setTargetPointer(targetPointer).apply(game, source);
-            game.applyEffects();
+            game.getState().processAction(game);
             ExileZone exileZone = game.getExile().getExileZone(exileId);
             if (exileZone != null && exileZone.size() > 2) {
                 new TransformSourceEffect(true).apply(game, source);
diff --git a/Mage.Sets/src/mage/cards/p/PureReflection.java b/Mage.Sets/src/mage/cards/p/PureReflection.java
index cad8231b33..645bc1d895 100644
--- a/Mage.Sets/src/mage/cards/p/PureReflection.java
+++ b/Mage.Sets/src/mage/cards/p/PureReflection.java
@@ -71,7 +71,7 @@ public final class PureReflection extends CardImpl {
             game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game).forEach((permanent) -> {
                 permanent.destroy(source.getSourceId(), game,false);
             });
-            game.applyEffects();
+            game.getState().processAction(game);
             
             // Then that player creates an X/X white Reflection creature token, where X is the converted mana cost of that spell.
             ReflectionPureToken token = new ReflectionPureToken(spell.getConvertedManaCost());
diff --git a/Mage.Sets/src/mage/cards/s/SavageSwipe.java b/Mage.Sets/src/mage/cards/s/SavageSwipe.java
index 8c8140ad78..65caea0855 100644
--- a/Mage.Sets/src/mage/cards/s/SavageSwipe.java
+++ b/Mage.Sets/src/mage/cards/s/SavageSwipe.java
@@ -70,7 +70,7 @@ class SavageSwipeEffect extends OneShotEffect {
             ContinuousEffect effect = new BoostTargetEffect(2, 2, Duration.EndOfTurn);
             effect.setTargetPointer(new FixedTarget(permanent, game));
             game.addEffect(effect, source);
-            game.applyEffects();
+            game.getState().processAction(game);
         }
         return new FightTargetsEffect().apply(game, source);
     }
diff --git a/Mage.Sets/src/mage/cards/s/SorinLordOfInnistrad.java b/Mage.Sets/src/mage/cards/s/SorinLordOfInnistrad.java
index 7a89e3f455..d65929f660 100644
--- a/Mage.Sets/src/mage/cards/s/SorinLordOfInnistrad.java
+++ b/Mage.Sets/src/mage/cards/s/SorinLordOfInnistrad.java
@@ -101,7 +101,7 @@ class SorinLordOfInnistradEffect extends OneShotEffect {
                 }
             }
         }
-        game.applyEffects();
+        game.getState().processAction(game);
         Player controller = game.getPlayer(source.getControllerId());
         if (controller != null) {
             return controller.moveCards(toBattlefield, Zone.BATTLEFIELD, source, game);
diff --git a/Mage.Sets/src/mage/cards/t/TahngarthFirstMate.java b/Mage.Sets/src/mage/cards/t/TahngarthFirstMate.java
index 693bf559f0..a8c700033e 100644
--- a/Mage.Sets/src/mage/cards/t/TahngarthFirstMate.java
+++ b/Mage.Sets/src/mage/cards/t/TahngarthFirstMate.java
@@ -133,7 +133,7 @@ class TahngarthFirstMateEffect extends OneShotEffect {
         ContinuousEffect effect = new GainControlTargetEffect(Duration.EndOfCombat, player.getId());
         effect.setTargetPointer(new FixedTarget(permanent, game));
         game.addEffect(effect, source);
-        game.applyEffects();
+        game.getState().processAction(game);
         return game.getCombat().addAttackerToCombat(permanent.getId(), target.getFirstTarget(), game);
     }
 }
diff --git a/Mage.Sets/src/mage/cards/t/TeleminPerformance.java b/Mage.Sets/src/mage/cards/t/TeleminPerformance.java
index b1b7aca67a..b01048d173 100644
--- a/Mage.Sets/src/mage/cards/t/TeleminPerformance.java
+++ b/Mage.Sets/src/mage/cards/t/TeleminPerformance.java
@@ -77,7 +77,7 @@ class TeleminPerformanceEffect extends OneShotEffect {
                 opponent.revealCards(source, reveal, game);
                 opponent.moveCards(nonCreatures, Zone.GRAVEYARD, source, game);
                 if (creature != null) {
-                    game.applyEffects();
+                    game.getState().processAction(game);
                     controller.moveCards(creature, Zone.BATTLEFIELD, source, game);
                 }
             }
diff --git a/Mage.Sets/src/mage/cards/t/Terastodon.java b/Mage.Sets/src/mage/cards/t/Terastodon.java
index 4a73c47b75..3adf3d54bb 100644
--- a/Mage.Sets/src/mage/cards/t/Terastodon.java
+++ b/Mage.Sets/src/mage/cards/t/Terastodon.java
@@ -88,7 +88,7 @@ class TerastodonEffect extends OneShotEffect {
                 }
             }
         }
-        game.applyEffects();
+        game.getState().processAction(game);
         ElephantToken elephantToken = new ElephantToken();
         for (Entry<UUID, Integer> entry : destroyedPermanents.entrySet()) {
             elephantToken.putOntoBattlefield(entry.getValue(), game, source.getSourceId(), entry.getKey());
diff --git a/Mage.Sets/src/mage/cards/t/TheGreatAurora.java b/Mage.Sets/src/mage/cards/t/TheGreatAurora.java
index ee5e5e07f9..063ae0af37 100644
--- a/Mage.Sets/src/mage/cards/t/TheGreatAurora.java
+++ b/Mage.Sets/src/mage/cards/t/TheGreatAurora.java
@@ -90,7 +90,7 @@ class TheGreatAuroraEffect extends OneShotEffect {
                 }
             }
 
-            game.applyEffects(); // so effects from creatures that were on the battlefield won't trigger from draw or put into play
+            game.getState().processAction(game); // so effects from creatures that were on the battlefield won't trigger from draw or put into play
 
             // Draw cards
             for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) {
diff --git a/Mage.Sets/src/mage/cards/t/ThoughtDissector.java b/Mage.Sets/src/mage/cards/t/ThoughtDissector.java
index f817054089..d57da0a9b6 100644
--- a/Mage.Sets/src/mage/cards/t/ThoughtDissector.java
+++ b/Mage.Sets/src/mage/cards/t/ThoughtDissector.java
@@ -90,7 +90,7 @@ class ThoughtDissectorEffect extends OneShotEffect {
             }
             targetOpponent.revealCards(source, reveal, game);
             if (artifact != null) {
-                game.applyEffects();
+                game.getState().processAction(game);
                 controller.moveCards(artifact, Zone.BATTLEFIELD, source, game);
                 Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game);
                 if (sourcePermanent != null) {
diff --git a/Mage.Sets/src/mage/cards/t/TwilightProphet.java b/Mage.Sets/src/mage/cards/t/TwilightProphet.java
index 1d09e42beb..bdc5347216 100644
--- a/Mage.Sets/src/mage/cards/t/TwilightProphet.java
+++ b/Mage.Sets/src/mage/cards/t/TwilightProphet.java
@@ -86,7 +86,7 @@ class TwilightProphetEffect extends OneShotEffect {
             if (card != null) {
                 controller.revealCards(sourceObject.getIdName(), new CardsImpl(card), game);
                 controller.moveCards(card, Zone.HAND, source, game);
-                game.applyEffects();
+                game.getState().processAction(game);
                 int amount = card.getConvertedManaCost();
                 if (amount > 0) {
                     new LoseLifeOpponentsEffect(amount).apply(game, source);
diff --git a/Mage.Sets/src/mage/cards/v/VesuvanShapeshifter.java b/Mage.Sets/src/mage/cards/v/VesuvanShapeshifter.java
index 234266a6fa..3d8109bd95 100644
--- a/Mage.Sets/src/mage/cards/v/VesuvanShapeshifter.java
+++ b/Mage.Sets/src/mage/cards/v/VesuvanShapeshifter.java
@@ -120,7 +120,7 @@ class VesuvanShapeshifterEffect extends OneShotEffect {
                 if (copyFromCreature != null) {
                     game.copyPermanent(Duration.Custom, copyFromCreature, copyToCreature.getId(), source, new VesuvanShapeShifterFaceUpApplier());
                     source.getTargets().clear();
-                    game.applyEffects(); // needed to get effects ready if copy happens in replacment and the copied abilities react of the same event (e.g. turn face up)
+                    game.getState().processAction(game); // needed to get effects ready if copy happens in replacment and the copied abilities react of the same event (e.g. turn face up)
                     return true;
                 }
             }
diff --git a/Mage.Sets/src/mage/cards/v/Victimize.java b/Mage.Sets/src/mage/cards/v/Victimize.java
index 89a75c8500..159bbf14b3 100644
--- a/Mage.Sets/src/mage/cards/v/Victimize.java
+++ b/Mage.Sets/src/mage/cards/v/Victimize.java
@@ -64,7 +64,7 @@ class VictimizeEffect extends OneShotEffect {
         if (controller != null) {
             SacrificeTargetCost cost = new SacrificeTargetCost(new TargetControlledCreaturePermanent(FILTER_CONTROLLED_CREATURE_SHORT_TEXT));
             if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), false, null)) {
-                game.applyEffects(); // To end effects of the sacrificed creature
+                game.getState().processAction(game); // To end effects of the sacrificed creature
                 controller.moveCards(new CardsImpl(getTargetPointer().getTargets(game, source)).getCards(game),
                         Zone.BATTLEFIELD, source, game, true, false, false, null);
             }
diff --git a/Mage.Sets/src/mage/cards/w/WarpWorld.java b/Mage.Sets/src/mage/cards/w/WarpWorld.java
index 08be3d7186..fa93b55655 100644
--- a/Mage.Sets/src/mage/cards/w/WarpWorld.java
+++ b/Mage.Sets/src/mage/cards/w/WarpWorld.java
@@ -97,7 +97,7 @@ class WarpWorldEffect extends OneShotEffect {
             }
         }
 
-        game.applyEffects(); // so effects from creatures that were on the battlefield won't trigger from draw or later put into play
+        game.getState().processAction(game); // so effects from creatures that were on the battlefield won't trigger from draw or later put into play
 
         Map<UUID, CardsImpl> cardsRevealed = new HashMap<>();
 
@@ -111,7 +111,7 @@ class WarpWorldEffect extends OneShotEffect {
                 cardsRevealed.put(player.getId(), cards);
             }
         }
-        game.applyEffects();
+        game.getState().processAction(game);
         // put artifacts, creaturs and lands onto the battlefield
         for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) {
             Player player = game.getPlayer(playerId);
@@ -129,7 +129,7 @@ class WarpWorldEffect extends OneShotEffect {
                 player.moveCards(toBattlefield, Zone.BATTLEFIELD, source, game);
             }
         }
-        game.applyEffects();
+        game.getState().processAction(game);
         // put enchantments onto the battlefield
         for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) {
             Player player = game.getPlayer(playerId);
diff --git a/Mage.Sets/src/mage/cards/w/WaveOfVitriol.java b/Mage.Sets/src/mage/cards/w/WaveOfVitriol.java
index 77496dc4f9..528d4f14ec 100644
--- a/Mage.Sets/src/mage/cards/w/WaveOfVitriol.java
+++ b/Mage.Sets/src/mage/cards/w/WaveOfVitriol.java
@@ -92,7 +92,7 @@ class WaveOfVitriolEffect extends OneShotEffect {
                     }
                 }
             }
-            game.applyEffects();
+            game.getState().processAction(game);
             Cards toBattlefield = new CardsImpl();
             Set<Player> playersToShuffle = new LinkedHashSet<>();
             for (Map.Entry<Player, Integer> entry : sacrificedLands.entrySet()) {
diff --git a/Mage.Sets/src/mage/cards/w/WidespreadBrutality.java b/Mage.Sets/src/mage/cards/w/WidespreadBrutality.java
index bc301ae444..5cdb9dd9f1 100644
--- a/Mage.Sets/src/mage/cards/w/WidespreadBrutality.java
+++ b/Mage.Sets/src/mage/cards/w/WidespreadBrutality.java
@@ -61,7 +61,7 @@ class WidespreadBrutalityEffect extends OneShotEffect {
         if (amassedArmy == null) {
             return false;
         }
-        game.applyEffects();
+        game.getState().processAction(game);
         int power = amassedArmy.getPower().getValue();
         for (Permanent permanent : game.getBattlefield().getActivePermanents(source.getControllerId(), game)) {
             if (permanent != null && permanent.isCreature() && !permanent.hasSubtype(SubType.ARMY, game)) {
diff --git a/Mage.Sets/src/mage/cards/y/YorvoLordOfGarenbrig.java b/Mage.Sets/src/mage/cards/y/YorvoLordOfGarenbrig.java
index 580cbdc595..2cfb09ad0e 100644
--- a/Mage.Sets/src/mage/cards/y/YorvoLordOfGarenbrig.java
+++ b/Mage.Sets/src/mage/cards/y/YorvoLordOfGarenbrig.java
@@ -93,7 +93,7 @@ class YorvoLordOfGarenbrigEffect extends OneShotEffect {
         if (permanent == null) {
             return true;
         }
-        game.applyEffects();
+        game.getState().processAction(game);
         if (permanent.getPower().getValue() > sourcePerm.getPower().getValue()) {
             sourcePerm.addCounters(CounterType.P1P1.createInstance(), source, game);
         }
diff --git a/Mage.Sets/src/mage/cards/z/ZombieApocalypse.java b/Mage.Sets/src/mage/cards/z/ZombieApocalypse.java
index 530bd9e7a1..5c06588625 100644
--- a/Mage.Sets/src/mage/cards/z/ZombieApocalypse.java
+++ b/Mage.Sets/src/mage/cards/z/ZombieApocalypse.java
@@ -66,7 +66,7 @@ class ZombieApocalypseEffect extends OneShotEffect {
         Player controller = game.getPlayer(source.getControllerId());
         if (controller != null) {
             controller.moveCards(controller.getGraveyard().getCards(filterZombie, game), Zone.BATTLEFIELD, source, game, true, false, false, null);
-            game.applyEffects();
+            game.getState().processAction(game);
             for (Permanent permanent : game.getBattlefield().getActivePermanents(
                     new FilterPermanent(SubType.HUMAN, "Humans"), source.getControllerId(), game)) {
                 permanent.destroy(source.getSourceId(), game, false);

From 066f24c90971fc56d0a4bb05d156abec160374ce Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 8 Jun 2020 10:00:32 -0400
Subject: [PATCH 150/586] Implemented Bolt Hound

---
 Mage.Sets/src/mage/cards/b/BoltHound.java | 45 +++++++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java  |  1 +
 Utils/mtg-cards-data.txt                  |  1 +
 3 files changed, 47 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/b/BoltHound.java

diff --git a/Mage.Sets/src/mage/cards/b/BoltHound.java b/Mage.Sets/src/mage/cards/b/BoltHound.java
new file mode 100644
index 0000000000..abda305699
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/b/BoltHound.java
@@ -0,0 +1,45 @@
+package mage.cards.b;
+
+import mage.MageInt;
+import mage.abilities.common.AttacksTriggeredAbility;
+import mage.abilities.effects.common.continuous.BoostControlledEffect;
+import mage.abilities.keyword.HasteAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class BoltHound extends CardImpl {
+
+    public BoltHound(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}");
+
+        this.subtype.add(SubType.ELEMENTAL);
+        this.subtype.add(SubType.DOG);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(2);
+
+        // Haste
+        this.addAbility(HasteAbility.getInstance());
+
+        // Whenever Bolt Hound attacks, other creatures you control get +1/+0 until end of turn.
+        this.addAbility(new AttacksTriggeredAbility(new BoostControlledEffect(
+                1, 0, Duration.EndOfTurn, true
+        ), false));
+    }
+
+    private BoltHound(final BoltHound card) {
+        super(card);
+    }
+
+    @Override
+    public BoltHound copy() {
+        return new BoltHound(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 0a39c2036a..90c475f098 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -39,6 +39,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Baneslayer Angel", 6, Rarity.MYTHIC, mage.cards.b.BaneslayerAngel.class));
         cards.add(new SetCardInfo("Basri's Aegis", 322, Rarity.RARE, mage.cards.b.BasrisAegis.class));
         cards.add(new SetCardInfo("Basri, Devoted Paladin", 320, Rarity.MYTHIC, mage.cards.b.BasriDevotedPaladin.class));
+        cards.add(new SetCardInfo("Bolt Hound", 131, Rarity.UNCOMMON, mage.cards.b.BoltHound.class));
         cards.add(new SetCardInfo("Carrion Grub", 92, Rarity.UNCOMMON, mage.cards.c.CarrionGrub.class));
         cards.add(new SetCardInfo("Chandra's Firemaw", 333, Rarity.RARE, mage.cards.c.ChandrasFiremaw.class));
         cards.add(new SetCardInfo("Chandra, Flame's Catalyst", 332, Rarity.MYTHIC, mage.cards.c.ChandraFlamesCatalyst.class));
diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt
index ebb0d9f1fb..a11ca14ed5 100644
--- a/Utils/mtg-cards-data.txt
+++ b/Utils/mtg-cards-data.txt
@@ -37460,6 +37460,7 @@ Liliana, Waker of the Dead|Core Set 2021|108|M|{2}{B}{B}|Legendary Planeswalker
 Necromentia|Core Set 2021|116|R|{1}{B}{B}|Sorcery|||Choose a card name other than a basic land card name. Search target opponent's graveyard, hand, and library for any number of cards with that name and exile them. That player shuffles their library, then creates a 2/2 black Zombie creature token for each card exiled from their hand this way.|
 Peer into the Abyss|Core Set 2021|117|R|{4}{B}{B}{B}|Sorcery|||Target player draws cards equal to half the number of cards in their library and loses half their life. Round up each time.|
 Vito, Thorn of the Dusk Rose|Core Set 2021|127|R|{2}{B}|Legendary Creature - Vampire Cleric|1|3|Whenever you gain life, target opponent loses that much life.${3}{B}{B}: Creatures you control gain lifelink until end of turn.|
+Bolt Hound|Core Set 2021|131|U|{2}{R}|Creature - Elemental Dog|2|2|Haste$Whenever Bolt Hound attacks, other creatures you control get +1/+0 until end of turn.|
 Double Vision|Core Set 2021|142|R|{3}{R}{R}|Enchantment|||Whenever you cast your first instant or sorcery spell each turn, copy that spell. You may choose new targets for the copy.|
 Gadrak, the Crown-Scourge|Core Set 2021|146|R|{2}{R}|Legendary Creature - Dragon|5|4|Flying$Gadrak, the Crown-Scourge can't attack unless you control four or more artifacts.$At the beginning of your end step, create a Treasure token for each nontoken creature that died this turn.|
 Azusa, Lost but Seeking|Core Set 2021|173|R|{2}{G}|Legendary Creature - Human Monk|1|2|You may play two additional lands on each of your turns.|

From 0bdbd36d2da1167d1e67c4aff20de1c079bb9109 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Mon, 8 Jun 2020 17:16:12 +0200
Subject: [PATCH 151/586] * Added test from #6349.

---
 .../cards/triggers/DivineVisitationTest.java  | 81 +++++++++++++++++++
 1 file changed, 81 insertions(+)
 create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/triggers/DivineVisitationTest.java

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/DivineVisitationTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/DivineVisitationTest.java
new file mode 100644
index 0000000000..c6829b9fae
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/DivineVisitationTest.java
@@ -0,0 +1,81 @@
+package org.mage.test.cards.triggers;
+
+import mage.ObjectColor;
+import mage.constants.CardType;
+import mage.constants.PhaseStep;
+import mage.constants.SubType;
+import mage.constants.Zone;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestPlayerBase;
+
+/**
+ *
+ * @author luziferius
+ */
+public class DivineVisitationTest extends CardTestPlayerBase {
+
+    /**
+     * Test case for issue #6349. Divine Visitation should not replace Treasure tokens created by Smothering Tithe
+     */
+    @Test
+    public void testDivineVisitationDoesNotReplaceNoncreatureTokens() {
+
+        // If one or more creature tokens would be created under your control,
+        // that many 4/4 white Angel creature tokens with flying and vigilance are created instead.
+        addCard(Zone.BATTLEFIELD, playerA, "Divine Visitation");
+        // Whenever an opponent draws a card, that player may pay {2}.
+        // If the player doesn’t, you create a Treasure token.
+        // (It’s an artifact with “{T}, Sacrifice this artifact: Add one mana of any color.”)
+        addCard(Zone.BATTLEFIELD, playerA, "Smothering Tithe");
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 1);
+        // Target player draws three cards.
+        addCard(Zone.HAND, playerA, "Ancestral Recall"); // {U}
+        // Let the opponent draw
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ancestral Recall", playerB);
+        setChoice(playerA, "Whenever an opponent draws a card", 2); // choose order of triggers
+        setChoice(playerB, "No", 3); // Decline to pay 2
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        
+        setStrictChooseMode(true);
+        execute();
+        assertAllCommandsUsed();
+        
+        assertHandCount(playerB, 3);
+        assertPermanentCount(playerA, "Treasure", 3);
+        assertType("Treasure", CardType.ARTIFACT, SubType.TREASURE);
+        assertNotType("Treasure", CardType.CREATURE);
+        assertNotSubtype("Treasure", SubType.ANGEL);
+        assertPermanentCount(playerA, "Angel", 0);
+        assertPermanentCount(playerA, 6);
+        assertGraveyardCount(playerA, 1);
+    }
+
+    @Test
+    public void testDivineVisitationReplacesCreatureTokens() {
+
+        // If one or more creature tokens would be created under your control,
+        // that many 4/4 white Angel creature tokens with flying and vigilance are created instead.
+        addCard(Zone.BATTLEFIELD, playerA, "Divine Visitation");
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
+
+        // Create two 1/1 red Goblin creature tokens.
+        addCard(Zone.HAND, playerA, "Dragon Fodder");
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dragon Fodder");
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        
+        setStrictChooseMode(true);
+        execute();
+        assertAllCommandsUsed();
+
+        assertGraveyardCount(playerA, 1);
+        assertPermanentCount(playerA, 5);
+        assertPermanentCount(playerA, "Goblin", 0);
+        assertPermanentCount(playerA, "Angel", 2);
+        assertType("Angel", CardType.CREATURE, SubType.ANGEL);
+        assertColor(playerA, "Angel", ObjectColor.WHITE, true);
+        assertColor(playerA, "Angel", ObjectColor.RED, false);
+        assertPowerToughness(playerA, "Angel", 4,4);
+        assertNotSubtype("Angel", SubType.GOBLIN);
+        
+    }
+}
\ No newline at end of file

From 84ec743b075c6d5ba4fcf1c62d3651e4c6c1db26 Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Mon, 8 Jun 2020 17:04:49 -0500
Subject: [PATCH 152/586] - Fixed #6599 #6405 #4999 #4270

---
 Mage/src/main/java/mage/game/combat/Combat.java | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/Mage/src/main/java/mage/game/combat/Combat.java b/Mage/src/main/java/mage/game/combat/Combat.java
index 32d834b1a1..3dff44f399 100644
--- a/Mage/src/main/java/mage/game/combat/Combat.java
+++ b/Mage/src/main/java/mage/game/combat/Combat.java
@@ -433,7 +433,8 @@ public class Combat implements Serializable, Copyable<Combat> {
             // check if a creature has to attack
             for (Map.Entry<RequirementEffect, Set<Ability>> entry : game.getContinuousEffects().getApplicableRequirementEffects(creature, false, game).entrySet()) {
                 RequirementEffect effect = entry.getKey();
-                if (effect.mustAttack(game)) {
+                if (effect.mustAttack(game)
+                        && checkAttackRestrictions(player, game)) { // needed for Goad Effect
                     mustAttack = true;
                     for (Ability ability : entry.getValue()) {
                         UUID defenderId = effect.mustAttackDefender(ability, game);

From 9348e2f61592f2a0439f9f292e272701f4abb170 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 8 Jun 2020 19:20:28 -0400
Subject: [PATCH 153/586] updated M21 spoiler and reprints

---
 Mage.Sets/src/mage/sets/CoreSet2021.java |  7 +++++++
 Utils/mtg-cards-data.txt                 | 17 +++++++++++++++++
 2 files changed, 24 insertions(+)

diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 90c475f098..6fb20269df 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -45,6 +45,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Chandra, Flame's Catalyst", 332, Rarity.MYTHIC, mage.cards.c.ChandraFlamesCatalyst.class));
         cards.add(new SetCardInfo("Chromatic Orrery", 228, Rarity.MYTHIC, mage.cards.c.ChromaticOrrery.class));
         cards.add(new SetCardInfo("Containment Priest", 314, Rarity.RARE, mage.cards.c.ContainmentPriest.class));
+        cards.add(new SetCardInfo("Cultivate", 177, Rarity.UNCOMMON, mage.cards.c.Cultivate.class));
         cards.add(new SetCardInfo("Double Vision", 142, Rarity.RARE, mage.cards.d.DoubleVision.class));
         cards.add(new SetCardInfo("Elder Gargaroth", 179, Rarity.MYTHIC, mage.cards.e.ElderGargaroth.class));
         cards.add(new SetCardInfo("Faith's Fetters", 17, Rarity.UNCOMMON, mage.cards.f.FaithsFetters.class));
@@ -64,19 +65,25 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Liliana, Death Mage", 328, Rarity.MYTHIC, mage.cards.l.LilianaDeathMage.class));
         cards.add(new SetCardInfo("Liliana, Waker of the Dead", 108, Rarity.MYTHIC, mage.cards.l.LilianaWakerOfTheDead.class));
         cards.add(new SetCardInfo("Llanowar Visionary", 193, Rarity.COMMON, mage.cards.l.LlanowarVisionary.class));
+        cards.add(new SetCardInfo("Lorescale Coatl", 221, Rarity.UNCOMMON, mage.cards.l.LorescaleCoatl.class));
         cards.add(new SetCardInfo("Mangara, the Diplomat", 27, Rarity.MYTHIC, mage.cards.m.MangaraTheDiplomat.class));
+        cards.add(new SetCardInfo("Massacre Wurm", 114, Rarity.MYTHIC, mage.cards.m.MassacreWurm.class));
         cards.add(new SetCardInfo("Mystic Skyfish", 326, Rarity.COMMON, mage.cards.m.MysticSkyfish.class));
         cards.add(new SetCardInfo("Necromentia", 116, Rarity.RARE, mage.cards.n.Necromentia.class));
         cards.add(new SetCardInfo("Pack Leader", 29, Rarity.RARE, mage.cards.p.PackLeader.class));
         cards.add(new SetCardInfo("Peer into the Abyss", 117, Rarity.RARE, mage.cards.p.PeerIntoTheAbyss.class));
+        cards.add(new SetCardInfo("Plains", 309, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Predatory Wurm", 338, Rarity.UNCOMMON, mage.cards.p.PredatoryWurm.class));
         cards.add(new SetCardInfo("Quirion Dryad", 198, Rarity.UNCOMMON, mage.cards.q.QuirionDryad.class));
+        cards.add(new SetCardInfo("Radiant Fountain", 248, Rarity.COMMON, mage.cards.r.RadiantFountain.class));
         cards.add(new SetCardInfo("Rain of Revelation", 61, Rarity.UNCOMMON, mage.cards.r.RainOfRevelation.class));
+        cards.add(new SetCardInfo("Revitalize", 31, Rarity.COMMON, mage.cards.r.Revitalize.class));
         cards.add(new SetCardInfo("Rewind", 63, Rarity.UNCOMMON, mage.cards.r.Rewind.class));
         cards.add(new SetCardInfo("Rin and Seri, Inseparable", 278, Rarity.MYTHIC, mage.cards.r.RinAndSeriInseparable.class));
         cards.add(new SetCardInfo("Runed Halo", 32, Rarity.RARE, mage.cards.r.RunedHalo.class));
         cards.add(new SetCardInfo("Shipwreck Dowser", 71, Rarity.UNCOMMON, mage.cards.s.ShipwreckDowser.class));
         cards.add(new SetCardInfo("Sigiled Contender", 323, Rarity.UNCOMMON, mage.cards.s.SigiledContender.class));
+        cards.add(new SetCardInfo("Solemn Simulacrum", 239, Rarity.RARE, mage.cards.s.SolemnSimulacrum.class));
         cards.add(new SetCardInfo("Sparkhunter Masticore", 240, Rarity.RARE, mage.cards.s.SparkhunterMasticore.class));
         cards.add(new SetCardInfo("Spirit of Malevolence", 331, Rarity.COMMON, mage.cards.s.SpiritOfMalevolence.class));
         cards.add(new SetCardInfo("Storm Caller", 335, Rarity.COMMON, mage.cards.s.StormCaller.class));
diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt
index a11ca14ed5..fd1db3d878 100644
--- a/Utils/mtg-cards-data.txt
+++ b/Utils/mtg-cards-data.txt
@@ -37440,9 +37440,14 @@ Dirge Bat|Ikoria: Lair of Behemoths|386|R|{2}{B}{B}|Creature - Bat|3|3|Mutate {4
 Crystalline Giant|Ikoria: Lair of Behemoths|387|R|{3}|Artifact Creature - Giant|3|3|At the beginning of combat on your turn, choose a kind of counter at random that Crystalline Giant doesn't have on it from among flying, first strike, deathtouch, hexproof, lifelink, menace, reach, trample, vigilance, or +1/+1. Put a counter of that kind on Crystalline Giant.|
 Ugin, the Spirit Dragon|Core Set 2021|1|M|{8}|Legendary Planeswalker - Ugin|7|+2: Ugin, the Spirit Dragon deals 3 damage to any target.$−X: Exile each permanent with converted mana cost X or less that's one or more colors.$−10: You gain 7 life, draw seven cards, then put up to seven permanent cards from your hand onto the battlefield.|
 Baneslayer Angel|Core Set 2021|6|M|{3}{W}{W}|Creature - Angel|5|5|Flying, first strike, lifelink, protection from Demons and from Dragons|
+Basri Ket|Core Set 2021|7|M|{1}{W}{W}|Legendary Planeswalker - Basri|3|+1: Put a +1/+1 counter on up to one target creature. It gains indestructible until end of turn.$−2: Whenever one or more nontoken creatures attack this turn, create that many 1/1 white Soldier creature tokens that are tapped and attacking.$−6: You get an emblem with "At the beginning of combat on your turn, create a 1/1 white Soldier creature token, then put a +1/+1 counter on each creature you control."|
+Basri's Acolyte|Core Set 2021|8|C|{2}{W}{W}|Creature - Cat Cleric|2|3|Lifelink$When Basri's Acolyte enters the battlefield, put a +1/+1 counter on each of up to two target creatures you control.|
+Basri's Lieutenant|Core Set 2021|9|R|{3}{W}|Creature - Human Knight|3|4|Vigilance, protection from multicolored$When Basri's Lieutenant enters the battlefield, put a +1/+1 counter on target creature you control.$Whenever Basri's Lieutenant or another creature you control dies, if it had a +1/+1 counter on it, create a 2/2 white Knight creature token with vigilance.|
+Basri's Solidarity|Core Set 2021|10|U|{1}{W}|Sorcery|||Put a +1/+1 counter on each creature you control.|
 Faith's Fetters|Core Set 2021|17|U|{3}{W}|Enchantment - Aura|||Enchant permanent$When Faith's Fetters enters the battlefield, you gain 4 life.$Enchanted permanent can't attack or block, and its activated abilities can't be activated unless they're mana abilities.|
 Mangara, the Diplomat|Core Set 2021|27|M|{3}{W}|Legendary Creature - Human Cleric|2|4|Lifelink$Whenever an opponent attacks with creatures, if two or more of those creatures are attacking you and/or a planeswalker you control, draw a card.$Whenever an opponent casts their second spell each turn, draw a card.|
 Pack Leader|Core Set 2021|29|R|{1}{W}|Creature - Dog|2|2|Other Dogs you control get +1/+1.$Whenever Pack Leader attacks, prevent all combat damage that would be dealt this turn to Dogs you control.|
+Revitalize|Core Set 2021|31|C|{1}{W}|Instant|||You gain 3 life.$Draw a card.|
 Runed Halo|Core Set 2021|32|R|{W}{W}|Enchantment|||As Runed Halo enters the battlefield, choose a card name.$You have protection from the chosen name.|
 Jeskai Elder|Core Set 2021|53|U|{1}{U}|Creature - Human Monk|1|2|Prowess$Whenever Jeskai Elder deals combat damage to a player, you may draw a card. If you do, discard a card.|
 Rain of Revelation|Core Set 2021|61|U|{3}{U}|Instant|||Draw three cards, then discard a card.|
@@ -37455,15 +37460,19 @@ Teferi's Tutelage|Core Set 2021|78|U|{2}{U}|Enchantment|||When Teferi's Tutelage
 Tide Skimmer|Core Set 2021|79|U|{3}{U}|Creature - Drake|2|3|Flying$Whenever you attack with two or more creatures with flying, draw a card.|
 Bad Deal|Core Set 2021|89|U|{4}{B}{B}|Sorcery|||You draw two cards and each opponent discards two cards. Each player loses 2 life.|
 Carrion Grub|Core Set 2021|92|U|{3}{B}|Creature - Insect|0|5|Carrion Grub gets +X/+0, where X is the greatest power among creature cards in your graveyard.$When Carrion Grub enters the battlefield, mill four cards.|
+Eliminate|Core Set 2021|97|U|{1}{B}|Instant|||Destroy target creature or planeswalker with converted mana cost 3 or less.|
 Grim Tutor|Core Set 2021|103|M|{1}{B}{B}|Sorcery|||Search your library for a card and put that card into your hand, then shuffle your library. You lose 3 life.|
 Liliana, Waker of the Dead|Core Set 2021|108|M|{2}{B}{B}|Legendary Planeswalker - Liliana|4|+1: Each player discards a card. Each opponent who can't loses 3 life.$−3: Target creature gets -X/-X until end of turn, where X is the number of cards in your graveyard.$−7: You get an emblem with "At the beginning of combat on your turn, put target creature card from a graveyard onto the battlefield under your control. It gains haste."|
+Massacre Wurm|Core Set 2021|114|M|{3}{B}{B}{B}|Creature - Wurm|6|5|When Massacre Wurm enters the battlefield, creatures your opponents control get -2/-2 until end of turn.$Whenever a creature an opponent controls dies, that player loses 2 life.|
 Necromentia|Core Set 2021|116|R|{1}{B}{B}|Sorcery|||Choose a card name other than a basic land card name. Search target opponent's graveyard, hand, and library for any number of cards with that name and exile them. That player shuffles their library, then creates a 2/2 black Zombie creature token for each card exiled from their hand this way.|
 Peer into the Abyss|Core Set 2021|117|R|{4}{B}{B}{B}|Sorcery|||Target player draws cards equal to half the number of cards in their library and loses half their life. Round up each time.|
+Pestilent Haze|Core Set 2021|118|U|{1}{B}{B}|Sorcery|||Choose one —$• All creatures get -2/-2 until end of turn.$• Remove two loyalty counters from each planeswalker.|
 Vito, Thorn of the Dusk Rose|Core Set 2021|127|R|{2}{B}|Legendary Creature - Vampire Cleric|1|3|Whenever you gain life, target opponent loses that much life.${3}{B}{B}: Creatures you control gain lifelink until end of turn.|
 Bolt Hound|Core Set 2021|131|U|{2}{R}|Creature - Elemental Dog|2|2|Haste$Whenever Bolt Hound attacks, other creatures you control get +1/+0 until end of turn.|
 Double Vision|Core Set 2021|142|R|{3}{R}{R}|Enchantment|||Whenever you cast your first instant or sorcery spell each turn, copy that spell. You may choose new targets for the copy.|
 Gadrak, the Crown-Scourge|Core Set 2021|146|R|{2}{R}|Legendary Creature - Dragon|5|4|Flying$Gadrak, the Crown-Scourge can't attack unless you control four or more artifacts.$At the beginning of your end step, create a Treasure token for each nontoken creature that died this turn.|
 Azusa, Lost but Seeking|Core Set 2021|173|R|{2}{G}|Legendary Creature - Human Monk|1|2|You may play two additional lands on each of your turns.|
+Cultivate|Core Set 2021|177|U|{2}{G}|Sorcery|||Search your library for up to two basic land cards, reveal those cards, and put one onto the battlefield tapped and the other into your hand. Then shuffle your library.|
 Elder Gargaroth|Core Set 2021|179|M|{3}{G}{G}|Creature - Beast|6|6|Vigilance, reach, trample$Whenever Elder Gargaroth attacks or blocks, choose one —$• Create a 3/3 green Beast creature token.$• You gain 3 life.$• Draw a card.|
 Fierce Empath|Core Set 2021|181|U|{2}{G}|Creature - Elf|1|1|When Fierce Empath enters the battlefield, you may search your library for a creature card with converted mana cost 6 or greater, reveal it, put it into your hand, then shuffle your library.|
 Fungal Rebirth|Core Set 2021|182|U|{2}{G}|Instant|||Return target permanent card from your graveyard to your hand. If a creature died this turn, create two 1/1 green Saproling creature tokens.|
@@ -37471,10 +37480,18 @@ Llanowar Visionary|Core Set 2021|193|C|{2}{G}|Creature - Elf Druid|2|2|When Llan
 Primal Might|Core Set 2021|197|R|{X}{G}|Sorcery|||Target creature you control gets +X/+X until end of turn. Then it fights up to one target creature you don't control.|
 Quirion Dryad|Core Set 2021|198|U|{1}{G}|Creature - Dryad|1|1|Whenever you cast a white, blue, black, or red spell, put a +1/+1 counter on Quirion Dryad.|
 Indulging Patrician|Core Set 2021|219|U|{1}{W}{B}|Creature - Vampire Noble|1|4|Flying$Lifelink$At the beginning of your end step, if you gained 3 or more life this turn, each opponent loses 3 life.|
+Lorescale Coatl|Core Set 2021|221|U|{1}{G}{U}|Creature - Snake|2|2|Whenever you draw a card, you may put a +1/+1 counter on Lorescale Coatl.|
 Chromatic Orrery|Core Set 2021|228|M|{7}|Legendary Artifact|||You may spend mana as though it were mana of any color.${T}: Add {C}{C}{C}{C}{C}.${5}, {T}: Draw a card for each color among permanents you control.|
+Solemn Simulacrum|Core Set 2021|239|R|{4}|Artifact Creature - Golem|2|2|When Solemn Simulacrum enters the battlefield, you may search your library for a basic land card, put that card onto the battlefield tapped, then shuffle your library.$When Solemn Simulacrum dies, you may draw a card.|
 Sparkhunter Masticore|Core Set 2021|240|R|{3}|Artifact Creature - Masticore|3|4|As an additional cost to cast this spell, discard a card.$Protection from planeswalkers${1}: Sparkhunter Masticore deals 1 damage to target planeswalker.${3}: Sparkhunter Masticore gains indestructible until end of turn.|
 Tormod's Crypt|Core Set 2021|241|U|{0}|Artifact|||{T}, Sacrifice Tormod's Crypt: Exile all cards from target player's graveyard.|
+Radiant Fountain|Core Set 2021|248|C||Land|||When Radiant Fountain enters the battlefield, you gain 2 life.${T}: Add {C}.|
 Rin and Seri, Inseparable|Core Set 2021|278|M|{1}{R}{G}{W}|Legendary Creature - Dog Cat|4|4|Whenever you cast a Dog spell, create a 1/1 green Cat creature token.$Whenever you cast a Cat spell, create a 1/1 white Dog creature token.${R}{G}{W}, {T}: Rin and Seri, Inseparable deals damage to any target equal to the number of Dogs you control. You gain life equal to the number of Cats you control.|
+Chandra, Heart of Fire|Core Set 2021|301|M|{3}{R}{R}|Legendary Planeswalker - Chandra|5|+1: Discard your hand, then exile the top three cards of your library. Until end of turn, you may play cards exiled this way.$+1: Chandra, Heart of Fire deals 2 damage to any target.$−9: Search your graveyard and library for any number of red instant and/or sorcery spells, exile them, then shuffle your library. You may cast them this turn. Add six {R}.|
+Chandra's Incinerator|Core Set 2021|302|R|{5}{R}|Creature - Elemental|6|6|This spell costs {X} less to cast, where X is the total amount of noncombat damage dealt to your opponents this turn.$Trample$Wheenver a source you control deals noncombat damage to an opponent, Chandra's Incinerator deals that much damage to target creature or planeswalker that player controls.|
+Chandra's Magmutt|Core Set 2021|303|C|{1}{R}|Creature - Elemental Dog|2|2|{T}: Chandra's Magmutt deals 1 damage to target player or planeswalker.|
+Chandra's Pyreling|Core Set 2021|304|U|{1}{R}|Creature - Elemental Lizard|1|3|Whenever a source you control deals noncombat damage to an opponent, Chandra's Pyreling gets +1/+0 and gains double strike until end of turn.|
+Plains|Core Set 2021|309|C||Basic Land - Plains|||({T}: Add {W}.)|
 Island|Core Set 2021|310|C||Basic Land - Island|||({T}: Add {U}.)|
 Containment Priest|Core Set 2021|314|R|{1}{W}|Creature - Human Cleric|2|2|Flash$If a nontoken creature would enter the battlefield and it wasn't cast, exile it instead.|
 Basri, Devoted Paladin|Core Set 2021|320|M|{4}{W}{W}|Legendary Planeswalker - Basri|4|+1: Put a +1/+1 counter on up to one target creature. It gains vigilance until end of turn.$−1: Whenever a creature attacks this turn, put a +1/+1 counter on it.$−6: Creatures you control get +2/+2 and gain flying until end of turn.|

From fc2297181a202c1c545788276a2a6f4e906c12e3 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 8 Jun 2020 19:21:36 -0400
Subject: [PATCH 154/586] Implemented Chandra's Magemutt

---
 .../src/mage/cards/c/ChandrasMagmutt.java     | 43 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 44 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/c/ChandrasMagmutt.java

diff --git a/Mage.Sets/src/mage/cards/c/ChandrasMagmutt.java b/Mage.Sets/src/mage/cards/c/ChandrasMagmutt.java
new file mode 100644
index 0000000000..e4e617c127
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/c/ChandrasMagmutt.java
@@ -0,0 +1,43 @@
+package mage.cards.c;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.common.TapSourceCost;
+import mage.abilities.effects.common.DamageTargetEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.target.common.TargetPlayerOrPlaneswalker;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class ChandrasMagmutt extends CardImpl {
+
+    public ChandrasMagmutt(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}");
+
+        this.subtype.add(SubType.ELEMENTAL);
+        this.subtype.add(SubType.DOG);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(2);
+
+        // {T}: Chandra's Magmutt deals 1 damage to target player or planeswalker.
+        Ability ability = new SimpleActivatedAbility(new DamageTargetEffect(1), new TapSourceCost());
+        ability.addTarget(new TargetPlayerOrPlaneswalker());
+        this.addAbility(ability);
+    }
+
+    private ChandrasMagmutt(final ChandrasMagmutt card) {
+        super(card);
+    }
+
+    @Override
+    public ChandrasMagmutt copy() {
+        return new ChandrasMagmutt(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 6fb20269df..a011d4b171 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -42,6 +42,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Bolt Hound", 131, Rarity.UNCOMMON, mage.cards.b.BoltHound.class));
         cards.add(new SetCardInfo("Carrion Grub", 92, Rarity.UNCOMMON, mage.cards.c.CarrionGrub.class));
         cards.add(new SetCardInfo("Chandra's Firemaw", 333, Rarity.RARE, mage.cards.c.ChandrasFiremaw.class));
+        cards.add(new SetCardInfo("Chandra's Magmutt", 303, Rarity.COMMON, mage.cards.c.ChandrasMagmutt.class));
         cards.add(new SetCardInfo("Chandra, Flame's Catalyst", 332, Rarity.MYTHIC, mage.cards.c.ChandraFlamesCatalyst.class));
         cards.add(new SetCardInfo("Chromatic Orrery", 228, Rarity.MYTHIC, mage.cards.c.ChromaticOrrery.class));
         cards.add(new SetCardInfo("Containment Priest", 314, Rarity.RARE, mage.cards.c.ContainmentPriest.class));

From c1e37c89b04fe3cb6107dcd885a00ce504d710e0 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 8 Jun 2020 19:22:22 -0400
Subject: [PATCH 155/586] Implemented Pestilent Haze

---
 Mage.Sets/src/mage/cards/p/PestilentHaze.java | 70 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 71 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/p/PestilentHaze.java

diff --git a/Mage.Sets/src/mage/cards/p/PestilentHaze.java b/Mage.Sets/src/mage/cards/p/PestilentHaze.java
new file mode 100644
index 0000000000..424901bd53
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/p/PestilentHaze.java
@@ -0,0 +1,70 @@
+package mage.cards.p;
+
+import mage.abilities.Ability;
+import mage.abilities.Mode;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.continuous.BoostAllEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.Outcome;
+import mage.counters.CounterType;
+import mage.filter.StaticFilters;
+import mage.game.Game;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class PestilentHaze extends CardImpl {
+
+    public PestilentHaze(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}{B}");
+
+        // Choose one —
+        // • All creatures get -2/-2 until end of turn.
+        this.getSpellAbility().addEffect(new BoostAllEffect(-2, -2, Duration.EndOfTurn));
+
+        // • Remove two loyalty counters from each planeswalker.
+        this.getSpellAbility().addMode(new Mode(new PestilentHazeEffect()));
+    }
+
+    private PestilentHaze(final PestilentHaze card) {
+        super(card);
+    }
+
+    @Override
+    public PestilentHaze copy() {
+        return new PestilentHaze(this);
+    }
+}
+
+class PestilentHazeEffect extends OneShotEffect {
+
+    PestilentHazeEffect() {
+        super(Outcome.Benefit);
+        staticText = "remove two loyalty counters from each planeswalker";
+    }
+
+    private PestilentHazeEffect(final PestilentHazeEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public PestilentHazeEffect copy() {
+        return new PestilentHazeEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        game.getBattlefield()
+                .getActivePermanents(
+                        StaticFilters.FILTER_PERMANENT_PLANESWALKER,
+                        source.getControllerId(), source.getSourceId(), game
+                ).stream()
+                .forEach(permanent -> permanent.removeCounters(CounterType.LOYALTY.createInstance(2), game));
+        return true;
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index a011d4b171..33449421a7 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -73,6 +73,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Necromentia", 116, Rarity.RARE, mage.cards.n.Necromentia.class));
         cards.add(new SetCardInfo("Pack Leader", 29, Rarity.RARE, mage.cards.p.PackLeader.class));
         cards.add(new SetCardInfo("Peer into the Abyss", 117, Rarity.RARE, mage.cards.p.PeerIntoTheAbyss.class));
+        cards.add(new SetCardInfo("Pestilent Haze", 118, Rarity.UNCOMMON, mage.cards.p.PestilentHaze.class));
         cards.add(new SetCardInfo("Plains", 309, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Predatory Wurm", 338, Rarity.UNCOMMON, mage.cards.p.PredatoryWurm.class));
         cards.add(new SetCardInfo("Quirion Dryad", 198, Rarity.UNCOMMON, mage.cards.q.QuirionDryad.class));

From 50b3c03dd6d5be188241565a8003fd66ce5bd557 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 8 Jun 2020 19:41:46 -0400
Subject: [PATCH 156/586] Implemented Basri's Solidarity

---
 .../src/mage/cards/b/BasrisSolidarity.java    | 34 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 35 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/b/BasrisSolidarity.java

diff --git a/Mage.Sets/src/mage/cards/b/BasrisSolidarity.java b/Mage.Sets/src/mage/cards/b/BasrisSolidarity.java
new file mode 100644
index 0000000000..3af3b9c814
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/b/BasrisSolidarity.java
@@ -0,0 +1,34 @@
+package mage.cards.b;
+
+import mage.abilities.effects.common.counter.AddCountersAllEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.counters.CounterType;
+import mage.filter.StaticFilters;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class BasrisSolidarity extends CardImpl {
+
+    public BasrisSolidarity(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{W}");
+
+        // Put a +1/+1 counter on each creature you control.
+        this.getSpellAbility().addEffect(new AddCountersAllEffect(
+                CounterType.P1P1.createInstance(), StaticFilters.FILTER_CONTROLLED_CREATURE
+        ));
+    }
+
+    private BasrisSolidarity(final BasrisSolidarity card) {
+        super(card);
+    }
+
+    @Override
+    public BasrisSolidarity copy() {
+        return new BasrisSolidarity(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 33449421a7..80133c2299 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -38,6 +38,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Bad Deal", 89, Rarity.UNCOMMON, mage.cards.b.BadDeal.class));
         cards.add(new SetCardInfo("Baneslayer Angel", 6, Rarity.MYTHIC, mage.cards.b.BaneslayerAngel.class));
         cards.add(new SetCardInfo("Basri's Aegis", 322, Rarity.RARE, mage.cards.b.BasrisAegis.class));
+        cards.add(new SetCardInfo("Basri's Solidarity", 10, Rarity.UNCOMMON, mage.cards.b.BasrisSolidarity.class));
         cards.add(new SetCardInfo("Basri, Devoted Paladin", 320, Rarity.MYTHIC, mage.cards.b.BasriDevotedPaladin.class));
         cards.add(new SetCardInfo("Bolt Hound", 131, Rarity.UNCOMMON, mage.cards.b.BoltHound.class));
         cards.add(new SetCardInfo("Carrion Grub", 92, Rarity.UNCOMMON, mage.cards.c.CarrionGrub.class));

From b2ae98e33748da983b4fd668c650282808d6e190 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 8 Jun 2020 19:51:06 -0400
Subject: [PATCH 157/586] Implemented Basri's Acolyte

---
 Mage.Sets/src/mage/cards/b/BasrisAcolyte.java | 59 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 60 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/b/BasrisAcolyte.java

diff --git a/Mage.Sets/src/mage/cards/b/BasrisAcolyte.java b/Mage.Sets/src/mage/cards/b/BasrisAcolyte.java
new file mode 100644
index 0000000000..a1e1a2e520
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/b/BasrisAcolyte.java
@@ -0,0 +1,59 @@
+package mage.cards.b;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.effects.common.counter.AddCountersTargetEffect;
+import mage.abilities.keyword.LifelinkAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.counters.CounterType;
+import mage.filter.FilterPermanent;
+import mage.filter.common.FilterControlledCreaturePermanent;
+import mage.filter.predicate.permanent.AnotherPredicate;
+import mage.target.TargetPermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class BasrisAcolyte extends CardImpl {
+
+    private static final FilterPermanent filter = new FilterControlledCreaturePermanent("other creatures you control");
+
+    static {
+        filter.add(AnotherPredicate.instance);
+    }
+
+    public BasrisAcolyte(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{W}");
+
+        this.subtype.add(SubType.CAT);
+        this.subtype.add(SubType.CLERIC);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(3);
+
+        // Lifelink
+        this.addAbility(LifelinkAbility.getInstance());
+
+        // When Basri's Acolyte enters the battlefield, put a +1/+1 counter on each of up to two other target creatures you control.
+        Ability ability = new EntersBattlefieldTriggeredAbility(
+                new AddCountersTargetEffect(CounterType.P1P1.createInstance())
+                        .setText("put a +1/+1 counter on each of up to two other target creatures you control")
+        );
+        ability.addTarget(new TargetPermanent(0, 2, filter, false));
+        this.addAbility(ability);
+    }
+
+    private BasrisAcolyte(final BasrisAcolyte card) {
+        super(card);
+    }
+
+    @Override
+    public BasrisAcolyte copy() {
+        return new BasrisAcolyte(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 80133c2299..9867bcbd95 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -37,6 +37,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Azusa, Lost but Seeking", 173, Rarity.RARE, mage.cards.a.AzusaLostButSeeking.class));
         cards.add(new SetCardInfo("Bad Deal", 89, Rarity.UNCOMMON, mage.cards.b.BadDeal.class));
         cards.add(new SetCardInfo("Baneslayer Angel", 6, Rarity.MYTHIC, mage.cards.b.BaneslayerAngel.class));
+        cards.add(new SetCardInfo("Basri's Acolyte", 8, Rarity.COMMON, mage.cards.b.BasrisAcolyte.class));
         cards.add(new SetCardInfo("Basri's Aegis", 322, Rarity.RARE, mage.cards.b.BasrisAegis.class));
         cards.add(new SetCardInfo("Basri's Solidarity", 10, Rarity.UNCOMMON, mage.cards.b.BasrisSolidarity.class));
         cards.add(new SetCardInfo("Basri, Devoted Paladin", 320, Rarity.MYTHIC, mage.cards.b.BasriDevotedPaladin.class));

From 39a09ef5383fceca364600ba0cd8cdc10dfd09b6 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 8 Jun 2020 20:23:49 -0400
Subject: [PATCH 158/586] Implemented Eliminate

---
 Mage.Sets/src/mage/cards/e/Eliminate.java | 44 +++++++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java  |  1 +
 2 files changed, 45 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/e/Eliminate.java

diff --git a/Mage.Sets/src/mage/cards/e/Eliminate.java b/Mage.Sets/src/mage/cards/e/Eliminate.java
new file mode 100644
index 0000000000..39423122e3
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/e/Eliminate.java
@@ -0,0 +1,44 @@
+package mage.cards.e;
+
+import mage.abilities.effects.common.DestroyTargetEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.ComparisonType;
+import mage.filter.FilterPermanent;
+import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent;
+import mage.filter.predicate.mageobject.ConvertedManaCostPredicate;
+import mage.target.TargetPermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class Eliminate extends CardImpl {
+
+    private static final FilterPermanent filter = new FilterCreatureOrPlaneswalkerPermanent(
+            "creature or planeswalker with converted mana cost 3 or less"
+    );
+
+    static {
+        filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4));
+    }
+
+    public Eliminate(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{B}");
+
+        // Destroy target creature or planeswalker with converted mana cost 3 or less.
+        this.getSpellAbility().addEffect(new DestroyTargetEffect());
+        this.getSpellAbility().addTarget(new TargetPermanent(filter));
+    }
+
+    private Eliminate(final Eliminate card) {
+        super(card);
+    }
+
+    @Override
+    public Eliminate copy() {
+        return new Eliminate(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 9867bcbd95..2bb97dbda8 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -51,6 +51,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Cultivate", 177, Rarity.UNCOMMON, mage.cards.c.Cultivate.class));
         cards.add(new SetCardInfo("Double Vision", 142, Rarity.RARE, mage.cards.d.DoubleVision.class));
         cards.add(new SetCardInfo("Elder Gargaroth", 179, Rarity.MYTHIC, mage.cards.e.ElderGargaroth.class));
+        cards.add(new SetCardInfo("Eliminate", 97, Rarity.UNCOMMON, mage.cards.e.Eliminate.class));
         cards.add(new SetCardInfo("Faith's Fetters", 17, Rarity.UNCOMMON, mage.cards.f.FaithsFetters.class));
         cards.add(new SetCardInfo("Fierce Empath", 181, Rarity.UNCOMMON, mage.cards.f.FierceEmpath.class));
         cards.add(new SetCardInfo("Fungal Rebirth", 182, Rarity.UNCOMMON, mage.cards.f.FungalRebirth.class));

From 07309003b43471aa3677759aa88cdec55d31d1a1 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 8 Jun 2020 21:01:48 -0400
Subject: [PATCH 159/586] Implemented Chandra's Incinerator

---
 .../src/mage/cards/c/ChandrasIncinerator.java | 168 ++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |   1 +
 2 files changed, 169 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/c/ChandrasIncinerator.java

diff --git a/Mage.Sets/src/mage/cards/c/ChandrasIncinerator.java b/Mage.Sets/src/mage/cards/c/ChandrasIncinerator.java
new file mode 100644
index 0000000000..8081f8614b
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/c/ChandrasIncinerator.java
@@ -0,0 +1,168 @@
+package mage.cards.c;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.SpellAbility;
+import mage.abilities.TriggeredAbilityImpl;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.effects.common.DamageTargetEffect;
+import mage.abilities.effects.common.cost.CostModificationEffectImpl;
+import mage.abilities.keyword.TrampleAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.*;
+import mage.filter.FilterPermanent;
+import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent;
+import mage.filter.predicate.permanent.ControllerIdPredicate;
+import mage.game.Game;
+import mage.game.events.DamagedPlayerEvent;
+import mage.game.events.GameEvent;
+import mage.target.TargetPermanent;
+import mage.util.CardUtil;
+import mage.watchers.Watcher;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class ChandrasIncinerator extends CardImpl {
+
+    public ChandrasIncinerator(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{R}");
+
+        this.subtype.add(SubType.ELEMENTAL);
+        this.power = new MageInt(6);
+        this.toughness = new MageInt(6);
+
+        // This spell costs {X} less to cast, where X is the total amount of noncombat damage dealt to your opponents this turn.
+        this.addAbility(new SimpleStaticAbility(
+                Zone.ALL, new ChandrasIncineratorCostReductionEffect()
+        ), new ChandrasIncineratorWatcher());
+
+        // Trample
+        this.addAbility(TrampleAbility.getInstance());
+
+        // Whenever a source you control deals noncombat damage to an opponent, Chandra's Incinerator deals that much damage to target creature or planeswalker that player controls.
+        this.addAbility(new ChandrasIncineratorTriggeredAbility());
+    }
+
+    private ChandrasIncinerator(final ChandrasIncinerator card) {
+        super(card);
+    }
+
+    @Override
+    public ChandrasIncinerator copy() {
+        return new ChandrasIncinerator(this);
+    }
+}
+
+class ChandrasIncineratorCostReductionEffect extends CostModificationEffectImpl {
+
+    ChandrasIncineratorCostReductionEffect() {
+        super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.REDUCE_COST);
+        staticText = "This spell costs {X} less to cast, where X is the total amount of noncombat damage dealt to your opponents this turn";
+    }
+
+    private ChandrasIncineratorCostReductionEffect(final ChandrasIncineratorCostReductionEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source, Ability abilityToModify) {
+        ChandrasIncineratorWatcher watcher = game.getState().getWatcher(ChandrasIncineratorWatcher.class);
+        if (watcher == null) {
+            return true;
+        }
+        int reductionAmount = watcher.getDamage(source.getControllerId());
+        CardUtil.reduceCost(abilityToModify, Math.max(0, reductionAmount));
+        return true;
+    }
+
+    @Override
+    public boolean applies(Ability abilityToModify, Ability source, Game game) {
+        return abilityToModify instanceof SpellAbility
+                && abilityToModify.getSourceId().equals(source.getSourceId())
+                && game.getCard(abilityToModify.getSourceId()) != null;
+    }
+
+    @Override
+    public ChandrasIncineratorCostReductionEffect copy() {
+        return new ChandrasIncineratorCostReductionEffect(this);
+    }
+}
+
+class ChandrasIncineratorWatcher extends Watcher {
+
+    private final Map<UUID, Integer> damageMap = new HashMap<>();
+
+    ChandrasIncineratorWatcher() {
+        super(WatcherScope.GAME);
+    }
+
+    @Override
+    public void watch(GameEvent event, Game game) {
+        if (event.getType() != GameEvent.EventType.DAMAGED_PLAYER
+                || ((DamagedPlayerEvent) event).isCombatDamage()) {
+            return;
+        }
+        for (UUID playerId : game.getOpponents(event.getPlayerId())) {
+            damageMap.compute(playerId, ((u, i) -> i == null ? 0 : Integer.sum(i, event.getAmount())));
+        }
+    }
+
+    @Override
+    public void reset() {
+        damageMap.clear();
+    }
+
+    int getDamage(UUID playerId) {
+        return damageMap.getOrDefault(playerId, 0);
+    }
+}
+
+class ChandrasIncineratorTriggeredAbility extends TriggeredAbilityImpl {
+
+    ChandrasIncineratorTriggeredAbility() {
+        super(Zone.BATTLEFIELD, null);
+    }
+
+    private ChandrasIncineratorTriggeredAbility(final ChandrasIncineratorTriggeredAbility ability) {
+        super(ability);
+    }
+
+    @Override
+    public boolean checkEventType(GameEvent event, Game game) {
+        return event.getType() == GameEvent.EventType.DAMAGED_PLAYER;
+    }
+
+    @Override
+    public boolean checkTrigger(GameEvent event, Game game) {
+        DamagedPlayerEvent dEvent = (DamagedPlayerEvent) event;
+        if (dEvent.isCombatDamage()
+                || !game.getOpponents(event.getTargetId()).contains(getControllerId())
+                || !event.getPlayerId().equals(getControllerId())) {
+            return false;
+        }
+        this.getEffects().clear();
+        this.addEffect(new DamageTargetEffect(event.getAmount()));
+        FilterPermanent filter = new FilterCreatureOrPlaneswalkerPermanent("creature or planeswalker");
+        filter.add(new ControllerIdPredicate(event.getTargetId()));
+        this.getTargets().clear();
+        this.addTarget(new TargetPermanent(filter));
+        return true;
+    }
+
+    @Override
+    public ChandrasIncineratorTriggeredAbility copy() {
+        return new ChandrasIncineratorTriggeredAbility(this);
+    }
+
+    @Override
+    public String getRule() {
+        return "Whenever a source you control deals noncombat damage to an opponent, " +
+                "{this} deals that much damage to target creature or planeswalker that player controls.";
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 2bb97dbda8..a7402425ff 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -44,6 +44,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Bolt Hound", 131, Rarity.UNCOMMON, mage.cards.b.BoltHound.class));
         cards.add(new SetCardInfo("Carrion Grub", 92, Rarity.UNCOMMON, mage.cards.c.CarrionGrub.class));
         cards.add(new SetCardInfo("Chandra's Firemaw", 333, Rarity.RARE, mage.cards.c.ChandrasFiremaw.class));
+        cards.add(new SetCardInfo("Chandra's Incinerator", 302, Rarity.RARE, mage.cards.c.ChandrasIncinerator.class));
         cards.add(new SetCardInfo("Chandra's Magmutt", 303, Rarity.COMMON, mage.cards.c.ChandrasMagmutt.class));
         cards.add(new SetCardInfo("Chandra, Flame's Catalyst", 332, Rarity.MYTHIC, mage.cards.c.ChandraFlamesCatalyst.class));
         cards.add(new SetCardInfo("Chromatic Orrery", 228, Rarity.MYTHIC, mage.cards.c.ChromaticOrrery.class));

From 3119e7e78cdb7c41779d4ab611e08057ba88e40d Mon Sep 17 00:00:00 2001
From: johnmeat <64497504+johnmeat@users.noreply.github.com>
Date: Tue, 9 Jun 2020 02:51:58 +0100
Subject: [PATCH 160/586] Fixed Unlicensed Disintegration damage part (#6614)

* Damage to creature's controller abilities -- fixed that damage part can be skipped if that creature died/destroyed (example: Unlicensed Disintegration, see #6614)

Co-authored-by: johnm <johnm@WINDOWS-QR5QIIL.lan>
Co-authored-by: Oleg Agafonov <jaydi85@gmail.com>
---
 .../cards/u/UnlicensedDisintegration.java     |   2 +-
 .../destroy/UnlicensedDisintegrationTest.java | 128 ++++++++++++++++++
 .../common/DamageTargetControllerEffect.java  |   2 +-
 3 files changed, 130 insertions(+), 2 deletions(-)
 create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/destroy/UnlicensedDisintegrationTest.java

diff --git a/Mage.Sets/src/mage/cards/u/UnlicensedDisintegration.java b/Mage.Sets/src/mage/cards/u/UnlicensedDisintegration.java
index 5d05edd564..58ea7b8389 100644
--- a/Mage.Sets/src/mage/cards/u/UnlicensedDisintegration.java
+++ b/Mage.Sets/src/mage/cards/u/UnlicensedDisintegration.java
@@ -28,7 +28,7 @@ public final class UnlicensedDisintegration extends CardImpl {
                 new DamageTargetControllerEffect(3),
                 new PermanentsOnTheBattlefieldCondition(new FilterControlledArtifactPermanent()),
                 "If you control an artifact, Unlicensed Disintegration deals 3 damage to that creature's controller"));
-
+        
     }
 
     public UnlicensedDisintegration(final UnlicensedDisintegration card) {
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/destroy/UnlicensedDisintegrationTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/destroy/UnlicensedDisintegrationTest.java
new file mode 100644
index 0000000000..4e0469f3cb
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/destroy/UnlicensedDisintegrationTest.java
@@ -0,0 +1,128 @@
+package org.mage.test.cards.abilities.oneshot.destroy;
+
+import mage.constants.PhaseStep;
+import mage.constants.Zone;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestPlayerBase;
+
+
+/*
+Unlicensed Disintigration - Hi! Noticed that everytime that i succesfully cast 
+Unlicensed Disintigration with an artifact on the board the opponent wont lose 3 life. 
+The creature dies but the last piece of text does not work (teM, 2020-02-24 15:17:36)
+*/
+public class UnlicensedDisintegrationTest extends CardTestPlayerBase{
+    
+    /*
+    Unlicensed Disintegration {1}{B}{R}
+    
+    Destroy target creature. If you control an artifact, 
+    Unlicensed Disintegration deals 3 damage to that creature's controller.
+    
+    
+    Avacyn, Angel of Hope {5}{W}{W}
+    
+    Flying, vigilance, indestructible
+    Other permanents you control have indestructible.
+    */
+    
+    @Test
+    public void testDestroyCreatureLifeLoss(){
+        addCard(Zone.HAND, playerA, "Unlicensed Disintegration");
+        addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears");
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain");
+        addCard(Zone.BATTLEFIELD, playerA, "Swamp",2);
+        
+        // Need an artifact to trigger the damage
+        addCard(Zone.BATTLEFIELD, playerA, "Sol Ring");
+
+        // Play Unlicensed Disintegration, targeting Balduvian Bears
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Unlicensed Disintegration", "Balduvian Bears");
+        
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        
+        setStrictChooseMode(true);
+        execute();
+        assertAllCommandsUsed();
+        
+        assertLife(playerA, 20);
+        assertGraveyardCount(playerA, "Unlicensed Disintegration", 1);
+        assertLife(playerB, 17);
+        assertGraveyardCount(playerB, "Balduvian Bears", 1);
+    }
+  
+    @Test
+    public void testDestroyCreatureLifeLossIndestructible(){
+        addCard(Zone.HAND, playerA, "Unlicensed Disintegration");
+        addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears");
+        addCard(Zone.BATTLEFIELD, playerB, "Avacyn, Angel of Hope");
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain");
+        addCard(Zone.BATTLEFIELD, playerA, "Swamp",2);
+        
+        // Need an artifact to trigger the damage
+        addCard(Zone.BATTLEFIELD, playerA, "Sol Ring");
+
+        // Play Unlicensed Disintegration, targeting Balduvian Bears
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Unlicensed Disintegration", "Balduvian Bears");
+        
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        
+        setStrictChooseMode(true);
+        execute();
+        assertAllCommandsUsed();
+        
+        assertLife(playerA, 20);
+        assertGraveyardCount(playerA, "Unlicensed Disintegration", 1);
+        
+        assertLife(playerB, 17);
+        assertPermanentCount(playerB, "Balduvian Bears", 1);
+        assertPermanentCount(playerB, "Avacyn, Angel of Hope", 1);
+    }
+
+    @Test
+    public void testDestroyCreatureNoLifeLossNoArtifact(){
+        addCard(Zone.HAND, playerA, "Unlicensed Disintegration");
+        addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears");
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain");
+        addCard(Zone.BATTLEFIELD, playerA, "Swamp",2);
+        
+        // Play Unlicensed Disintegration, targeting Balduvian Bears
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Unlicensed Disintegration", "Balduvian Bears");
+        
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        
+        setStrictChooseMode(true);
+        execute();
+        assertAllCommandsUsed();
+        
+        assertLife(playerA, 20);
+        assertGraveyardCount(playerA, "Unlicensed Disintegration", 1);
+        assertLife(playerB, 20);
+        assertGraveyardCount(playerB, "Balduvian Bears", 1);
+    }
+
+    @Test
+    public void testDestroyCreatureNoLifeLossNoArtifactIndestructible(){
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain");
+        addCard(Zone.BATTLEFIELD, playerA, "Swamp",2);  
+        addCard(Zone.HAND, playerA, "Unlicensed Disintegration");
+        addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears");
+        addCard(Zone.BATTLEFIELD, playerB, "Avacyn, Angel of Hope");
+        
+        // Play Unlicensed Disintegration, targeting Balduvian Bears
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Unlicensed Disintegration", "Balduvian Bears");
+        
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        
+        setStrictChooseMode(true);
+        execute();
+        assertAllCommandsUsed();
+        
+        assertLife(playerA, 20);
+        assertGraveyardCount(playerA, "Unlicensed Disintegration", 1);
+        
+        assertLife(playerB, 20);
+        assertPermanentCount(playerB, "Balduvian Bears", 1);
+        assertPermanentCount(playerB, "Avacyn, Angel of Hope", 1);
+    }
+}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/DamageTargetControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DamageTargetControllerEffect.java
index 48e1cdf3b1..1309db75fd 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/DamageTargetControllerEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/DamageTargetControllerEffect.java
@@ -49,7 +49,7 @@ public class DamageTargetControllerEffect extends OneShotEffect {
 
     @Override
     public boolean apply(Game game, Ability source) {
-        Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
+        Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source));
         if (permanent != null) {
             Player targetController = game.getPlayer(permanent.getControllerId());
             if (targetController != null) {

From 9483bf2ba3ea4a2554c8641420ff1a8c7c854f3d Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Tue, 9 Jun 2020 12:48:28 +0200
Subject: [PATCH 161/586] * Added test for #6344. Problem not reproducable
 (closes #6344).

---
 .../test/cards/triggers/GripOfChaosTest.java  | 95 +++++++++++++++++++
 1 file changed, 95 insertions(+)
 create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/triggers/GripOfChaosTest.java

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/GripOfChaosTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/GripOfChaosTest.java
new file mode 100644
index 0000000000..6fb41fd6da
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/GripOfChaosTest.java
@@ -0,0 +1,95 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.mage.test.cards.triggers;
+
+import mage.constants.PhaseStep;
+import mage.constants.Zone;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestPlayerBase;
+
+/**
+ *
+ * @author LevelX2
+ */
+public class GripOfChaosTest extends CardTestPlayerBase {
+
+    /**
+     * From #6344
+     * I just had a game where we had an interaction between Grip of Chaos,
+     * Felidar Guardian, and Panharmonicon in which the cloned Felidar trigger
+     * fizzled with valid targets on field because Grip retargeted that trigger
+     * onto Felidar itself, which isn't a valid target. Grip of Chaos
+     * specifically states it only chooses from valid targets when retargeting,
+     * so this is a bug somewhere in that interaction, though whether it only
+     * happens with cloned triggers or if there's a bad interaction between Grip
+     * and Felidar itself isn't clear.
+     */
+    @Test
+    public void noValidTargetsTest() {
+        // Whenever a spell or ability is put onto the stack, if it has a single target, reselect its target at random.
+        addCard(Zone.BATTLEFIELD, playerB, "Grip of Chaos", 1); // Enchantment
+
+        // If an artifact or creature entering the battlefield causes a triggered ability 
+        // of a permanent you control to trigger, that ability triggers an additional time.
+        addCard(Zone.BATTLEFIELD, playerA, "Panharmonicon", 1); // Artifact
+
+        // When Felidar Guardian enters the battlefield, you may exile another target permanent you control, 
+        // then return that card to the battlefield under its owner's control.
+        addCard(Zone.HAND, playerA, "Felidar Guardian"); // Creature {3}{W}
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Felidar Guardian");
+
+        setChoice(playerA, "When "); // Select order of Felidar trigger
+
+        setChoice(playerB, "Whenever "); // Select order of Grip of Chaos trigger
+
+        setChoice(playerA, "Yes"); // use for the original trigger of Felidar Guardian
+        setChoice(playerA, "Yes"); // use for the copied trigger of Felidar Guardian
+
+        addTarget(playerA, "Forest");
+        addTarget(playerA, "Mountain");
+
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+
+        setStrictChooseMode(true);
+        execute();
+
+        assertPermanentCount(playerA, "Felidar Guardian", 1);
+        
+        int zcc = 0;
+        zcc += getPermanent("Mountain").getZoneChangeCounter(currentGame);
+        zcc += getPermanent("Forest").getZoneChangeCounter(currentGame);
+        zcc += getPermanent("Swamp").getZoneChangeCounter(currentGame);
+        zcc += getPermanent("Plains").getZoneChangeCounter(currentGame);
+        zcc += getPermanent("Panharmonicon").getZoneChangeCounter(currentGame);
+        if (zcc != 7) {
+          Assert.assertEquals("Sum of zone change counter should be 9", 9, zcc);
+          assertAllCommandsUsed(); // creates error if the random targets do select the same target twice zcc is 7 then the second trigger has an invalid target
+        }
+    }
+
+    /**
+     * Maybe also good situation to create an test for
+     * 9/20/2016 Panharmonicon
+     * 
+     * In some cases involving linked abilities, an ability requires
+     * information about “the exiled card.” When this happens, the ability gets
+     * multiple answers. If these answers are being used to determine the value
+     * of a variable, the sum is used. For example, if Elite Arcanist’s
+     * enters-the-battlefield ability triggers twice, two cards are exiled. The
+     * value of X in the activation cost of Elite Arcanist’s other ability is
+     * the sum of the two cards’ converted mana costs. As the ability resolves,
+     * you create copies of both cards and can cast none, one, or both of the
+     * copies in any order.
+     */
+    
+    
+}

From be063b9c8f153595b8e9cdeef1f6161c9ffe1fdc Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 9 Jun 2020 07:29:27 -0400
Subject: [PATCH 162/586] updated M21 spoiler

---
 Utils/mtg-cards-data.txt | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt
index fd1db3d878..78b4898acc 100644
--- a/Utils/mtg-cards-data.txt
+++ b/Utils/mtg-cards-data.txt
@@ -37441,7 +37441,7 @@ Crystalline Giant|Ikoria: Lair of Behemoths|387|R|{3}|Artifact Creature - Giant|
 Ugin, the Spirit Dragon|Core Set 2021|1|M|{8}|Legendary Planeswalker - Ugin|7|+2: Ugin, the Spirit Dragon deals 3 damage to any target.$−X: Exile each permanent with converted mana cost X or less that's one or more colors.$−10: You gain 7 life, draw seven cards, then put up to seven permanent cards from your hand onto the battlefield.|
 Baneslayer Angel|Core Set 2021|6|M|{3}{W}{W}|Creature - Angel|5|5|Flying, first strike, lifelink, protection from Demons and from Dragons|
 Basri Ket|Core Set 2021|7|M|{1}{W}{W}|Legendary Planeswalker - Basri|3|+1: Put a +1/+1 counter on up to one target creature. It gains indestructible until end of turn.$−2: Whenever one or more nontoken creatures attack this turn, create that many 1/1 white Soldier creature tokens that are tapped and attacking.$−6: You get an emblem with "At the beginning of combat on your turn, create a 1/1 white Soldier creature token, then put a +1/+1 counter on each creature you control."|
-Basri's Acolyte|Core Set 2021|8|C|{2}{W}{W}|Creature - Cat Cleric|2|3|Lifelink$When Basri's Acolyte enters the battlefield, put a +1/+1 counter on each of up to two target creatures you control.|
+Basri's Acolyte|Core Set 2021|8|C|{2}{W}{W}|Creature - Cat Cleric|2|3|Lifelink$When Basri's Acolyte enters the battlefield, put a +1/+1 counter on each of up to two other target creatures you control.|
 Basri's Lieutenant|Core Set 2021|9|R|{3}{W}|Creature - Human Knight|3|4|Vigilance, protection from multicolored$When Basri's Lieutenant enters the battlefield, put a +1/+1 counter on target creature you control.$Whenever Basri's Lieutenant or another creature you control dies, if it had a +1/+1 counter on it, create a 2/2 white Knight creature token with vigilance.|
 Basri's Solidarity|Core Set 2021|10|U|{1}{W}|Sorcery|||Put a +1/+1 counter on each creature you control.|
 Faith's Fetters|Core Set 2021|17|U|{3}{W}|Enchantment - Aura|||Enchant permanent$When Faith's Fetters enters the battlefield, you gain 4 life.$Enchanted permanent can't attack or block, and its activated abilities can't be activated unless they're mana abilities.|
@@ -37449,10 +37449,13 @@ Mangara, the Diplomat|Core Set 2021|27|M|{3}{W}|Legendary Creature - Human Cleri
 Pack Leader|Core Set 2021|29|R|{1}{W}|Creature - Dog|2|2|Other Dogs you control get +1/+1.$Whenever Pack Leader attacks, prevent all combat damage that would be dealt this turn to Dogs you control.|
 Revitalize|Core Set 2021|31|C|{1}{W}|Instant|||You gain 3 life.$Draw a card.|
 Runed Halo|Core Set 2021|32|R|{W}{W}|Enchantment|||As Runed Halo enters the battlefield, choose a card name.$You have protection from the chosen name.|
+Seasoned Hallowblade|Core Set 2021|34|U|{1}{W}|Creature - Human Warrior|3|1|Discard a card: Tap Seasoned Hallowblade. It gains indestructible until end of turn.|
+Selfless Rescue Dog|Core Set 2021|36|U|{W}|Creature - Dog|1|1|Sacrifice Selfless Rescue Dog: Another target creature gains indestructible until end of turn.|
 Jeskai Elder|Core Set 2021|53|U|{1}{U}|Creature - Human Monk|1|2|Prowess$Whenever Jeskai Elder deals combat damage to a player, you may draw a card. If you do, discard a card.|
 Rain of Revelation|Core Set 2021|61|U|{3}{U}|Instant|||Draw three cards, then discard a card.|
 Rewind|Core Set 2021|63|U|{2}{U}{U}|Instant|||Counter target spell. Untap up to four lands.|
 Shipwreck Dowser|Core Set 2021|71|U|{3}{U}{U}|Creature - Merfolk Wizard|3|3|Prowess$When Shipwreck Dowser enters the battlefield, return target instant or sorcery card from your graveyard to your hand.|
+Spined Megalodon|Core Set 2021|72|C|{5}{U}{U}|Creature - Shark|5|7|Hexproof$Whenever Spined Megalodon attacks, scry 1.|
 Teferi, Master of Time|Core Set 2021|75|M|{2}{U}{U}|Legendary Planeswalker - Teferi|3|You may activate loyalty abilities of Teferi, Master of Time on any player's turn any time you could cast an instant.$+1: Draw a card, then discard a card.$−3: Target creature you don't control phases out.$−10: Take two extra turns after this one.|
 Teferi's Ageless Insight|Core Set 2021|76|R|{2}{U}{U}|Legendary Enchantment|||If you would draw a card except the first one you draw in each of your draw steps, draw two cards instead.|
 Teferi's Protege|Core Set 2021|77|C|{2}{U}|Creature - Human Wizard|2|3|{1}{U}, {T}: Draw a card, then discard a card.|
@@ -37463,6 +37466,7 @@ Carrion Grub|Core Set 2021|92|U|{3}{B}|Creature - Insect|0|5|Carrion Grub gets +
 Eliminate|Core Set 2021|97|U|{1}{B}|Instant|||Destroy target creature or planeswalker with converted mana cost 3 or less.|
 Grim Tutor|Core Set 2021|103|M|{1}{B}{B}|Sorcery|||Search your library for a card and put that card into your hand, then shuffle your library. You lose 3 life.|
 Liliana, Waker of the Dead|Core Set 2021|108|M|{2}{B}{B}|Legendary Planeswalker - Liliana|4|+1: Each player discards a card. Each opponent who can't loses 3 life.$−3: Target creature gets -X/-X until end of turn, where X is the number of cards in your graveyard.$−7: You get an emblem with "At the beginning of combat on your turn, put target creature card from a graveyard onto the battlefield under your control. It gains haste."|
+Malefic Scythe|Core Set 2021|112|U|{1}{B}|Artifact - Equipment|||Malefic Scythe enters the battlefield with a soul counter on it.$Equipped creature gets +1/+1 for each soul counter on Malefic Scythe.$Whenever equipped creature dies, put a soul counter on Malefic Scythe.$Equip {1}|
 Massacre Wurm|Core Set 2021|114|M|{3}{B}{B}{B}|Creature - Wurm|6|5|When Massacre Wurm enters the battlefield, creatures your opponents control get -2/-2 until end of turn.$Whenever a creature an opponent controls dies, that player loses 2 life.|
 Necromentia|Core Set 2021|116|R|{1}{B}{B}|Sorcery|||Choose a card name other than a basic land card name. Search target opponent's graveyard, hand, and library for any number of cards with that name and exile them. That player shuffles their library, then creates a 2/2 black Zombie creature token for each card exiled from their hand this way.|
 Peer into the Abyss|Core Set 2021|117|R|{4}{B}{B}{B}|Sorcery|||Target player draws cards equal to half the number of cards in their library and loses half their life. Round up each time.|
@@ -37471,24 +37475,29 @@ Vito, Thorn of the Dusk Rose|Core Set 2021|127|R|{2}{B}|Legendary Creature - Vam
 Bolt Hound|Core Set 2021|131|U|{2}{R}|Creature - Elemental Dog|2|2|Haste$Whenever Bolt Hound attacks, other creatures you control get +1/+0 until end of turn.|
 Double Vision|Core Set 2021|142|R|{3}{R}{R}|Enchantment|||Whenever you cast your first instant or sorcery spell each turn, copy that spell. You may choose new targets for the copy.|
 Gadrak, the Crown-Scourge|Core Set 2021|146|R|{2}{R}|Legendary Creature - Dragon|5|4|Flying$Gadrak, the Crown-Scourge can't attack unless you control four or more artifacts.$At the beginning of your end step, create a Treasure token for each nontoken creature that died this turn.|
+Havoc Jester|Core Set 2021|149|U|{4}{R}|Creature - Devil|5|5|Whenever you sacrifice a permanent, Havoc Jester deals 1 damage to any target.|
+Soul Sear|Core Set 2021|160|U|{2}{R}|Instant|||Soul Sear deals 5 damage to target creature or planeswalker. That permanent loses indestructible until end of turn.|
 Azusa, Lost but Seeking|Core Set 2021|173|R|{2}{G}|Legendary Creature - Human Monk|1|2|You may play two additional lands on each of your turns.|
 Cultivate|Core Set 2021|177|U|{2}{G}|Sorcery|||Search your library for up to two basic land cards, reveal those cards, and put one onto the battlefield tapped and the other into your hand. Then shuffle your library.|
 Elder Gargaroth|Core Set 2021|179|M|{3}{G}{G}|Creature - Beast|6|6|Vigilance, reach, trample$Whenever Elder Gargaroth attacks or blocks, choose one —$• Create a 3/3 green Beast creature token.$• You gain 3 life.$• Draw a card.|
 Fierce Empath|Core Set 2021|181|U|{2}{G}|Creature - Elf|1|1|When Fierce Empath enters the battlefield, you may search your library for a creature card with converted mana cost 6 or greater, reveal it, put it into your hand, then shuffle your library.|
 Fungal Rebirth|Core Set 2021|182|U|{2}{G}|Instant|||Return target permanent card from your graveyard to your hand. If a creature died this turn, create two 1/1 green Saproling creature tokens.|
+Jolrael, Mwonvuli Recluse|Core Set 2021|191|R|{1}{G}|Legendary Creature - Human Druid|1|2|Whenever you draw your second card each turn, create a 2/2 green Cat creature token.${4}{G}{G}: Until end of turn, creatures you control have base power and toughness X/X, where X is the number of cards in your hand.|
 Llanowar Visionary|Core Set 2021|193|C|{2}{G}|Creature - Elf Druid|2|2|When Llanowar Visionary enters the battlefield, draw a card.${T}: Add {G}.|
+Pridemalkin|Core Set 2021|196|C|{2}{G}|Creature - Cat|2|1|When Pridemalkin enters the battlefield, put a +1/+1 counter on target creature you control.$Each creature you control with a +1/+1 counter on it has trample.|
 Primal Might|Core Set 2021|197|R|{X}{G}|Sorcery|||Target creature you control gets +X/+X until end of turn. Then it fights up to one target creature you don't control.|
 Quirion Dryad|Core Set 2021|198|U|{1}{G}|Creature - Dryad|1|1|Whenever you cast a white, blue, black, or red spell, put a +1/+1 counter on Quirion Dryad.|
 Indulging Patrician|Core Set 2021|219|U|{1}{W}{B}|Creature - Vampire Noble|1|4|Flying$Lifelink$At the beginning of your end step, if you gained 3 or more life this turn, each opponent loses 3 life.|
 Lorescale Coatl|Core Set 2021|221|U|{1}{G}{U}|Creature - Snake|2|2|Whenever you draw a card, you may put a +1/+1 counter on Lorescale Coatl.|
 Chromatic Orrery|Core Set 2021|228|M|{7}|Legendary Artifact|||You may spend mana as though it were mana of any color.${T}: Add {C}{C}{C}{C}{C}.${5}, {T}: Draw a card for each color among permanents you control.|
+Mazemind Tome|Core Set 2021|232|R|{2}|Artifact|||{T}, Put a page counter on Mazemind Tome: Scry 1.${2}, {T}, Put a page counter on Mazemind Tome: Draw a card.$Whenever there are four or more page counters on Mazemind Tome, exile it. If you do, you gain 4 life.|
 Solemn Simulacrum|Core Set 2021|239|R|{4}|Artifact Creature - Golem|2|2|When Solemn Simulacrum enters the battlefield, you may search your library for a basic land card, put that card onto the battlefield tapped, then shuffle your library.$When Solemn Simulacrum dies, you may draw a card.|
 Sparkhunter Masticore|Core Set 2021|240|R|{3}|Artifact Creature - Masticore|3|4|As an additional cost to cast this spell, discard a card.$Protection from planeswalkers${1}: Sparkhunter Masticore deals 1 damage to target planeswalker.${3}: Sparkhunter Masticore gains indestructible until end of turn.|
 Tormod's Crypt|Core Set 2021|241|U|{0}|Artifact|||{T}, Sacrifice Tormod's Crypt: Exile all cards from target player's graveyard.|
 Radiant Fountain|Core Set 2021|248|C||Land|||When Radiant Fountain enters the battlefield, you gain 2 life.${T}: Add {C}.|
 Rin and Seri, Inseparable|Core Set 2021|278|M|{1}{R}{G}{W}|Legendary Creature - Dog Cat|4|4|Whenever you cast a Dog spell, create a 1/1 green Cat creature token.$Whenever you cast a Cat spell, create a 1/1 white Dog creature token.${R}{G}{W}, {T}: Rin and Seri, Inseparable deals damage to any target equal to the number of Dogs you control. You gain life equal to the number of Cats you control.|
 Chandra, Heart of Fire|Core Set 2021|301|M|{3}{R}{R}|Legendary Planeswalker - Chandra|5|+1: Discard your hand, then exile the top three cards of your library. Until end of turn, you may play cards exiled this way.$+1: Chandra, Heart of Fire deals 2 damage to any target.$−9: Search your graveyard and library for any number of red instant and/or sorcery spells, exile them, then shuffle your library. You may cast them this turn. Add six {R}.|
-Chandra's Incinerator|Core Set 2021|302|R|{5}{R}|Creature - Elemental|6|6|This spell costs {X} less to cast, where X is the total amount of noncombat damage dealt to your opponents this turn.$Trample$Wheenver a source you control deals noncombat damage to an opponent, Chandra's Incinerator deals that much damage to target creature or planeswalker that player controls.|
+Chandra's Incinerator|Core Set 2021|302|R|{5}{R}|Creature - Elemental|6|6|This spell costs {X} less to cast, where X is the total amount of noncombat damage dealt to your opponents this turn.$Trample$Whenever a source you control deals noncombat damage to an opponent, Chandra's Incinerator deals that much damage to target creature or planeswalker that player controls.|
 Chandra's Magmutt|Core Set 2021|303|C|{1}{R}|Creature - Elemental Dog|2|2|{T}: Chandra's Magmutt deals 1 damage to target player or planeswalker.|
 Chandra's Pyreling|Core Set 2021|304|U|{1}{R}|Creature - Elemental Lizard|1|3|Whenever a source you control deals noncombat damage to an opponent, Chandra's Pyreling gets +1/+0 and gains double strike until end of turn.|
 Plains|Core Set 2021|309|C||Basic Land - Plains|||({T}: Add {W}.)|

From 6c90eb1b3c5df5e0331f21f90465a2dc7d9936e9 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Tue, 9 Jun 2020 14:49:46 +0200
Subject: [PATCH 163/586] * Changed the method for step end processing of
 spells with multiple steps (608.2) from game.applyEffects() to
 game.getState().processAction(game); Added d8fb962 change for two more
 effects.

---
 .../effects/common/ExileAndGainLifeEqualPowerTargetEffect.java  | 2 +-
 .../common/ShuffleHandIntoLibraryDrawThatManySourceEffect.java  | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileAndGainLifeEqualPowerTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileAndGainLifeEqualPowerTargetEffect.java
index efa3ecd82d..64ceb7543e 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/ExileAndGainLifeEqualPowerTargetEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/ExileAndGainLifeEqualPowerTargetEffect.java
@@ -35,7 +35,7 @@ public class ExileAndGainLifeEqualPowerTargetEffect extends OneShotEffect {
             if (player != null) {
                 int creaturePower = permanent.getPower().getValue();
                 permanent.moveToExile(null, null, source.getSourceId(), game);
-                game.applyEffects();
+                game.getState().processAction(game);
                 player.gainLife(creaturePower, game, source);
             }
             return true;
diff --git a/Mage/src/main/java/mage/abilities/effects/common/ShuffleHandIntoLibraryDrawThatManySourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ShuffleHandIntoLibraryDrawThatManySourceEffect.java
index 8f896f18cc..edc6e83545 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/ShuffleHandIntoLibraryDrawThatManySourceEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/ShuffleHandIntoLibraryDrawThatManySourceEffect.java
@@ -36,7 +36,7 @@ public class ShuffleHandIntoLibraryDrawThatManySourceEffect extends OneShotEffec
             if (cardsHand > 0) {
                 controller.moveCards(controller.getHand(), Zone.LIBRARY, source, game);
                 controller.shuffleLibrary(source, game);
-                game.applyEffects(); // then
+                game.getState().processAction(game); // then
                 controller.drawCards(cardsHand, source.getSourceId(), game);
             }
             return true;

From d1b8ab9cccd166d2f4953a863c9c574f435b69e8 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 9 Jun 2020 07:38:22 -0400
Subject: [PATCH 164/586] Implemented Havoc Jester

---
 Mage.Sets/src/mage/cards/h/HavocJester.java | 73 +++++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java    |  1 +
 2 files changed, 74 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/h/HavocJester.java

diff --git a/Mage.Sets/src/mage/cards/h/HavocJester.java b/Mage.Sets/src/mage/cards/h/HavocJester.java
new file mode 100644
index 0000000000..ffa1ab353e
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/h/HavocJester.java
@@ -0,0 +1,73 @@
+package mage.cards.h;
+
+import mage.MageInt;
+import mage.abilities.TriggeredAbilityImpl;
+import mage.abilities.effects.common.DamageTargetEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.Zone;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+import mage.target.common.TargetAnyTarget;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class HavocJester extends CardImpl {
+
+    public HavocJester(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}");
+
+        this.subtype.add(SubType.DEVIL);
+        this.power = new MageInt(5);
+        this.toughness = new MageInt(5);
+
+        // Whenever you sacrifice a permanent, Havoc Jester deals 1 damage to any target.
+        this.addAbility(new HavocJesterTriggeredAbility());
+    }
+
+    private HavocJester(final HavocJester card) {
+        super(card);
+    }
+
+    @Override
+    public HavocJester copy() {
+        return new HavocJester(this);
+    }
+}
+
+class HavocJesterTriggeredAbility extends TriggeredAbilityImpl {
+
+    HavocJesterTriggeredAbility() {
+        super(Zone.BATTLEFIELD, new DamageTargetEffect(1));
+        this.addTarget(new TargetAnyTarget());
+    }
+
+    private HavocJesterTriggeredAbility(final HavocJesterTriggeredAbility ability) {
+        super(ability);
+    }
+
+    @Override
+    public boolean checkEventType(GameEvent event, Game game) {
+        return event.getType() == GameEvent.EventType.SACRIFICED_PERMANENT;
+    }
+
+    @Override
+    public boolean checkTrigger(GameEvent event, Game game) {
+        return event.getPlayerId().equals(getControllerId());
+    }
+
+    @Override
+    public HavocJesterTriggeredAbility copy() {
+        return new HavocJesterTriggeredAbility(this);
+    }
+
+    @Override
+    public String getRule() {
+        return "Whenever you sacrifice a permanent, {this} deals 1 damage to any target.";
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index a7402425ff..c57021280a 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -60,6 +60,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Garruk's Warsteed", 337, Rarity.RARE, mage.cards.g.GarruksWarsteed.class));
         cards.add(new SetCardInfo("Garruk, Savage Herald", 336, Rarity.MYTHIC, mage.cards.g.GarrukSavageHerald.class));
         cards.add(new SetCardInfo("Grim Tutor", 103, Rarity.MYTHIC, mage.cards.g.GrimTutor.class));
+        cards.add(new SetCardInfo("Havoc Jester", 149, Rarity.UNCOMMON, mage.cards.h.HavocJester.class));
         cards.add(new SetCardInfo("Historian of Zhalfir", 325, Rarity.UNCOMMON, mage.cards.h.HistorianOfZhalfir.class));
         cards.add(new SetCardInfo("Indulging Patrician", 219, Rarity.UNCOMMON, mage.cards.i.IndulgingPatrician.class));
         cards.add(new SetCardInfo("Island", 310, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));

From 06a3e3d365080a691bfe9878152346cc282ab316 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 9 Jun 2020 07:48:14 -0400
Subject: [PATCH 165/586] Implemented Malefic Scythe

---
 Mage.Sets/src/mage/cards/m/MaleficScythe.java | 58 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 59 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/m/MaleficScythe.java

diff --git a/Mage.Sets/src/mage/cards/m/MaleficScythe.java b/Mage.Sets/src/mage/cards/m/MaleficScythe.java
new file mode 100644
index 0000000000..d89b4cc62e
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/m/MaleficScythe.java
@@ -0,0 +1,58 @@
+package mage.cards.m;
+
+import mage.abilities.common.DiesAttachedTriggeredAbility;
+import mage.abilities.common.EntersBattlefieldAbility;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.dynamicvalue.DynamicValue;
+import mage.abilities.dynamicvalue.common.CountersSourceCount;
+import mage.abilities.effects.common.continuous.BoostEquippedEffect;
+import mage.abilities.effects.common.counter.AddCountersSourceEffect;
+import mage.abilities.keyword.EquipAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+import mage.counters.CounterType;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class MaleficScythe extends CardImpl {
+
+    private static final DynamicValue xValue = new CountersSourceCount(CounterType.SOUL);
+
+    public MaleficScythe(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}{B}");
+
+        this.subtype.add(SubType.EQUIPMENT);
+
+        // Malefic Scythe enters the battlefield with a soul counter on it.
+        this.addAbility(new EntersBattlefieldAbility(
+                new AddCountersSourceEffect(CounterType.SOUL.createInstance(1)),
+                "with a soul counter on it"
+        ));
+
+        // Equipped creature gets +1/+1 for each soul counter on Malefic Scythe.
+        this.addAbility(new SimpleStaticAbility(new BoostEquippedEffect(xValue, xValue, Duration.WhileOnBattlefield)));
+
+        // Whenever equipped creature dies, put a soul counter on Malefic Scythe.
+        this.addAbility(new DiesAttachedTriggeredAbility(
+                new AddCountersSourceEffect(CounterType.SOUL.createInstance()), "equipped"
+        ));
+
+        // Equip {1}
+        this.addAbility(new EquipAbility(1));
+    }
+
+    private MaleficScythe(final MaleficScythe card) {
+        super(card);
+    }
+
+    @Override
+    public MaleficScythe copy() {
+        return new MaleficScythe(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index c57021280a..5405e04d11 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -72,6 +72,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Liliana, Waker of the Dead", 108, Rarity.MYTHIC, mage.cards.l.LilianaWakerOfTheDead.class));
         cards.add(new SetCardInfo("Llanowar Visionary", 193, Rarity.COMMON, mage.cards.l.LlanowarVisionary.class));
         cards.add(new SetCardInfo("Lorescale Coatl", 221, Rarity.UNCOMMON, mage.cards.l.LorescaleCoatl.class));
+        cards.add(new SetCardInfo("Malefic Scythe", 112, Rarity.UNCOMMON, mage.cards.m.MaleficScythe.class));
         cards.add(new SetCardInfo("Mangara, the Diplomat", 27, Rarity.MYTHIC, mage.cards.m.MangaraTheDiplomat.class));
         cards.add(new SetCardInfo("Massacre Wurm", 114, Rarity.MYTHIC, mage.cards.m.MassacreWurm.class));
         cards.add(new SetCardInfo("Mystic Skyfish", 326, Rarity.COMMON, mage.cards.m.MysticSkyfish.class));

From f95ad03de924bc7f46fb217a4bafb92510de4dfd Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 9 Jun 2020 07:57:10 -0400
Subject: [PATCH 166/586] Implemented Pridemalkin

---
 Mage.Sets/src/mage/cards/p/Pridemalkin.java | 61 +++++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java    |  1 +
 2 files changed, 62 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/p/Pridemalkin.java

diff --git a/Mage.Sets/src/mage/cards/p/Pridemalkin.java b/Mage.Sets/src/mage/cards/p/Pridemalkin.java
new file mode 100644
index 0000000000..fab9b798b1
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/p/Pridemalkin.java
@@ -0,0 +1,61 @@
+package mage.cards.p;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.effects.common.continuous.GainAbilityAllEffect;
+import mage.abilities.effects.common.counter.AddCountersTargetEffect;
+import mage.abilities.keyword.TrampleAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+import mage.counters.CounterType;
+import mage.filter.FilterPermanent;
+import mage.filter.common.FilterControlledCreaturePermanent;
+import mage.filter.predicate.permanent.CounterPredicate;
+import mage.target.common.TargetControlledCreaturePermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class Pridemalkin extends CardImpl {
+
+    private static final FilterPermanent filter
+            = new FilterControlledCreaturePermanent("creature you control with a +1/+1 counter on it");
+
+    static {
+        filter.add(new CounterPredicate(CounterType.P1P1));
+    }
+
+    public Pridemalkin(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}");
+
+        this.subtype.add(SubType.CAT);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(1);
+
+        // When Pridemalkin enters the battlefield, put a +1/+1 counter on target creature you control.
+        Ability ability = new EntersBattlefieldTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance()));
+        ability.addTarget(new TargetControlledCreaturePermanent());
+        this.addAbility(ability);
+
+        // Each creature you control with a +1/+1 counter on it has trample.
+        this.addAbility(new SimpleStaticAbility(new GainAbilityAllEffect(
+                TrampleAbility.getInstance(), Duration.WhileOnBattlefield, filter
+        )));
+    }
+
+    private Pridemalkin(final Pridemalkin card) {
+        super(card);
+    }
+
+    @Override
+    public Pridemalkin copy() {
+        return new Pridemalkin(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 5405e04d11..69903298af 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -82,6 +82,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Pestilent Haze", 118, Rarity.UNCOMMON, mage.cards.p.PestilentHaze.class));
         cards.add(new SetCardInfo("Plains", 309, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Predatory Wurm", 338, Rarity.UNCOMMON, mage.cards.p.PredatoryWurm.class));
+        cards.add(new SetCardInfo("Pridemalkin", 196, Rarity.COMMON, mage.cards.p.Pridemalkin.class));
         cards.add(new SetCardInfo("Quirion Dryad", 198, Rarity.UNCOMMON, mage.cards.q.QuirionDryad.class));
         cards.add(new SetCardInfo("Radiant Fountain", 248, Rarity.COMMON, mage.cards.r.RadiantFountain.class));
         cards.add(new SetCardInfo("Rain of Revelation", 61, Rarity.UNCOMMON, mage.cards.r.RainOfRevelation.class));

From 43b4419e485f8918ed0f7804fcd46ecd3d45b24c Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 9 Jun 2020 08:01:58 -0400
Subject: [PATCH 167/586] Implemented Seasoned Hallowblade

---
 .../src/mage/cards/s/SeasonedHallowblade.java | 47 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 48 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/s/SeasonedHallowblade.java

diff --git a/Mage.Sets/src/mage/cards/s/SeasonedHallowblade.java b/Mage.Sets/src/mage/cards/s/SeasonedHallowblade.java
new file mode 100644
index 0000000000..5d20277ce9
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/SeasonedHallowblade.java
@@ -0,0 +1,47 @@
+package mage.cards.s;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.common.DiscardCardCost;
+import mage.abilities.effects.common.TapSourceEffect;
+import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
+import mage.abilities.keyword.IndestructibleAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class SeasonedHallowblade extends CardImpl {
+
+    public SeasonedHallowblade(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}");
+
+        this.subtype.add(SubType.HUMAN);
+        this.subtype.add(SubType.WARRIOR);
+        this.power = new MageInt(3);
+        this.toughness = new MageInt(1);
+
+        // Discard a card: Tap Seasoned Hallowblade. It gains indestructible until end of turn.
+        Ability ability = new SimpleActivatedAbility(new TapSourceEffect(), new DiscardCardCost());
+        ability.addEffect(new GainAbilitySourceEffect(
+                IndestructibleAbility.getInstance(), Duration.EndOfTurn
+        ).setText("It gains indestructible until end of turn"));
+        this.addAbility(ability);
+    }
+
+    private SeasonedHallowblade(final SeasonedHallowblade card) {
+        super(card);
+    }
+
+    @Override
+    public SeasonedHallowblade copy() {
+        return new SeasonedHallowblade(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 69903298af..3dbbf35a76 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -90,6 +90,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Rewind", 63, Rarity.UNCOMMON, mage.cards.r.Rewind.class));
         cards.add(new SetCardInfo("Rin and Seri, Inseparable", 278, Rarity.MYTHIC, mage.cards.r.RinAndSeriInseparable.class));
         cards.add(new SetCardInfo("Runed Halo", 32, Rarity.RARE, mage.cards.r.RunedHalo.class));
+        cards.add(new SetCardInfo("Seasoned Hallowblade", 34, Rarity.UNCOMMON, mage.cards.s.SeasonedHallowblade.class));
         cards.add(new SetCardInfo("Shipwreck Dowser", 71, Rarity.UNCOMMON, mage.cards.s.ShipwreckDowser.class));
         cards.add(new SetCardInfo("Sigiled Contender", 323, Rarity.UNCOMMON, mage.cards.s.SigiledContender.class));
         cards.add(new SetCardInfo("Solemn Simulacrum", 239, Rarity.RARE, mage.cards.s.SolemnSimulacrum.class));

From 0e82c87f597afb351d6e276d7134ea3dd9d866cd Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 9 Jun 2020 08:05:14 -0400
Subject: [PATCH 168/586] Implemented Spined Megalodon

---
 .../src/mage/cards/s/SpinedMegalodon.java     | 41 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 42 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/s/SpinedMegalodon.java

diff --git a/Mage.Sets/src/mage/cards/s/SpinedMegalodon.java b/Mage.Sets/src/mage/cards/s/SpinedMegalodon.java
new file mode 100644
index 0000000000..b740d87914
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/SpinedMegalodon.java
@@ -0,0 +1,41 @@
+package mage.cards.s;
+
+import mage.MageInt;
+import mage.abilities.common.AttacksTriggeredAbility;
+import mage.abilities.effects.keyword.ScryEffect;
+import mage.abilities.keyword.HexproofAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class SpinedMegalodon extends CardImpl {
+
+    public SpinedMegalodon(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{U}{U}");
+
+        this.subtype.add(SubType.SHARK);
+        this.power = new MageInt(5);
+        this.toughness = new MageInt(7);
+
+        // Hexproof
+        this.addAbility(HexproofAbility.getInstance());
+
+        // Whenever Spined Megalodon attacks, scry 1.
+        this.addAbility(new AttacksTriggeredAbility(new ScryEffect(1), false));
+    }
+
+    private SpinedMegalodon(final SpinedMegalodon card) {
+        super(card);
+    }
+
+    @Override
+    public SpinedMegalodon copy() {
+        return new SpinedMegalodon(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 3dbbf35a76..5996a641e6 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -95,6 +95,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Sigiled Contender", 323, Rarity.UNCOMMON, mage.cards.s.SigiledContender.class));
         cards.add(new SetCardInfo("Solemn Simulacrum", 239, Rarity.RARE, mage.cards.s.SolemnSimulacrum.class));
         cards.add(new SetCardInfo("Sparkhunter Masticore", 240, Rarity.RARE, mage.cards.s.SparkhunterMasticore.class));
+        cards.add(new SetCardInfo("Spined Megalodon", 72, Rarity.COMMON, mage.cards.s.SpinedMegalodon.class));
         cards.add(new SetCardInfo("Spirit of Malevolence", 331, Rarity.COMMON, mage.cards.s.SpiritOfMalevolence.class));
         cards.add(new SetCardInfo("Storm Caller", 335, Rarity.COMMON, mage.cards.s.StormCaller.class));
         cards.add(new SetCardInfo("Teferi's Ageless Insight", 76, Rarity.RARE, mage.cards.t.TeferisAgelessInsight.class));

From e879233ea1beb6103163c0ccdd6bb02b65ad517f Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 9 Jun 2020 08:18:42 -0400
Subject: [PATCH 169/586] Implemented Jolrael, Mwonvuli Recluse

---
 .../mage/cards/j/JolraelMwonvuliRecluse.java  | 54 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 .../game/permanent/token/GreenCatToken2.java  | 28 ++++++++++
 3 files changed, 83 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/j/JolraelMwonvuliRecluse.java
 create mode 100644 Mage/src/main/java/mage/game/permanent/token/GreenCatToken2.java

diff --git a/Mage.Sets/src/mage/cards/j/JolraelMwonvuliRecluse.java b/Mage.Sets/src/mage/cards/j/JolraelMwonvuliRecluse.java
new file mode 100644
index 0000000000..49e20864a8
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/j/JolraelMwonvuliRecluse.java
@@ -0,0 +1,54 @@
+package mage.cards.j;
+
+import mage.MageInt;
+import mage.abilities.common.DrawSecondCardTriggeredAbility;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.dynamicvalue.common.CardsInControllerHandCount;
+import mage.abilities.effects.common.CreateTokenEffect;
+import mage.abilities.effects.common.continuous.SetPowerToughnessAllEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+import mage.constants.SuperType;
+import mage.filter.StaticFilters;
+import mage.game.permanent.token.GreenCatToken2;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class JolraelMwonvuliRecluse extends CardImpl {
+
+    public JolraelMwonvuliRecluse(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}");
+
+        this.addSuperType(SuperType.LEGENDARY);
+        this.subtype.add(SubType.HUMAN);
+        this.subtype.add(SubType.DRUID);
+        this.power = new MageInt(1);
+        this.toughness = new MageInt(2);
+
+        // Whenever you draw your second card each turn, create a 2/2 green Cat creature token.
+        this.addAbility(new DrawSecondCardTriggeredAbility(new CreateTokenEffect(new GreenCatToken2()), false));
+
+        // {4}{G}{G}: Until end of turn, creatures you control have base power and toughness X/X, where X is the number of cards in your hand.
+        this.addAbility(new SimpleActivatedAbility(new SetPowerToughnessAllEffect(
+                CardsInControllerHandCount.instance, CardsInControllerHandCount.instance,
+                Duration.EndOfTurn, StaticFilters.FILTER_CONTROLLED_CREATURES, true
+        ).setText("until end of turn, creatures you control have base power and toughness X/X, " +
+                "where X is the number of cards in your hand"), new ManaCostsImpl("{4}{G}{G}")));
+    }
+
+    private JolraelMwonvuliRecluse(final JolraelMwonvuliRecluse card) {
+        super(card);
+    }
+
+    @Override
+    public JolraelMwonvuliRecluse copy() {
+        return new JolraelMwonvuliRecluse(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 5996a641e6..d4980a4644 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -65,6 +65,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Indulging Patrician", 219, Rarity.UNCOMMON, mage.cards.i.IndulgingPatrician.class));
         cards.add(new SetCardInfo("Island", 310, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Jeskai Elder", 53, Rarity.UNCOMMON, mage.cards.j.JeskaiElder.class));
+        cards.add(new SetCardInfo("Jolrael, Mwonvuli Recluse", 191, Rarity.RARE, mage.cards.j.JolraelMwonvuliRecluse.class));
         cards.add(new SetCardInfo("Keral Keep Disciples", 334, Rarity.UNCOMMON, mage.cards.k.KeralKeepDisciples.class));
         cards.add(new SetCardInfo("Liliana's Scorn", 329, Rarity.RARE, mage.cards.l.LilianasScorn.class));
         cards.add(new SetCardInfo("Liliana's Scrounger", 330, Rarity.UNCOMMON, mage.cards.l.LilianasScrounger.class));
diff --git a/Mage/src/main/java/mage/game/permanent/token/GreenCatToken2.java b/Mage/src/main/java/mage/game/permanent/token/GreenCatToken2.java
new file mode 100644
index 0000000000..403aa94477
--- /dev/null
+++ b/Mage/src/main/java/mage/game/permanent/token/GreenCatToken2.java
@@ -0,0 +1,28 @@
+package mage.game.permanent.token;
+
+import mage.MageInt;
+import mage.constants.CardType;
+import mage.constants.SubType;
+
+/**
+ * @author TheElk801
+ */
+public final class GreenCatToken2 extends TokenImpl {
+
+    public GreenCatToken2() {
+        super("Cat", "2/2 green Cat creature token");
+        cardType.add(CardType.CREATURE);
+        color.setGreen(true);
+        subtype.add(SubType.CAT);
+        power = new MageInt(2);
+        toughness = new MageInt(2);
+    }
+
+    private GreenCatToken2(final GreenCatToken2 token) {
+        super(token);
+    }
+
+    public GreenCatToken2 copy() {
+        return new GreenCatToken2(this);
+    }
+}

From 1d936bcddc2b4a033020d6ad8418b240d59fbbee Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 9 Jun 2020 08:32:08 -0400
Subject: [PATCH 170/586] Implemented Mazemind Tome

---
 Mage.Sets/src/mage/cards/m/MazemindTome.java | 86 ++++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java     |  1 +
 2 files changed, 87 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/m/MazemindTome.java

diff --git a/Mage.Sets/src/mage/cards/m/MazemindTome.java b/Mage.Sets/src/mage/cards/m/MazemindTome.java
new file mode 100644
index 0000000000..282b220a4e
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/m/MazemindTome.java
@@ -0,0 +1,86 @@
+package mage.cards.m;
+
+import mage.abilities.Ability;
+import mage.abilities.StateTriggeredAbility;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.common.ExileSourceCost;
+import mage.abilities.costs.common.PutCountersSourceCost;
+import mage.abilities.costs.common.TapSourceCost;
+import mage.abilities.costs.mana.GenericManaCost;
+import mage.abilities.effects.common.DoIfCostPaid;
+import mage.abilities.effects.common.DrawCardSourceControllerEffect;
+import mage.abilities.effects.common.GainLifeEffect;
+import mage.abilities.effects.keyword.ScryEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Zone;
+import mage.counters.CounterType;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+import mage.game.permanent.Permanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class MazemindTome extends CardImpl {
+
+    public MazemindTome(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}");
+
+        // {T}, Put a page counter on Mazemind Tome: Scry 1.
+        Ability ability = new SimpleActivatedAbility(new ScryEffect(1), new TapSourceCost());
+        ability.addCost(new PutCountersSourceCost(CounterType.PAGE.createInstance()));
+        this.addAbility(ability);
+
+        // {2}, {T}, Put a page counter on Mazemind Tome: Draw a card.
+        ability = new SimpleActivatedAbility(new DrawCardSourceControllerEffect(1), new GenericManaCost(2));
+        ability.addCost(new TapSourceCost());
+        ability.addCost(new PutCountersSourceCost(CounterType.PAGE.createInstance()));
+        this.addAbility(ability);
+
+        // When there are four or more page counters on Mazemind Tome, exile it. If you do, you gain 4 life.
+        this.addAbility(new MazemindTomeTriggeredAbility());
+    }
+
+    private MazemindTome(final MazemindTome card) {
+        super(card);
+    }
+
+    @Override
+    public MazemindTome copy() {
+        return new MazemindTome(this);
+    }
+}
+
+class MazemindTomeTriggeredAbility extends StateTriggeredAbility {
+
+    MazemindTomeTriggeredAbility() {
+        super(Zone.BATTLEFIELD, new DoIfCostPaid(
+                new GainLifeEffect(4), new ExileSourceCost(), null, false
+        ));
+    }
+
+    private MazemindTomeTriggeredAbility(final MazemindTomeTriggeredAbility ability) {
+        super(ability);
+    }
+
+    @Override
+    public MazemindTomeTriggeredAbility copy() {
+        return new MazemindTomeTriggeredAbility(this);
+    }
+
+    @Override
+    public boolean checkTrigger(GameEvent event, Game game) {
+        Permanent permanent = game.getPermanent(getSourceId());
+        return permanent != null && permanent.getCounters(game).getCount(CounterType.PAGE) >= 4;
+    }
+
+    @Override
+    public String getRule() {
+        return "When there are four or more page counters on {this}, exile it. If you do, you gain 4 life.";
+    }
+
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index d4980a4644..caea954e4a 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -76,6 +76,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Malefic Scythe", 112, Rarity.UNCOMMON, mage.cards.m.MaleficScythe.class));
         cards.add(new SetCardInfo("Mangara, the Diplomat", 27, Rarity.MYTHIC, mage.cards.m.MangaraTheDiplomat.class));
         cards.add(new SetCardInfo("Massacre Wurm", 114, Rarity.MYTHIC, mage.cards.m.MassacreWurm.class));
+        cards.add(new SetCardInfo("Mazemind Tome", 232, Rarity.RARE, mage.cards.m.MazemindTome.class));
         cards.add(new SetCardInfo("Mystic Skyfish", 326, Rarity.COMMON, mage.cards.m.MysticSkyfish.class));
         cards.add(new SetCardInfo("Necromentia", 116, Rarity.RARE, mage.cards.n.Necromentia.class));
         cards.add(new SetCardInfo("Pack Leader", 29, Rarity.RARE, mage.cards.p.PackLeader.class));

From 3e3c43f0100b600d0566dbaf5fe633c0cc6f1a55 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 9 Jun 2020 08:34:42 -0400
Subject: [PATCH 171/586] Implemented Soul Sear

---
 Mage.Sets/src/mage/cards/s/SoulSear.java | 38 ++++++++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java |  1 +
 2 files changed, 39 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/s/SoulSear.java

diff --git a/Mage.Sets/src/mage/cards/s/SoulSear.java b/Mage.Sets/src/mage/cards/s/SoulSear.java
new file mode 100644
index 0000000000..af843a5000
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/SoulSear.java
@@ -0,0 +1,38 @@
+package mage.cards.s;
+
+import mage.abilities.effects.common.DamageTargetEffect;
+import mage.abilities.effects.common.continuous.LoseAbilityTargetEffect;
+import mage.abilities.keyword.IndestructibleAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.target.common.TargetCreatureOrPlaneswalker;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class SoulSear extends CardImpl {
+
+    public SoulSear(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R}");
+
+        // Soul Sear deals 5 damage to target creature or planeswalker. That permanent loses indestructible until end of turn.
+        this.getSpellAbility().addEffect(new DamageTargetEffect(5));
+        this.getSpellAbility().addEffect(new LoseAbilityTargetEffect(
+                IndestructibleAbility.getInstance(), Duration.EndOfTurn
+        ).setText("That permanent loses indestructible until end of turn"));
+        this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker());
+    }
+
+    private SoulSear(final SoulSear card) {
+        super(card);
+    }
+
+    @Override
+    public SoulSear copy() {
+        return new SoulSear(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index caea954e4a..610311c3c9 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -96,6 +96,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Shipwreck Dowser", 71, Rarity.UNCOMMON, mage.cards.s.ShipwreckDowser.class));
         cards.add(new SetCardInfo("Sigiled Contender", 323, Rarity.UNCOMMON, mage.cards.s.SigiledContender.class));
         cards.add(new SetCardInfo("Solemn Simulacrum", 239, Rarity.RARE, mage.cards.s.SolemnSimulacrum.class));
+        cards.add(new SetCardInfo("Soul Sear", 160, Rarity.UNCOMMON, mage.cards.s.SoulSear.class));
         cards.add(new SetCardInfo("Sparkhunter Masticore", 240, Rarity.RARE, mage.cards.s.SparkhunterMasticore.class));
         cards.add(new SetCardInfo("Spined Megalodon", 72, Rarity.COMMON, mage.cards.s.SpinedMegalodon.class));
         cards.add(new SetCardInfo("Spirit of Malevolence", 331, Rarity.COMMON, mage.cards.s.SpiritOfMalevolence.class));

From eb846c0499b891c18d096e19682a4a1a5257e5ed Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Tue, 9 Jun 2020 16:37:42 +0200
Subject: [PATCH 172/586] * Added ID name instead of only the name to the
 replacement effect selector list. Otherwise you can't identify the related
 source if you have multiple sources with the same name (related #6298).

---
 Mage.Sets/src/mage/cards/p/PalisadeGiant.java |  2 +-
 .../cards/abilities/keywords/MadnessTest.java | 12 ++--
 .../cards/abilities/keywords/RiotTest.java    |  2 +-
 .../redirect/PalisadeGiantTest.java           | 60 +++++++++++++++++++
 .../abilities/effects/ContinuousEffects.java  |  2 +-
 5 files changed, 69 insertions(+), 9 deletions(-)
 create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/replacement/redirect/PalisadeGiantTest.java

diff --git a/Mage.Sets/src/mage/cards/p/PalisadeGiant.java b/Mage.Sets/src/mage/cards/p/PalisadeGiant.java
index 0ceed13bfb..f1c808fbe9 100644
--- a/Mage.Sets/src/mage/cards/p/PalisadeGiant.java
+++ b/Mage.Sets/src/mage/cards/p/PalisadeGiant.java
@@ -65,7 +65,7 @@ class PalisadeGiantReplacementEffect extends ReplacementEffectImpl {
 
     PalisadeGiantReplacementEffect() {
         super(Duration.WhileOnBattlefield, Outcome.RedirectDamage);
-        staticText = "All damage that would be dealt to you or another permanent you control is dealt to Palisade Giant instead";
+        staticText = "All damage that would be dealt to you or another permanent you control is dealt to {this} instead";
     }
 
     PalisadeGiantReplacementEffect(final PalisadeGiantReplacementEffect effect) {
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MadnessTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MadnessTest.java
index 2687181768..bb4c94e383 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MadnessTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MadnessTest.java
@@ -177,15 +177,15 @@ public class MadnessTest extends CardTestPlayerBase {
         addCard(Zone.HAND, playerA, "Forest");
 
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Falkenrath Gorger");
+        
         setChoice(playerA, "Yes"); // Discard a card and put a +1/+1 counter on that creature, it gains haste until end of turn, and it becomes a Vampire in addition to its other types?
-        setChoice(playerA, "Asylum Visitor");
-        setChoice(playerA, "Asylum Visitor: Madness {1}{B}"); // choose replacement effect (TODO: 2 same madness effetcs: one from Asylum Visitor, one from Falkenrath -- is that ok?!)
-        //
-        setChoice(playerA, "Yes"); // use madness triggered ability
-        setChoice(playerA, "Yes"); // use madness cast
+        setChoice(playerA, "Asylum Visitor"); // Card to discard from Falkenrath entering by Olivia effect
+        setChoice(playerA, "Asylum Visito"); // Madness {1}{B}
+        setChoice(playerA, "Yes"); // use madness triggered ability        
+        setChoice(playerA, "Yes"); // use madness cast        
         setChoice(playerA, "Yes"); // Discard a card and put a +1/+1 counter on that creature, it gains haste until end of turn, and it becomes a Vampire in addition to its other types?
         setChoice(playerA, "Forest");
-
+        
         setStrictChooseMode(true);
         setStopAt(1, PhaseStep.BEGIN_COMBAT);
         execute();
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/RiotTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/RiotTest.java
index 310de3055e..7315ce32c6 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/RiotTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/RiotTest.java
@@ -101,7 +101,7 @@ public class RiotTest extends CardTestPlayerBase {
         addCard(Zone.HAND, playerA, "Silvercoat Lion", 1); // Creature {1}{W}  2/2
 
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Silvercoat Lion");
-        setChoice(playerA, "Rhythm of the Wild: Nontoken creatu"); // choose replacement effect
+        setChoice(playerA, "Rhythm of the Wild"); // choose replacement effect
         setChoice(playerA, "Yes"); // yes - counter
         setChoice(playerA, "Yes"); // yes - counter
 
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/redirect/PalisadeGiantTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/redirect/PalisadeGiantTest.java
new file mode 100644
index 0000000000..63410e6417
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/redirect/PalisadeGiantTest.java
@@ -0,0 +1,60 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.mage.test.cards.replacement.redirect;
+
+import mage.constants.PhaseStep;
+import mage.constants.Zone;
+import mage.game.permanent.Permanent;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestPlayerBase;
+
+/**
+ *
+ * @author LevelX2
+ */
+public class PalisadeGiantTest extends CardTestPlayerBase {
+
+    /**
+     * when you have two Palisade Giants in play and your opponent deals you
+     * damage, the damage is applied to both of them rather than allowing you to
+     * choose which replacement effect applies (as it should). I experienced
+     * this multiple times in games vs the A.I.
+     */
+    @Test
+    public void testRedirectDamage() {
+                
+        // All damage that would be dealt to you or another permanent you control is dealt to Palisade Giant instead.
+        addCard(Zone.BATTLEFIELD, playerA, "Palisade Giant", 2); // Creature 2/7
+
+        addCard(Zone.HAND, playerB, "Lightning Bolt"); // Instant {R}
+        addCard(Zone.BATTLEFIELD, playerB, "Mountain");
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt", playerA);
+
+        setChoice(playerA, "Palisade Giant");
+        
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        assertGraveyardCount(playerB, "Lightning Bolt", 1);
+
+        assertLife(playerA, 20);
+        assertLife(playerB, 20);
+        
+        int damage = 0;
+        for (Permanent permanent : currentGame.getBattlefield().getAllActivePermanents(playerA.getId())) {
+            if (permanent.getName().equals("Palisade Giant")) {
+                damage += permanent.getDamage();
+            }
+        }
+        Assert.assertEquals("Only 3 damage in sum should be dealt to the Palisade Giants", 3, damage);
+
+    }
+
+}
diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java b/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java
index f32f87e537..bf7d2beed1 100644
--- a/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java
+++ b/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java
@@ -1349,7 +1349,7 @@ public class ContinuousEffects implements Serializable {
                 for (Ability ability : entry.getValue()) {
                     MageObject object = game.getObject(ability.getSourceId());
                     if (object != null) {
-                        texts.put(ability.getId().toString() + '_' + entry.getKey().getId().toString(), object.getName() + ": " + ability.getRule(object.getName()));
+                        texts.put(ability.getId().toString() + '_' + entry.getKey().getId().toString(), object.getIdName() + ": " + ability.getRule(object.getName()));
                     } else {
                         texts.put(ability.getId().toString() + '_' + entry.getKey().getId().toString(), entry.getKey().getText(null));
                     }

From 2e8ece1dbd4618f5e6bccaaeb6f334d55c915bba Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Tue, 9 Jun 2020 10:04:59 -0500
Subject: [PATCH 173/586] - Fixed #6581

---
 .../effects/common/combat/CantAttackYouEffect.java  | 13 ++++++++++++-
 .../effects/common/combat/GoadTargetEffect.java     |  4 ++--
 2 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackYouEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackYouEffect.java
index cdd9e6b0b2..462394887b 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackYouEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackYouEffect.java
@@ -13,12 +13,20 @@ import java.util.UUID;
  */
 public class CantAttackYouEffect extends RestrictionEffect {
 
+    UUID controllerId;
+
     public CantAttackYouEffect(Duration duration) {
         super(duration);
     }
 
+    public CantAttackYouEffect(Duration duration, UUID controllerId) {
+        super(duration);
+        this.controllerId = controllerId;
+    }
+
     public CantAttackYouEffect(final CantAttackYouEffect effect) {
         super(effect);
+        this.controllerId = effect.controllerId;
     }
 
     @Override
@@ -36,6 +44,9 @@ public class CantAttackYouEffect extends RestrictionEffect {
         if (defenderId == null) {
             return true;
         }
-        return !defenderId.equals(source.getControllerId());
+        if (controllerId == null) {
+            controllerId = source.getControllerId();
+        }
+        return !defenderId.equals(controllerId);
     }
 }
diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/GoadTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/GoadTargetEffect.java
index e953e8b058..fe5d9a8923 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/combat/GoadTargetEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/combat/GoadTargetEffect.java
@@ -41,7 +41,7 @@ public class GoadTargetEffect extends OneShotEffect {
         Permanent targetCreature = game.getPermanent(getTargetPointer().getFirst(game, source));
         Player controller = game.getPlayer(source.getControllerId());
         if (targetCreature != null && controller != null) {
-            // TODO: impoves goad to allows to target controller, current AttacksIfAbleTargetEffect is not support it
+            // TODO: Allow goad to target controller, current AttacksIfAbleTargetEffect is not support it
             // https://github.com/magefree/mage/issues/5283
             /*
             If the creature doesn’t meet any of the above exceptions and can attack, it must attack a player other than
@@ -52,7 +52,7 @@ public class GoadTargetEffect extends OneShotEffect {
             ContinuousEffect effect = new AttacksIfAbleTargetEffect(Duration.UntilYourNextTurn);
             effect.setTargetPointer(new FixedTarget(getTargetPointer().getFirst(game, source)));
             game.addEffect(effect, source);
-            effect = new CantAttackYouEffect(Duration.UntilYourNextTurn);
+            effect = new CantAttackYouEffect(Duration.UntilYourNextTurn, source.getControllerId()); // remember current controller
             effect.setTargetPointer(new FixedTarget(getTargetPointer().getFirst(game, source)));
             game.addEffect(effect, source);
             game.informPlayers(controller.getLogName() + " is goading " + targetCreature.getLogName());

From 17504d4243beb6c5d45cd4ba76eda79be8d9454f Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Tue, 9 Jun 2020 11:10:06 -0500
Subject: [PATCH 174/586] - Fixed #6426

---
 Mage.Sets/src/mage/cards/o/OppressiveRays.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Mage.Sets/src/mage/cards/o/OppressiveRays.java b/Mage.Sets/src/mage/cards/o/OppressiveRays.java
index ceb1fd488a..77df99c37b 100644
--- a/Mage.Sets/src/mage/cards/o/OppressiveRays.java
+++ b/Mage.Sets/src/mage/cards/o/OppressiveRays.java
@@ -39,7 +39,7 @@ public final class OppressiveRays extends CardImpl {
         // Enchant creature
         TargetPermanent auraTarget = new TargetCreaturePermanent();
         this.getSpellAbility().addTarget(auraTarget);
-        this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature));
+        this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment));
         Ability ability = new EnchantAbility(auraTarget.getTargetName());
         this.addAbility(ability);
 

From 169fa7691fd709c2f2763b0a0158d698f77eedbd Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Tue, 9 Jun 2020 11:34:57 -0500
Subject: [PATCH 175/586] - Fixed text of Mind Warp

---
 Mage.Sets/src/mage/cards/m/MindWarp.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Mage.Sets/src/mage/cards/m/MindWarp.java b/Mage.Sets/src/mage/cards/m/MindWarp.java
index c7fd19c858..e67eddd1c4 100644
--- a/Mage.Sets/src/mage/cards/m/MindWarp.java
+++ b/Mage.Sets/src/mage/cards/m/MindWarp.java
@@ -43,7 +43,7 @@ class MindWarpEffect extends OneShotEffect {
 
     MindWarpEffect() {
         super(Outcome.Discard);
-        staticText = "Look at target player's hand and choose X cards from it. That player discards those card.";
+        staticText = "Look at target player's hand and choose X cards from it. That player discards those cards.";
     }
 
     private MindWarpEffect(final MindWarpEffect effect) {

From 5e7c3ea9f9c3c7438c1dd549247905d4dbf9d1b1 Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Tue, 9 Jun 2020 15:21:29 -0500
Subject: [PATCH 176/586] - Fixed Oath of Lim-Dul.  The discard is now done
 simultaneously.

---
 Mage.Sets/src/mage/cards/o/OathOfLimDul.java | 44 +++++++++++---------
 1 file changed, 24 insertions(+), 20 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/o/OathOfLimDul.java b/Mage.Sets/src/mage/cards/o/OathOfLimDul.java
index f650fe4815..d8e6aee466 100644
--- a/Mage.Sets/src/mage/cards/o/OathOfLimDul.java
+++ b/Mage.Sets/src/mage/cards/o/OathOfLimDul.java
@@ -14,6 +14,7 @@ import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.constants.Zone;
+import mage.filter.FilterCard;
 import mage.filter.common.FilterControlledPermanent;
 import mage.filter.predicate.permanent.AnotherPredicate;
 import mage.game.Game;
@@ -88,7 +89,7 @@ class OathOfLimDulTriggeredAbility extends TriggeredAbilityImpl {
 
 class OathOfLimDulEffect extends OneShotEffect {
 
-    private static final FilterControlledPermanent filter = new FilterControlledPermanent("controlled permanent other than Oath of Lim-Dul");
+    private static final FilterControlledPermanent filter = new FilterControlledPermanent("controlled permanent other than Oath of Lim-Dul to sacrifice");
 
     static {
         filter.add(AnotherPredicate.instance);
@@ -104,31 +105,34 @@ class OathOfLimDulEffect extends OneShotEffect {
 
     @Override
     public boolean apply(Game game, Ability source) {
+        Boolean sacrificeDone = false;
+        int numberSacrificed = 0;
+        int numberToDiscard = 0;
+        int numberOfControlledPermanents = 0;
         Player controller = game.getPlayer(source.getControllerId());
-        int amount = (int) game.getState().getValue(source.getSourceId().toString() + "oathOfLimDul");
-        if (amount > 0
+        int amountDamage = (int) game.getState().getValue(source.getSourceId().toString() + "oathOfLimDul");
+        if (amountDamage > 0
                 && controller != null) {
-            for (int i = 0; i < amount; i++) {
-                TargetControlledPermanent target = new TargetControlledPermanent(filter);
-                target.setNotTarget(true);
-                if (target.canChoose(controller.getId(), game)
-                        && controller.choose(Outcome.Sacrifice, target, source.getSourceId(), game)) {
-                    Cost cost = new DiscardTargetCost(new TargetCardInHand());
-                    if (cost.canPay(source, source.getSourceId(), controller.getId(), game)
-                            && controller.chooseUse(Outcome.Benefit, 
-                                    "Do you wish to discard a card rather than sacrifice the target permanent?", source, game)) {
-                        cost.pay(source, game, source.getSourceId(), controller.getId(), true);
-                    } else {
-                        Permanent targetPermanent = game.getPermanent(target.getFirstTarget());
-                        if (targetPermanent != null) {
-                            targetPermanent.sacrifice(source.getSourceId(), game);
-                        }
+            TargetControlledPermanent target = new TargetControlledPermanent(0, numberOfControlledPermanents, filter, true);
+            target.setNotTarget(true);
+            if (controller.choose(Outcome.Detriment, target, source.getSourceId(), game)) {
+                for (UUID targetPermanentId : target.getTargets()) {
+                    Permanent permanent = game.getPermanent(targetPermanentId);
+                    if (permanent != null
+                            && permanent.sacrifice(source.getSourceId(), game)) {
+                        numberSacrificed += 1;
+                        sacrificeDone = true;
                     }
                 }
             }
-            return true;
+            numberToDiscard = amountDamage - numberSacrificed;
+            Cost cost = new DiscardTargetCost(new TargetCardInHand(numberToDiscard, new FilterCard("card(s) in your hand to discard")));
+            if (numberToDiscard > 0
+                    && cost.canPay(source, source.getSourceId(), controller.getId(), game)) {
+                return cost.pay(source, game, source.getSourceId(), controller.getId(), true);  // discard cost paid simultaneously
+            }
         }
-        return false;
+        return sacrificeDone;
     }
 
     @Override

From 44b84bfbbc004aaf2740996a713e4831c169b389 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Wed, 10 Jun 2020 00:22:48 +0400
Subject: [PATCH 177/586] Code cleanup

---
 Mage.Server/config/config.xml                | 14 +++++++-------
 Mage.Sets/src/mage/cards/m/MeddlingMage.java |  7 +++----
 2 files changed, 10 insertions(+), 11 deletions(-)

diff --git a/Mage.Server/config/config.xml b/Mage.Server/config/config.xml
index d89f21d53b..28ffc64abf 100644
--- a/Mage.Server/config/config.xml
+++ b/Mage.Server/config/config.xml
@@ -56,13 +56,13 @@
             saveGameActivated="false"
             authenticationActivated="false"
             googleAccount=""
-            mailgunApiKey="key-d93e81f19a9c9ed243ebb7cc9381385c"
-            mailgunDomain="sandbox401a433f30d445309a5e86b6c53f7812.mailgun.org"
-            mailSmtpHost="smtp.1und1.de"
-            mailSmtpPort="465"
-            mailUser="xmageserver@online.de"
-            mailPassword="24wrsfxv"
-            mailFromAddress="xmageserver@online.de"
+            mailgunApiKey=""
+            mailgunDomain=""
+            mailSmtpHost=""
+            mailSmtpPort=""
+            mailUser=""
+            mailPassword=""
+            mailFromAddress=""
     />
     <playerTypes>
         <playerType name="Human" jar="mage-player-human.jar" className="mage.player.human.HumanPlayer"/>
diff --git a/Mage.Sets/src/mage/cards/m/MeddlingMage.java b/Mage.Sets/src/mage/cards/m/MeddlingMage.java
index bb855380b1..14fc5d8f0d 100644
--- a/Mage.Sets/src/mage/cards/m/MeddlingMage.java
+++ b/Mage.Sets/src/mage/cards/m/MeddlingMage.java
@@ -30,10 +30,10 @@ public final class MeddlingMage extends CardImpl {
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
 
-        // As Meddling Mage enters the battlefield, name a nonland card.
+        // As Meddling Mage enters the battlefield, choose a nonland card name.
         this.addAbility(new AsEntersBattlefieldAbility(new ChooseACardNameEffect(ChooseACardNameEffect.TypeOfName.NON_LAND_NAME)));
 
-        //The named card can't be cast.
+        // Spells with the chosen name can't be cast.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new MeddlingMageReplacementEffect()));
     }
 
@@ -51,7 +51,7 @@ class MeddlingMageReplacementEffect extends ContinuousRuleModifyingEffectImpl {
 
     public MeddlingMageReplacementEffect() {
         super(Duration.WhileOnBattlefield, Outcome.Detriment);
-        staticText = "Spells with the chosen name can't be cast";
+        staticText = "Spells with the chosen name can't be cast.";
     }
 
     public MeddlingMageReplacementEffect(final MeddlingMageReplacementEffect effect) {
@@ -89,6 +89,5 @@ class MeddlingMageReplacementEffect extends ContinuousRuleModifyingEffectImpl {
         return object != null
                 && !object.isCopy()
                 && CardUtil.haveSameNames(object, cardName, game);
-
     }
 }

From 85e90e8edf3d1c2825f1218fd1201949029e3132 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Wed, 10 Jun 2020 00:24:58 +0400
Subject: [PATCH 178/586] Code cleanup

---
 Mage.Sets/src/mage/cards/a/AccursedWitch.java                 | 2 +-
 Mage.Sets/src/mage/cards/a/AetherStorm.java                   | 2 +-
 Mage.Sets/src/mage/cards/c/CloudKey.java                      | 2 +-
 Mage.Sets/src/mage/cards/g/GaddockTeeg.java                   | 4 ++--
 Mage.Sets/src/mage/cards/i/InfectiousCurse.java               | 2 +-
 Mage.Sets/src/mage/cards/m/MeddlingMage.java                  | 2 +-
 Mage.Sets/src/mage/cards/s/SenatorLottDod.java                | 4 ++--
 Mage.Sets/src/mage/cards/w/WillKenrith.java                   | 2 +-
 .../java/mage/game/command/planes/FeedingGroundsPlane.java    | 2 +-
 9 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/a/AccursedWitch.java b/Mage.Sets/src/mage/cards/a/AccursedWitch.java
index bcde8821e6..aba9e8a30e 100644
--- a/Mage.Sets/src/mage/cards/a/AccursedWitch.java
+++ b/Mage.Sets/src/mage/cards/a/AccursedWitch.java
@@ -98,7 +98,7 @@ class AccursedWitchSpellsCostReductionEffect extends CostModificationEffectImpl
 
     AccursedWitchSpellsCostReductionEffect() {
         super(Duration.WhileOnBattlefield, Outcome.Detriment, CostModificationType.REDUCE_COST);
-        this.staticText = "Spells your opponents cast that target {this} cost {1} less to cast.";
+        this.staticText = "Spells your opponents cast that target {this} cost {1} less to cast";
     }
 
     private AccursedWitchSpellsCostReductionEffect(AccursedWitchSpellsCostReductionEffect effect) {
diff --git a/Mage.Sets/src/mage/cards/a/AetherStorm.java b/Mage.Sets/src/mage/cards/a/AetherStorm.java
index a2298fa7be..3bc436ae30 100644
--- a/Mage.Sets/src/mage/cards/a/AetherStorm.java
+++ b/Mage.Sets/src/mage/cards/a/AetherStorm.java
@@ -50,7 +50,7 @@ class AetherStormReplacementEffect extends ContinuousRuleModifyingEffectImpl {
 
     public AetherStormReplacementEffect() {
         super(Duration.WhileOnBattlefield, Outcome.Detriment);
-        staticText = "Creature spells can't be cast.";
+        staticText = "Creature spells can't be cast";
     }
 
     private AetherStormReplacementEffect(final AetherStormReplacementEffect effect) {
diff --git a/Mage.Sets/src/mage/cards/c/CloudKey.java b/Mage.Sets/src/mage/cards/c/CloudKey.java
index 5a9bd8158e..78d18090cf 100644
--- a/Mage.Sets/src/mage/cards/c/CloudKey.java
+++ b/Mage.Sets/src/mage/cards/c/CloudKey.java
@@ -98,7 +98,7 @@ class CloudKeyCostModificationEffect extends CostModificationEffectImpl {
 
     public CloudKeyCostModificationEffect() {
         super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST);
-        this.staticText = "Spells you cast of the chosen type cost {1} less to cast.";
+        this.staticText = "Spells you cast of the chosen type cost {1} less to cast";
     }
 
     public CloudKeyCostModificationEffect(final CloudKeyCostModificationEffect effect) {
diff --git a/Mage.Sets/src/mage/cards/g/GaddockTeeg.java b/Mage.Sets/src/mage/cards/g/GaddockTeeg.java
index a18c4d4bd6..bc8b26b2fc 100644
--- a/Mage.Sets/src/mage/cards/g/GaddockTeeg.java
+++ b/Mage.Sets/src/mage/cards/g/GaddockTeeg.java
@@ -48,7 +48,7 @@ class GaddockTeegReplacementEffect4 extends ContinuousRuleModifyingEffectImpl {
 
     public GaddockTeegReplacementEffect4() {
         super(Duration.WhileOnBattlefield, Outcome.Detriment);
-        staticText = "Noncreature spells with converted mana cost 4 or greater can't be cast. Noncreature spells with {X} in their mana costs can't be cast.";
+        staticText = "Noncreature spells with converted mana cost 4 or greater can't be cast. Noncreature spells with {X} in their mana costs can't be cast";
     }
 
     public GaddockTeegReplacementEffect4(final GaddockTeegReplacementEffect4 effect) {
@@ -85,7 +85,7 @@ class GaddockTeegReplacementEffectX extends ContinuousRuleModifyingEffectImpl {
 
     public GaddockTeegReplacementEffectX() {
         super(Duration.WhileOnBattlefield, Outcome.Detriment);
-        staticText = "Noncreature spells with {X} in their mana costs can't be cast.";
+        staticText = "Noncreature spells with {X} in their mana costs can't be cast";
     }
 
     public GaddockTeegReplacementEffectX(final GaddockTeegReplacementEffectX effect) {
diff --git a/Mage.Sets/src/mage/cards/i/InfectiousCurse.java b/Mage.Sets/src/mage/cards/i/InfectiousCurse.java
index 555dc866bd..ab6ebe36e5 100644
--- a/Mage.Sets/src/mage/cards/i/InfectiousCurse.java
+++ b/Mage.Sets/src/mage/cards/i/InfectiousCurse.java
@@ -106,7 +106,7 @@ class InfectiousCurseCostReductionEffect extends CostModificationEffectImpl {
 
     public InfectiousCurseCostReductionEffect() {
         super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST);
-        this.staticText = "Spells you cast that target enchanted player cost {1} less to cast.";
+        this.staticText = "Spells you cast that target enchanted player cost {1} less to cast";
     }
 
     protected InfectiousCurseCostReductionEffect(InfectiousCurseCostReductionEffect effect) {
diff --git a/Mage.Sets/src/mage/cards/m/MeddlingMage.java b/Mage.Sets/src/mage/cards/m/MeddlingMage.java
index 14fc5d8f0d..cd912639bc 100644
--- a/Mage.Sets/src/mage/cards/m/MeddlingMage.java
+++ b/Mage.Sets/src/mage/cards/m/MeddlingMage.java
@@ -51,7 +51,7 @@ class MeddlingMageReplacementEffect extends ContinuousRuleModifyingEffectImpl {
 
     public MeddlingMageReplacementEffect() {
         super(Duration.WhileOnBattlefield, Outcome.Detriment);
-        staticText = "Spells with the chosen name can't be cast.";
+        staticText = "Spells with the chosen name can't be cast";
     }
 
     public MeddlingMageReplacementEffect(final MeddlingMageReplacementEffect effect) {
diff --git a/Mage.Sets/src/mage/cards/s/SenatorLottDod.java b/Mage.Sets/src/mage/cards/s/SenatorLottDod.java
index 2c6f13cddc..e771d963c3 100644
--- a/Mage.Sets/src/mage/cards/s/SenatorLottDod.java
+++ b/Mage.Sets/src/mage/cards/s/SenatorLottDod.java
@@ -53,7 +53,7 @@ class SenatorLottDodSpellsTargetingCreatureCostReductionEffect extends CostModif
 
     public SenatorLottDodSpellsTargetingCreatureCostReductionEffect() {
         super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST);
-        this.staticText = "Spell your opponents cast that target a creature you control cost {1} more to cast.";
+        this.staticText = "Spell your opponents cast that target a creature you control cost {1} more to cast";
     }
 
     protected SenatorLottDodSpellsTargetingCreatureCostReductionEffect(SenatorLottDodSpellsTargetingCreatureCostReductionEffect effect) {
@@ -96,7 +96,7 @@ class SenatorLottDodSpellsTargetingYouCostReductionEffect extends CostModificati
 
     public SenatorLottDodSpellsTargetingYouCostReductionEffect() {
         super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST);
-        this.staticText = "Spells your opponents cast that target you cost {2} more to cast.";
+        this.staticText = "Spells your opponents cast that target you cost {2} more to cast";
     }
 
     protected SenatorLottDodSpellsTargetingYouCostReductionEffect(SenatorLottDodSpellsTargetingYouCostReductionEffect effect) {
diff --git a/Mage.Sets/src/mage/cards/w/WillKenrith.java b/Mage.Sets/src/mage/cards/w/WillKenrith.java
index 8af6ce1385..e50d290473 100644
--- a/Mage.Sets/src/mage/cards/w/WillKenrith.java
+++ b/Mage.Sets/src/mage/cards/w/WillKenrith.java
@@ -89,7 +89,7 @@ class WillKenrithCostReductionEffect extends OneShotEffect {
 
     WillKenrithCostReductionEffect() {
         super(Outcome.Benefit);
-        this.staticText = "Until your next turn, instant, sorcery, and planeswalker spells that player casts cost {2} less to cast.";
+        this.staticText = "Until your next turn, instant, sorcery, and planeswalker spells that player casts cost {2} less to cast";
     }
 
     WillKenrithCostReductionEffect(final WillKenrithCostReductionEffect effect) {
diff --git a/Mage/src/main/java/mage/game/command/planes/FeedingGroundsPlane.java b/Mage/src/main/java/mage/game/command/planes/FeedingGroundsPlane.java
index f0546f714f..c81020ed7b 100644
--- a/Mage/src/main/java/mage/game/command/planes/FeedingGroundsPlane.java
+++ b/Mage/src/main/java/mage/game/command/planes/FeedingGroundsPlane.java
@@ -74,7 +74,7 @@ class FeedingGroundsEffect extends CostModificationEffectImpl {
                 new ColorPredicate(ObjectColor.GREEN)));
     }
 
-    private static final String rule = "Red spells cost {1} less to cast. Green spells cost {1} less to cast.";
+    private static final String rule = "Red spells cost {1} less to cast. Green spells cost {1} less to cast";
     private int amount = 1;
 
     public FeedingGroundsEffect() {

From 82032a686d576d6558242af6eb315cbfc205f8a0 Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Tue, 9 Jun 2020 15:25:32 -0500
Subject: [PATCH 179/586] - Fixed text on Alexi, Zephyr Mage.

---
 Mage.Sets/src/mage/cards/a/AlexiZephyrMage.java | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/Mage.Sets/src/mage/cards/a/AlexiZephyrMage.java b/Mage.Sets/src/mage/cards/a/AlexiZephyrMage.java
index 3d9f281655..05df483327 100644
--- a/Mage.Sets/src/mage/cards/a/AlexiZephyrMage.java
+++ b/Mage.Sets/src/mage/cards/a/AlexiZephyrMage.java
@@ -7,6 +7,7 @@ import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.common.DiscardTargetCost;
 import mage.abilities.costs.common.TapSourceCost;
 import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.ReturnToHandTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -35,7 +36,9 @@ public final class AlexiZephyrMage extends CardImpl {
         this.toughness = new MageInt(3);
 
         // {X}{U}, {tap}, Discard two cards: Return X target creatures to their owners' hands.
-        Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ReturnToHandTargetEffect(), new ManaCostsImpl("{X}{U}"));
+        Effect effect = new ReturnToHandTargetEffect();
+        effect.setText("Return X target creatures to their owner's hands");
+        Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("{X}{U}"));
         ability.addCost(new TapSourceCost());
         ability.addCost(new DiscardTargetCost(new TargetCardInHand(2, new FilterCard("two cards"))));
         ability.addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_CREATURES));

From 11d449a242d28aeed623736376205c96ad580998 Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Tue, 9 Jun 2020 16:58:26 -0500
Subject: [PATCH 180/586] - Fixed #6491

---
 Mage.Sets/src/mage/cards/v/VarchildsWarRiders.java | 12 ++++--------
 1 file changed, 4 insertions(+), 8 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/v/VarchildsWarRiders.java b/Mage.Sets/src/mage/cards/v/VarchildsWarRiders.java
index 10471c8723..3962f9c441 100644
--- a/Mage.Sets/src/mage/cards/v/VarchildsWarRiders.java
+++ b/Mage.Sets/src/mage/cards/v/VarchildsWarRiders.java
@@ -1,4 +1,3 @@
-
 package mage.cards.v;
 
 import java.util.UUID;
@@ -16,11 +15,10 @@ import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.SubType;
 import mage.constants.Outcome;
-import mage.filter.FilterOpponent;
 import mage.game.Game;
 import mage.game.permanent.token.SurvivorToken;
 import mage.players.Player;
-import mage.target.TargetPlayer;
+import mage.target.common.TargetOpponent;
 import mage.target.targetpointer.FixedTarget;
 
 /**
@@ -57,10 +55,8 @@ public final class VarchildsWarRiders extends CardImpl {
 
 class OpponentCreateSurvivorTokenCost extends CostImpl {
 
-    private static final FilterOpponent filter = new FilterOpponent();
-
     public OpponentCreateSurvivorTokenCost() {
-        this.text = "have an opponent create a 1/1 red Survivor creature token";
+        this.text = "Have an opponent create a 1/1 red Survivor creature token.";
     }
 
     public OpponentCreateSurvivorTokenCost(OpponentCreateSurvivorTokenCost cost) {
@@ -82,8 +78,8 @@ class OpponentCreateSurvivorTokenCost extends CostImpl {
     public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) {
         Player controller = game.getPlayer(controllerId);
         if (controller != null) {
-            TargetPlayer target = new TargetPlayer(1, 1, true, filter);
-            if (controller.chooseTarget(Outcome.Detriment, target, ability, game)) {
+            TargetOpponent target = new TargetOpponent();
+            if (controller.chooseTarget(Outcome.Neutral, target, ability, game)) {
                 Player opponent = game.getPlayer(target.getFirstTarget());
                 if (opponent != null) {
                     Effect effect = new CreateTokenTargetEffect(new SurvivorToken());

From 781462278cb06a666dbe3a5c1bb4a75aa86bad07 Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Tue, 9 Jun 2020 17:10:20 -0500
Subject: [PATCH 181/586] - Little fix Noxious Vapors

---
 Mage.Sets/src/mage/cards/n/NoxiousVapors.java | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/n/NoxiousVapors.java b/Mage.Sets/src/mage/cards/n/NoxiousVapors.java
index e550c4c05e..1f5fdc483e 100644
--- a/Mage.Sets/src/mage/cards/n/NoxiousVapors.java
+++ b/Mage.Sets/src/mage/cards/n/NoxiousVapors.java
@@ -16,10 +16,11 @@ import mage.filter.predicate.mageobject.ColorPredicate;
 import mage.game.Game;
 import mage.players.Player;
 import mage.target.common.TargetCardInHand;
-
 import java.util.HashSet;
 import java.util.Set;
 import java.util.UUID;
+import java.util.stream.Collectors;
+import mage.MageItem;
 
 /**
  * @author L_J
@@ -87,7 +88,7 @@ class NoxiousVaporsEffect extends OneShotEffect {
             chooseCardForColor(ObjectColor.GREEN, chosenCards, player, game, source);
             chosenCards.addAll(player.getHand().getCards(StaticFilters.FILTER_CARD_LAND, game));
             Cards cards = player.getHand().copy();
-            cards.removeIf(chosenCards::contains);
+            cards.removeIf(chosenCards.stream().map(MageItem::getId).collect(Collectors.toSet())::contains);
             player.discard(cards, source, game);
         }
         return true;

From bc69b8b5a9ea489eb9b3f664c4c744008ca14009 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 9 Jun 2020 18:17:56 -0400
Subject: [PATCH 182/586] updated M21 spoiler and reprints

---
 Mage.Sets/src/mage/sets/CoreSet2021.java | 13 ++++++++--
 Utils/mtg-cards-data.txt                 | 30 ++++++++++++++++++++----
 2 files changed, 36 insertions(+), 7 deletions(-)

diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 610311c3c9..05b81e365b 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -44,8 +44,8 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Bolt Hound", 131, Rarity.UNCOMMON, mage.cards.b.BoltHound.class));
         cards.add(new SetCardInfo("Carrion Grub", 92, Rarity.UNCOMMON, mage.cards.c.CarrionGrub.class));
         cards.add(new SetCardInfo("Chandra's Firemaw", 333, Rarity.RARE, mage.cards.c.ChandrasFiremaw.class));
-        cards.add(new SetCardInfo("Chandra's Incinerator", 302, Rarity.RARE, mage.cards.c.ChandrasIncinerator.class));
-        cards.add(new SetCardInfo("Chandra's Magmutt", 303, Rarity.COMMON, mage.cards.c.ChandrasMagmutt.class));
+        cards.add(new SetCardInfo("Chandra's Incinerator", 136, Rarity.RARE, mage.cards.c.ChandrasIncinerator.class));
+        cards.add(new SetCardInfo("Chandra's Magmutt", 137, Rarity.COMMON, mage.cards.c.ChandrasMagmutt.class));
         cards.add(new SetCardInfo("Chandra, Flame's Catalyst", 332, Rarity.MYTHIC, mage.cards.c.ChandraFlamesCatalyst.class));
         cards.add(new SetCardInfo("Chromatic Orrery", 228, Rarity.MYTHIC, mage.cards.c.ChromaticOrrery.class));
         cards.add(new SetCardInfo("Containment Priest", 314, Rarity.RARE, mage.cards.c.ContainmentPriest.class));
@@ -53,14 +53,17 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Double Vision", 142, Rarity.RARE, mage.cards.d.DoubleVision.class));
         cards.add(new SetCardInfo("Elder Gargaroth", 179, Rarity.MYTHIC, mage.cards.e.ElderGargaroth.class));
         cards.add(new SetCardInfo("Eliminate", 97, Rarity.UNCOMMON, mage.cards.e.Eliminate.class));
+        cards.add(new SetCardInfo("Fabled Passage", 246, Rarity.RARE, mage.cards.f.FabledPassage.class));
         cards.add(new SetCardInfo("Faith's Fetters", 17, Rarity.UNCOMMON, mage.cards.f.FaithsFetters.class));
         cards.add(new SetCardInfo("Fierce Empath", 181, Rarity.UNCOMMON, mage.cards.f.FierceEmpath.class));
         cards.add(new SetCardInfo("Fungal Rebirth", 182, Rarity.UNCOMMON, mage.cards.f.FungalRebirth.class));
         cards.add(new SetCardInfo("Gadrak, the Crown-Scourge", 146, Rarity.RARE, mage.cards.g.GadrakTheCrownScourge.class));
         cards.add(new SetCardInfo("Garruk's Warsteed", 337, Rarity.RARE, mage.cards.g.GarruksWarsteed.class));
         cards.add(new SetCardInfo("Garruk, Savage Herald", 336, Rarity.MYTHIC, mage.cards.g.GarrukSavageHerald.class));
+        cards.add(new SetCardInfo("Grasp of Darkness", 102, Rarity.COMMON, mage.cards.g.GraspOfDarkness.class));
         cards.add(new SetCardInfo("Grim Tutor", 103, Rarity.MYTHIC, mage.cards.g.GrimTutor.class));
         cards.add(new SetCardInfo("Havoc Jester", 149, Rarity.UNCOMMON, mage.cards.h.HavocJester.class));
+        cards.add(new SetCardInfo("Heroic Intervention", 188, Rarity.RARE, mage.cards.h.HeroicIntervention.class));
         cards.add(new SetCardInfo("Historian of Zhalfir", 325, Rarity.UNCOMMON, mage.cards.h.HistorianOfZhalfir.class));
         cards.add(new SetCardInfo("Indulging Patrician", 219, Rarity.UNCOMMON, mage.cards.i.IndulgingPatrician.class));
         cards.add(new SetCardInfo("Island", 310, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
@@ -77,6 +80,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Mangara, the Diplomat", 27, Rarity.MYTHIC, mage.cards.m.MangaraTheDiplomat.class));
         cards.add(new SetCardInfo("Massacre Wurm", 114, Rarity.MYTHIC, mage.cards.m.MassacreWurm.class));
         cards.add(new SetCardInfo("Mazemind Tome", 232, Rarity.RARE, mage.cards.m.MazemindTome.class));
+        cards.add(new SetCardInfo("Mountain", 312, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Mystic Skyfish", 326, Rarity.COMMON, mage.cards.m.MysticSkyfish.class));
         cards.add(new SetCardInfo("Necromentia", 116, Rarity.RARE, mage.cards.n.Necromentia.class));
         cards.add(new SetCardInfo("Pack Leader", 29, Rarity.RARE, mage.cards.p.PackLeader.class));
@@ -106,6 +110,11 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Teferi's Tutelage", 78, Rarity.UNCOMMON, mage.cards.t.TeferisTutelage.class));
         cards.add(new SetCardInfo("Teferi's Wavecaster", 327, Rarity.RARE, mage.cards.t.TeferisWavecaster.class));
         cards.add(new SetCardInfo("Teferi, Master of Time", 75, Rarity.MYTHIC, mage.cards.t.TeferiMasterOfTime.class));
+        cards.add(new SetCardInfo("Temple of Epiphany", 252, Rarity.RARE, mage.cards.t.TempleOfEpiphany.class));
+        cards.add(new SetCardInfo("Temple of Malady", 253, Rarity.RARE, mage.cards.t.TempleOfMalady.class));
+        cards.add(new SetCardInfo("Temple of Mystery", 254, Rarity.RARE, mage.cards.t.TempleOfMystery.class));
+        cards.add(new SetCardInfo("Temple of Silence", 255, Rarity.RARE, mage.cards.t.TempleOfSilence.class));
+        cards.add(new SetCardInfo("Temple of Triumph", 256, Rarity.RARE, mage.cards.t.TempleOfTriumph.class));
         cards.add(new SetCardInfo("Tide Skimmer", 79, Rarity.UNCOMMON, mage.cards.t.TideSkimmer.class));
         cards.add(new SetCardInfo("Tormod's Crypt", 241, Rarity.UNCOMMON, mage.cards.t.TormodsCrypt.class));
         cards.add(new SetCardInfo("Ugin, the Spirit Dragon", 1, Rarity.MYTHIC, mage.cards.u.UginTheSpiritDragon.class));
diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt
index 78b4898acc..cfc856b13f 100644
--- a/Utils/mtg-cards-data.txt
+++ b/Utils/mtg-cards-data.txt
@@ -37439,21 +37439,26 @@ Mysterious Egg|Ikoria: Lair of Behemoths|385|C|{1}|Creature - Egg|0|2|Whenever t
 Dirge Bat|Ikoria: Lair of Behemoths|386|R|{2}{B}{B}|Creature - Bat|3|3|Mutate {4}{B}{B}$Flash$Flying$Whenever this creature mutates, destroy target creature or planeswalker an opponent controls.|
 Crystalline Giant|Ikoria: Lair of Behemoths|387|R|{3}|Artifact Creature - Giant|3|3|At the beginning of combat on your turn, choose a kind of counter at random that Crystalline Giant doesn't have on it from among flying, first strike, deathtouch, hexproof, lifelink, menace, reach, trample, vigilance, or +1/+1. Put a counter of that kind on Crystalline Giant.|
 Ugin, the Spirit Dragon|Core Set 2021|1|M|{8}|Legendary Planeswalker - Ugin|7|+2: Ugin, the Spirit Dragon deals 3 damage to any target.$−X: Exile each permanent with converted mana cost X or less that's one or more colors.$−10: You gain 7 life, draw seven cards, then put up to seven permanent cards from your hand onto the battlefield.|
+Angelic Ascension|Core Set 2021|3|U|{1}{W}|Instant|||Exile target creature or planeswalker. Its controller creates a 4/4 white Angel creature token with flying.|
 Baneslayer Angel|Core Set 2021|6|M|{3}{W}{W}|Creature - Angel|5|5|Flying, first strike, lifelink, protection from Demons and from Dragons|
 Basri Ket|Core Set 2021|7|M|{1}{W}{W}|Legendary Planeswalker - Basri|3|+1: Put a +1/+1 counter on up to one target creature. It gains indestructible until end of turn.$−2: Whenever one or more nontoken creatures attack this turn, create that many 1/1 white Soldier creature tokens that are tapped and attacking.$−6: You get an emblem with "At the beginning of combat on your turn, create a 1/1 white Soldier creature token, then put a +1/+1 counter on each creature you control."|
 Basri's Acolyte|Core Set 2021|8|C|{2}{W}{W}|Creature - Cat Cleric|2|3|Lifelink$When Basri's Acolyte enters the battlefield, put a +1/+1 counter on each of up to two other target creatures you control.|
 Basri's Lieutenant|Core Set 2021|9|R|{3}{W}|Creature - Human Knight|3|4|Vigilance, protection from multicolored$When Basri's Lieutenant enters the battlefield, put a +1/+1 counter on target creature you control.$Whenever Basri's Lieutenant or another creature you control dies, if it had a +1/+1 counter on it, create a 2/2 white Knight creature token with vigilance.|
 Basri's Solidarity|Core Set 2021|10|U|{1}{W}|Sorcery|||Put a +1/+1 counter on each creature you control.|
 Faith's Fetters|Core Set 2021|17|U|{3}{W}|Enchantment - Aura|||Enchant permanent$When Faith's Fetters enters the battlefield, you gain 4 life.$Enchanted permanent can't attack or block, and its activated abilities can't be activated unless they're mana abilities.|
+Idol of Endurance|Core Set 2021|23|R|{2}{W}|Artifact|||When Idol of Endurance enters the battlefield, exile all creature cards with converted mana cost 3 or less from your graveyard until Idol of Endurance leaves the battlefield.${1}{W}, {T}: Until end of turn, you may cast a creature spell from among the cards exiled with Idol of Endurance without payingg its mana cost.|
 Mangara, the Diplomat|Core Set 2021|27|M|{3}{W}|Legendary Creature - Human Cleric|2|4|Lifelink$Whenever an opponent attacks with creatures, if two or more of those creatures are attacking you and/or a planeswalker you control, draw a card.$Whenever an opponent casts their second spell each turn, draw a card.|
 Pack Leader|Core Set 2021|29|R|{1}{W}|Creature - Dog|2|2|Other Dogs you control get +1/+1.$Whenever Pack Leader attacks, prevent all combat damage that would be dealt this turn to Dogs you control.|
 Revitalize|Core Set 2021|31|C|{1}{W}|Instant|||You gain 3 life.$Draw a card.|
 Runed Halo|Core Set 2021|32|R|{W}{W}|Enchantment|||As Runed Halo enters the battlefield, choose a card name.$You have protection from the chosen name.|
 Seasoned Hallowblade|Core Set 2021|34|U|{1}{W}|Creature - Human Warrior|3|1|Discard a card: Tap Seasoned Hallowblade. It gains indestructible until end of turn.|
-Selfless Rescue Dog|Core Set 2021|36|U|{W}|Creature - Dog|1|1|Sacrifice Selfless Rescue Dog: Another target creature gains indestructible until end of turn.|
+Selfless Savior|Core Set 2021|36|U|{W}|Creature - Dog|1|1|Sacrifice Selfless Savior: Another target creature gains indestructible until end of turn.|
+Siege Striker|Core Set 2021|37|U|{2}{W}|Creature - Human Soldier|1|1|Double strike$Whenever Siege Striker attacks, you may tap any number of untapped creatures you control. Siege Striker gets +1/+1 until end of turn for each creature tapped this way.|
 Jeskai Elder|Core Set 2021|53|U|{1}{U}|Creature - Human Monk|1|2|Prowess$Whenever Jeskai Elder deals combat damage to a player, you may draw a card. If you do, discard a card.|
+Pursued Whale|Core Set 2021|60|R|{5}{U}{U}|Creature - Whale|8|8|When Pursued Whale enters the battlefield, each opponent creates a 1/1 red Pirate creature token with "This creature can't block" and "Creatures you control attack each combat if able."$Spells your opponents cast that target Pursued Whale cost {3} more to cast.|
 Rain of Revelation|Core Set 2021|61|U|{3}{U}|Instant|||Draw three cards, then discard a card.|
 Rewind|Core Set 2021|63|U|{2}{U}{U}|Instant|||Counter target spell. Untap up to four lands.|
+See the Truth|Core Set 2021|69|R|{1}{U}|Sorcery|||Look at the top three cards of your library. Put one of those cards into your hand and the rest on the bottom of your library in any order. If this spell was cast from anywhere other than your hand, put each of those cards into your hand instead.|
 Shipwreck Dowser|Core Set 2021|71|U|{3}{U}{U}|Creature - Merfolk Wizard|3|3|Prowess$When Shipwreck Dowser enters the battlefield, return target instant or sorcery card from your graveyard to your hand.|
 Spined Megalodon|Core Set 2021|72|C|{5}{U}{U}|Creature - Shark|5|7|Hexproof$Whenever Spined Megalodon attacks, scry 1.|
 Teferi, Master of Time|Core Set 2021|75|M|{2}{U}{U}|Legendary Planeswalker - Teferi|3|You may activate loyalty abilities of Teferi, Master of Time on any player's turn any time you could cast an instant.$+1: Draw a card, then discard a card.$−3: Target creature you don't control phases out.$−10: Take two extra turns after this one.|
@@ -37461,9 +37466,12 @@ Teferi's Ageless Insight|Core Set 2021|76|R|{2}{U}{U}|Legendary Enchantment|||If
 Teferi's Protege|Core Set 2021|77|C|{2}{U}|Creature - Human Wizard|2|3|{1}{U}, {T}: Draw a card, then discard a card.|
 Teferi's Tutelage|Core Set 2021|78|U|{2}{U}|Enchantment|||When Teferi's Tutelage enters the battlefield, draw a card, then discard a card.$Whenever you draw a card, target opponent mills two cards.|
 Tide Skimmer|Core Set 2021|79|U|{3}{U}|Creature - Drake|2|3|Flying$Whenever you attack with two or more creatures with flying, draw a card.|
+Waker of Waves|Core Set 2021|84|U|{5}{U}{U}|Creature - Whale|7|7|Creatures your opponents control get -1/-0.${1}{U}, Discard Waker of Waves: Look at the top two cards of your library. Put one of them into your hand and the other into your graveyard.|
+Archfiend's Vessel|Core Set 2021|88|U|{B}|Creature - Human Cleric|1|1|Lifelink$When Archfiend's Vessel enters the battlefield, if it entered from your graveyard or you cast it from your graveyard, exile it. If you do, create a 5/5 black Demon creature token with flying.|
 Bad Deal|Core Set 2021|89|U|{4}{B}{B}|Sorcery|||You draw two cards and each opponent discards two cards. Each player loses 2 life.|
 Carrion Grub|Core Set 2021|92|U|{3}{B}|Creature - Insect|0|5|Carrion Grub gets +X/+0, where X is the greatest power among creature cards in your graveyard.$When Carrion Grub enters the battlefield, mill four cards.|
 Eliminate|Core Set 2021|97|U|{1}{B}|Instant|||Destroy target creature or planeswalker with converted mana cost 3 or less.|
+Grasp of Darkness|Core Set 2021|102|C|{B}{B}|Instant|||Target creature gets -4/-4 until end of turn.|
 Grim Tutor|Core Set 2021|103|M|{1}{B}{B}|Sorcery|||Search your library for a card and put that card into your hand, then shuffle your library. You lose 3 life.|
 Liliana, Waker of the Dead|Core Set 2021|108|M|{2}{B}{B}|Legendary Planeswalker - Liliana|4|+1: Each player discards a card. Each opponent who can't loses 3 life.$−3: Target creature gets -X/-X until end of turn, where X is the number of cards in your graveyard.$−7: You get an emblem with "At the beginning of combat on your turn, put target creature card from a graveyard onto the battlefield under your control. It gains haste."|
 Malefic Scythe|Core Set 2021|112|U|{1}{B}|Artifact - Equipment|||Malefic Scythe enters the battlefield with a soul counter on it.$Equipped creature gets +1/+1 for each soul counter on Malefic Scythe.$Whenever equipped creature dies, put a soul counter on Malefic Scythe.$Equip {1}|
@@ -37471,17 +37479,25 @@ Massacre Wurm|Core Set 2021|114|M|{3}{B}{B}{B}|Creature - Wurm|6|5|When Massacre
 Necromentia|Core Set 2021|116|R|{1}{B}{B}|Sorcery|||Choose a card name other than a basic land card name. Search target opponent's graveyard, hand, and library for any number of cards with that name and exile them. That player shuffles their library, then creates a 2/2 black Zombie creature token for each card exiled from their hand this way.|
 Peer into the Abyss|Core Set 2021|117|R|{4}{B}{B}{B}|Sorcery|||Target player draws cards equal to half the number of cards in their library and loses half their life. Round up each time.|
 Pestilent Haze|Core Set 2021|118|U|{1}{B}{B}|Sorcery|||Choose one —$• All creatures get -2/-2 until end of turn.$• Remove two loyalty counters from each planeswalker.|
+Village Rites|Core Set 2021|126|C|{B}|Instant|||As an additional cost to cast this spell, sacrifice a creature.$Draw two cards.|
 Vito, Thorn of the Dusk Rose|Core Set 2021|127|R|{2}{B}|Legendary Creature - Vampire Cleric|1|3|Whenever you gain life, target opponent loses that much life.${3}{B}{B}: Creatures you control gain lifelink until end of turn.|
 Bolt Hound|Core Set 2021|131|U|{2}{R}|Creature - Elemental Dog|2|2|Haste$Whenever Bolt Hound attacks, other creatures you control get +1/+0 until end of turn.|
+Chandra, Heart of Fire|Core Set 2021|135|M|{3}{R}{R}|Legendary Planeswalker - Chandra|5|+1: Discard your hand, then exile the top three cards of your library. Until end of turn, you may play cards exiled this way.$+1: Chandra, Heart of Fire deals 2 damage to any target.$−9: Search your graveyard and library for any number of red instant and/or sorcery spells, exile them, then shuffle your library. You may cast them this turn. Add six {R}.|
+Chandra's Incinerator|Core Set 2021|136|R|{5}{R}|Creature - Elemental|6|6|This spell costs {X} less to cast, where X is the total amount of noncombat damage dealt to your opponents this turn.$Trample$Whenever a source you control deals noncombat damage to an opponent, Chandra's Incinerator deals that much damage to target creature or planeswalker that player controls.|
+Chandra's Magmutt|Core Set 2021|137|C|{1}{R}|Creature - Elemental Dog|2|2|{T}: Chandra's Magmutt deals 1 damage to target player or planeswalker.|
+Chandra's Pyreling|Core Set 2021|138|U|{1}{R}|Creature - Elemental Lizard|1|3|Whenever a source you control deals noncombat damage to an opponent, Chandra's Pyreling gets +1/+0 and gains double strike until end of turn.|
 Double Vision|Core Set 2021|142|R|{3}{R}{R}|Enchantment|||Whenever you cast your first instant or sorcery spell each turn, copy that spell. You may choose new targets for the copy.|
 Gadrak, the Crown-Scourge|Core Set 2021|146|R|{2}{R}|Legendary Creature - Dragon|5|4|Flying$Gadrak, the Crown-Scourge can't attack unless you control four or more artifacts.$At the beginning of your end step, create a Treasure token for each nontoken creature that died this turn.|
 Havoc Jester|Core Set 2021|149|U|{4}{R}|Creature - Devil|5|5|Whenever you sacrifice a permanent, Havoc Jester deals 1 damage to any target.|
+Kinetic Augur|Core Set 2021|154|U|{3}{R}|Creature - Human Shaman|*|4|Trample$Kinetic Augur's power is equal to the number of instant and sorcery cards in your graveyard.$When Kinetic Augur enters the battlefield, discard up to two cards, then draw that many cards.|
 Soul Sear|Core Set 2021|160|U|{2}{R}|Instant|||Soul Sear deals 5 damage to target creature or planeswalker. That permanent loses indestructible until end of turn.|
+Volcanic Salvo|Core Set 2021|172|R|{10}{R}{R}|Sorcery|||This spell costs {X} less to cast, where X is the total power of creatures you control.$Volcanic Salvo deals 6 damage to each of up to two target creatures and/or planeswalkers.|
 Azusa, Lost but Seeking|Core Set 2021|173|R|{2}{G}|Legendary Creature - Human Monk|1|2|You may play two additional lands on each of your turns.|
 Cultivate|Core Set 2021|177|U|{2}{G}|Sorcery|||Search your library for up to two basic land cards, reveal those cards, and put one onto the battlefield tapped and the other into your hand. Then shuffle your library.|
 Elder Gargaroth|Core Set 2021|179|M|{3}{G}{G}|Creature - Beast|6|6|Vigilance, reach, trample$Whenever Elder Gargaroth attacks or blocks, choose one —$• Create a 3/3 green Beast creature token.$• You gain 3 life.$• Draw a card.|
 Fierce Empath|Core Set 2021|181|U|{2}{G}|Creature - Elf|1|1|When Fierce Empath enters the battlefield, you may search your library for a creature card with converted mana cost 6 or greater, reveal it, put it into your hand, then shuffle your library.|
 Fungal Rebirth|Core Set 2021|182|U|{2}{G}|Instant|||Return target permanent card from your graveyard to your hand. If a creature died this turn, create two 1/1 green Saproling creature tokens.|
+Heroic Intervention|Core Set 2021|188|R|{1}{G}|Instant|||Permanents you control gain hexproof and indestructible until end of turn.|
 Jolrael, Mwonvuli Recluse|Core Set 2021|191|R|{1}{G}|Legendary Creature - Human Druid|1|2|Whenever you draw your second card each turn, create a 2/2 green Cat creature token.${4}{G}{G}: Until end of turn, creatures you control have base power and toughness X/X, where X is the number of cards in your hand.|
 Llanowar Visionary|Core Set 2021|193|C|{2}{G}|Creature - Elf Druid|2|2|When Llanowar Visionary enters the battlefield, draw a card.${T}: Add {G}.|
 Pridemalkin|Core Set 2021|196|C|{2}{G}|Creature - Cat|2|1|When Pridemalkin enters the battlefield, put a +1/+1 counter on target creature you control.$Each creature you control with a +1/+1 counter on it has trample.|
@@ -37489,19 +37505,23 @@ Primal Might|Core Set 2021|197|R|{X}{G}|Sorcery|||Target creature you control ge
 Quirion Dryad|Core Set 2021|198|U|{1}{G}|Creature - Dryad|1|1|Whenever you cast a white, blue, black, or red spell, put a +1/+1 counter on Quirion Dryad.|
 Indulging Patrician|Core Set 2021|219|U|{1}{W}{B}|Creature - Vampire Noble|1|4|Flying$Lifelink$At the beginning of your end step, if you gained 3 or more life this turn, each opponent loses 3 life.|
 Lorescale Coatl|Core Set 2021|221|U|{1}{G}{U}|Creature - Snake|2|2|Whenever you draw a card, you may put a +1/+1 counter on Lorescale Coatl.|
+Obsessive Stitcher|Core Set 2021|223|U|{1}{U}{B}|Creature - Human Wizard|0|3|{T}: Draw a card, then discard a card.${2}{U}{B}, {T}, Sacrifice Obsessive Stitcher: Return target creature card from your graveyard to the battlefield.|
 Chromatic Orrery|Core Set 2021|228|M|{7}|Legendary Artifact|||You may spend mana as though it were mana of any color.${T}: Add {C}{C}{C}{C}{C}.${5}, {T}: Draw a card for each color among permanents you control.|
 Mazemind Tome|Core Set 2021|232|R|{2}|Artifact|||{T}, Put a page counter on Mazemind Tome: Scry 1.${2}, {T}, Put a page counter on Mazemind Tome: Draw a card.$Whenever there are four or more page counters on Mazemind Tome, exile it. If you do, you gain 4 life.|
 Solemn Simulacrum|Core Set 2021|239|R|{4}|Artifact Creature - Golem|2|2|When Solemn Simulacrum enters the battlefield, you may search your library for a basic land card, put that card onto the battlefield tapped, then shuffle your library.$When Solemn Simulacrum dies, you may draw a card.|
 Sparkhunter Masticore|Core Set 2021|240|R|{3}|Artifact Creature - Masticore|3|4|As an additional cost to cast this spell, discard a card.$Protection from planeswalkers${1}: Sparkhunter Masticore deals 1 damage to target planeswalker.${3}: Sparkhunter Masticore gains indestructible until end of turn.|
 Tormod's Crypt|Core Set 2021|241|U|{0}|Artifact|||{T}, Sacrifice Tormod's Crypt: Exile all cards from target player's graveyard.|
+Fabled Passage|Core Set 2021|246|R||Land|||{T}, Sacrifice Fabled Passage: Search your library for a basic land card, put it onto the battlefield tapped, then shuffle your library. Then if you control four or more lands, untap that land.|
 Radiant Fountain|Core Set 2021|248|C||Land|||When Radiant Fountain enters the battlefield, you gain 2 life.${T}: Add {C}.|
+Temple of Epiphany|Core Set 2021|252|R||Land|||Temple of Epiphany enters the battlefield tapped.$When Temple of Epiphany enters the battlefield, scry 1.${T}: Add {U} or {R}.|
+Temple of Malady|Core Set 2021|253|R||Land|||Temple of Malady enters the battlefield tapped.$When Temple of Malady enters the battlefield, scry 1.${T}: Add {B} or {G}.|
+Temple of Mystery|Core Set 2021|254|R||Land|||Temple of Mystery enters the battlefield tapped.$When Temple of Mystery enters the battlefield, scry 1.${T}: Add {G} or {U}.|
+Temple of Silence|Core Set 2021|255|R||Land|||Temple of Silence enters the battlefield tapped.$When Temple of Silence enters the battlefield, scry 1.${T}: Add {W} or {B}.|
+Temple of Triumph|Core Set 2021|256|R||Land|||Temple of Triumph enters the battlefield tapped.$When Temple of Triumph enters the battlefield, scry 1.${T}: Add {R} or {W}.|
 Rin and Seri, Inseparable|Core Set 2021|278|M|{1}{R}{G}{W}|Legendary Creature - Dog Cat|4|4|Whenever you cast a Dog spell, create a 1/1 green Cat creature token.$Whenever you cast a Cat spell, create a 1/1 white Dog creature token.${R}{G}{W}, {T}: Rin and Seri, Inseparable deals damage to any target equal to the number of Dogs you control. You gain life equal to the number of Cats you control.|
-Chandra, Heart of Fire|Core Set 2021|301|M|{3}{R}{R}|Legendary Planeswalker - Chandra|5|+1: Discard your hand, then exile the top three cards of your library. Until end of turn, you may play cards exiled this way.$+1: Chandra, Heart of Fire deals 2 damage to any target.$−9: Search your graveyard and library for any number of red instant and/or sorcery spells, exile them, then shuffle your library. You may cast them this turn. Add six {R}.|
-Chandra's Incinerator|Core Set 2021|302|R|{5}{R}|Creature - Elemental|6|6|This spell costs {X} less to cast, where X is the total amount of noncombat damage dealt to your opponents this turn.$Trample$Whenever a source you control deals noncombat damage to an opponent, Chandra's Incinerator deals that much damage to target creature or planeswalker that player controls.|
-Chandra's Magmutt|Core Set 2021|303|C|{1}{R}|Creature - Elemental Dog|2|2|{T}: Chandra's Magmutt deals 1 damage to target player or planeswalker.|
-Chandra's Pyreling|Core Set 2021|304|U|{1}{R}|Creature - Elemental Lizard|1|3|Whenever a source you control deals noncombat damage to an opponent, Chandra's Pyreling gets +1/+0 and gains double strike until end of turn.|
 Plains|Core Set 2021|309|C||Basic Land - Plains|||({T}: Add {W}.)|
 Island|Core Set 2021|310|C||Basic Land - Island|||({T}: Add {U}.)|
+Mountain|Core Set 2021|312|C||Basic Land - Mountain|||({T}: Add {R}.)|
 Containment Priest|Core Set 2021|314|R|{1}{W}|Creature - Human Cleric|2|2|Flash$If a nontoken creature would enter the battlefield and it wasn't cast, exile it instead.|
 Basri, Devoted Paladin|Core Set 2021|320|M|{4}{W}{W}|Legendary Planeswalker - Basri|4|+1: Put a +1/+1 counter on up to one target creature. It gains vigilance until end of turn.$−1: Whenever a creature attacks this turn, put a +1/+1 counter on it.$−6: Creatures you control get +2/+2 and gain flying until end of turn.|
 Adherent of Hope|Core Set 2021|321|C|{1}{W}|Creature - Human Soldier|2|1|At the beginning of combat on your turn, if you control a Basri planeswalker, put a +1/+1 counter on Adherent of Hope.|

From 0d9d4b6705c1375e5b3986dfde82a5f6b7d13361 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 9 Jun 2020 18:18:03 -0400
Subject: [PATCH 183/586] Implemented See the Truth

---
 Mage.Sets/src/mage/cards/s/SeeTheTruth.java | 83 +++++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java    |  1 +
 2 files changed, 84 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/s/SeeTheTruth.java

diff --git a/Mage.Sets/src/mage/cards/s/SeeTheTruth.java b/Mage.Sets/src/mage/cards/s/SeeTheTruth.java
new file mode 100644
index 0000000000..66540a9ae3
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/SeeTheTruth.java
@@ -0,0 +1,83 @@
+package mage.cards.s;
+
+import mage.abilities.Ability;
+import mage.abilities.condition.common.CastFromHandSourceCondition;
+import mage.abilities.effects.OneShotEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.cards.Cards;
+import mage.cards.CardsImpl;
+import mage.constants.CardType;
+import mage.constants.Outcome;
+import mage.constants.Zone;
+import mage.game.Game;
+import mage.players.Player;
+import mage.target.common.TargetCardInLibrary;
+import mage.watchers.common.CastFromHandWatcher;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class SeeTheTruth extends CardImpl {
+
+    public SeeTheTruth(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{U}");
+
+        // Look at the top three cards of your library. Put one of those cards into your hand and the rest on the bottom of your library in any order. If this spell was cast from anywhere other than your hand, put each of those cards into your hand instead.
+        this.getSpellAbility().addEffect(new SeeTheTruthEffect());
+        this.getSpellAbility().addWatcher(new CastFromHandWatcher());
+    }
+
+    private SeeTheTruth(final SeeTheTruth card) {
+        super(card);
+    }
+
+    @Override
+    public SeeTheTruth copy() {
+        return new SeeTheTruth(this);
+    }
+}
+
+class SeeTheTruthEffect extends OneShotEffect {
+
+    SeeTheTruthEffect() {
+        super(Outcome.Benefit);
+        staticText = "Look at the top three cards of your library. " +
+                "Put one of those cards into your hand and the rest on the bottom of your library in any order. " +
+                "If this spell was cast from anywhere other than your hand, " +
+                "put each of those cards into your hand instead.";
+    }
+
+    private SeeTheTruthEffect(final SeeTheTruthEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public SeeTheTruthEffect copy() {
+        return new SeeTheTruthEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Player player = game.getPlayer(source.getControllerId());
+        if (player == null) {
+            return false;
+        }
+        Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, 3));
+        if (cards.size() < 1) {
+            return false;
+        }
+        if (CastFromHandSourceCondition.instance.apply(game, source)) {
+            TargetCardInLibrary target = new TargetCardInLibrary();
+            player.choose(outcome, cards, target, game);
+            cards.removeIf(target.getFirstTarget()::equals);
+            player.moveCards(game.getCard(target.getFirstTarget()), Zone.HAND, source, game);
+            player.putCardsOnBottomOfLibrary(cards, game, source, true);
+        } else {
+            player.moveCards(cards, Zone.HAND, source, game);
+        }
+        return true;
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 05b81e365b..5a7b07f97d 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -97,6 +97,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Rin and Seri, Inseparable", 278, Rarity.MYTHIC, mage.cards.r.RinAndSeriInseparable.class));
         cards.add(new SetCardInfo("Runed Halo", 32, Rarity.RARE, mage.cards.r.RunedHalo.class));
         cards.add(new SetCardInfo("Seasoned Hallowblade", 34, Rarity.UNCOMMON, mage.cards.s.SeasonedHallowblade.class));
+        cards.add(new SetCardInfo("See the Truth", 69, Rarity.RARE, mage.cards.s.SeeTheTruth.class));
         cards.add(new SetCardInfo("Shipwreck Dowser", 71, Rarity.UNCOMMON, mage.cards.s.ShipwreckDowser.class));
         cards.add(new SetCardInfo("Sigiled Contender", 323, Rarity.UNCOMMON, mage.cards.s.SigiledContender.class));
         cards.add(new SetCardInfo("Solemn Simulacrum", 239, Rarity.RARE, mage.cards.s.SolemnSimulacrum.class));

From e185746d0c301bd4cff65f93d040eca65d50b803 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 9 Jun 2020 18:22:30 -0400
Subject: [PATCH 184/586] Implemented Village Rites

---
 Mage.Sets/src/mage/cards/v/VillageRites.java | 38 ++++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java     |  1 +
 2 files changed, 39 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/v/VillageRites.java

diff --git a/Mage.Sets/src/mage/cards/v/VillageRites.java b/Mage.Sets/src/mage/cards/v/VillageRites.java
new file mode 100644
index 0000000000..3217fc3d6e
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/v/VillageRites.java
@@ -0,0 +1,38 @@
+package mage.cards.v;
+
+import mage.abilities.costs.common.SacrificeTargetCost;
+import mage.abilities.effects.common.DrawCardSourceControllerEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.filter.StaticFilters;
+import mage.target.common.TargetControlledPermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class VillageRites extends CardImpl {
+
+    public VillageRites(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}");
+
+        // As an additional cost to cast this spell, sacrifice a creature.
+        this.getSpellAbility().addCost(new SacrificeTargetCost(
+                new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT)
+        ));
+
+        // Draw two cards.
+        this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2));
+    }
+
+    private VillageRites(final VillageRites card) {
+        super(card);
+    }
+
+    @Override
+    public VillageRites copy() {
+        return new VillageRites(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 5a7b07f97d..e233bfe37e 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -119,6 +119,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Tide Skimmer", 79, Rarity.UNCOMMON, mage.cards.t.TideSkimmer.class));
         cards.add(new SetCardInfo("Tormod's Crypt", 241, Rarity.UNCOMMON, mage.cards.t.TormodsCrypt.class));
         cards.add(new SetCardInfo("Ugin, the Spirit Dragon", 1, Rarity.MYTHIC, mage.cards.u.UginTheSpiritDragon.class));
+        cards.add(new SetCardInfo("Village Rites", 126, Rarity.COMMON, mage.cards.v.VillageRites.class));
         cards.add(new SetCardInfo("Vito, Thorn of the Dusk Rose", 127, Rarity.RARE, mage.cards.v.VitoThornOfTheDuskRose.class));
         cards.add(new SetCardInfo("Wildwood Patrol", 339, Rarity.COMMON, mage.cards.w.WildwoodPatrol.class));
     }

From 94bd458628ddf86900ea2908da23dda6efada1d6 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 9 Jun 2020 19:54:41 -0400
Subject: [PATCH 185/586] Implemented Angelic Ascension

---
 .../src/mage/cards/a/AngelicAscension.java    | 68 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 69 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/a/AngelicAscension.java

diff --git a/Mage.Sets/src/mage/cards/a/AngelicAscension.java b/Mage.Sets/src/mage/cards/a/AngelicAscension.java
new file mode 100644
index 0000000000..4d99578f64
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/a/AngelicAscension.java
@@ -0,0 +1,68 @@
+package mage.cards.a;
+
+import mage.abilities.Ability;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.ExileTargetEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Outcome;
+import mage.game.Game;
+import mage.game.permanent.Permanent;
+import mage.game.permanent.token.AngelToken;
+import mage.game.permanent.token.Token;
+import mage.target.common.TargetCreatureOrPlaneswalker;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class AngelicAscension extends CardImpl {
+
+    public AngelicAscension(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}");
+
+        // Exile target creature or planeswalker. Its controller creates a 4/4 white Angel creature token with flying.
+        this.getSpellAbility().addEffect(new ExileTargetEffect());
+        this.getSpellAbility().addEffect(new AngelicAscensionEffect());
+        this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker());
+    }
+
+    private AngelicAscension(final AngelicAscension card) {
+        super(card);
+    }
+
+    @Override
+    public AngelicAscension copy() {
+        return new AngelicAscension(this);
+    }
+}
+
+class AngelicAscensionEffect extends OneShotEffect {
+
+    private static final Token token = new AngelToken();
+
+    AngelicAscensionEffect() {
+        super(Outcome.PutCreatureInPlay);
+        this.staticText = "Its controller creates a 4/4 white Angel creature token with flying";
+    }
+
+    private AngelicAscensionEffect(final AngelicAscensionEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public AngelicAscensionEffect copy() {
+        return new AngelicAscensionEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Permanent permanent = game.getPermanentOrLKIBattlefield(source.getFirstTarget());
+        if (permanent == null) {
+            return false;
+        }
+        return token.putOntoBattlefield(1, game, source.getSourceId(), permanent.getControllerId());
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index e233bfe37e..02fa4f475d 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -34,6 +34,7 @@ public final class CoreSet2021 extends ExpansionSet {
         this.maxCardNumberInBooster = 274;
 
         cards.add(new SetCardInfo("Adherent of Hope", 321, Rarity.COMMON, mage.cards.a.AdherentOfHope.class));
+        cards.add(new SetCardInfo("Angelic Ascension", 3, Rarity.UNCOMMON, mage.cards.a.AngelicAscension.class));
         cards.add(new SetCardInfo("Azusa, Lost but Seeking", 173, Rarity.RARE, mage.cards.a.AzusaLostButSeeking.class));
         cards.add(new SetCardInfo("Bad Deal", 89, Rarity.UNCOMMON, mage.cards.b.BadDeal.class));
         cards.add(new SetCardInfo("Baneslayer Angel", 6, Rarity.MYTHIC, mage.cards.b.BaneslayerAngel.class));

From 2096229af86482834777bb13498c47580ea06b48 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 9 Jun 2020 19:59:29 -0400
Subject: [PATCH 186/586] Implemented Selfless Savior

---
 .../src/mage/cards/s/SelflessSavior.java      | 55 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 56 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/s/SelflessSavior.java

diff --git a/Mage.Sets/src/mage/cards/s/SelflessSavior.java b/Mage.Sets/src/mage/cards/s/SelflessSavior.java
new file mode 100644
index 0000000000..a5af3c0118
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/SelflessSavior.java
@@ -0,0 +1,55 @@
+package mage.cards.s;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.common.SacrificeSourceCost;
+import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
+import mage.abilities.keyword.IndestructibleAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+import mage.filter.FilterPermanent;
+import mage.filter.common.FilterCreaturePermanent;
+import mage.filter.predicate.permanent.AnotherPredicate;
+import mage.target.TargetPermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class SelflessSavior extends CardImpl {
+
+    private static final FilterPermanent filter = new FilterCreaturePermanent("another target creature");
+
+    static {
+        filter.add(AnotherPredicate.instance);
+    }
+
+    public SelflessSavior(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}");
+
+        this.subtype.add(SubType.DOG);
+        this.power = new MageInt(1);
+        this.toughness = new MageInt(1);
+
+        // Sacrifice Selfless Savior: Another target creature gains indestructible until end of turn.
+        Ability ability = new SimpleActivatedAbility(new GainAbilityTargetEffect(
+                IndestructibleAbility.getInstance(), Duration.EndOfTurn
+        ), new SacrificeSourceCost());
+        ability.addTarget(new TargetPermanent(filter));
+        this.addAbility(ability);
+    }
+
+    private SelflessSavior(final SelflessSavior card) {
+        super(card);
+    }
+
+    @Override
+    public SelflessSavior copy() {
+        return new SelflessSavior(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 02fa4f475d..2ca4ea903d 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -99,6 +99,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Runed Halo", 32, Rarity.RARE, mage.cards.r.RunedHalo.class));
         cards.add(new SetCardInfo("Seasoned Hallowblade", 34, Rarity.UNCOMMON, mage.cards.s.SeasonedHallowblade.class));
         cards.add(new SetCardInfo("See the Truth", 69, Rarity.RARE, mage.cards.s.SeeTheTruth.class));
+        cards.add(new SetCardInfo("Selfless Savior", 36, Rarity.UNCOMMON, mage.cards.s.SelflessSavior.class));
         cards.add(new SetCardInfo("Shipwreck Dowser", 71, Rarity.UNCOMMON, mage.cards.s.ShipwreckDowser.class));
         cards.add(new SetCardInfo("Sigiled Contender", 323, Rarity.UNCOMMON, mage.cards.s.SigiledContender.class));
         cards.add(new SetCardInfo("Solemn Simulacrum", 239, Rarity.RARE, mage.cards.s.SolemnSimulacrum.class));

From b38ac2f575c3da161f98ac3fdc2048c9fc446169 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Wed, 10 Jun 2020 08:28:18 +0400
Subject: [PATCH 187/586] * Split cards - added cost modification effects
 support for fused spells (#227, #2242, #6603, #6549);

---
 .../cards/abilities/keywords/BestowTest.java  |  14 +-
 .../TappedForManaFromMultipleEffects.java     |   4 +-
 ...astSplitCardsWithCostModificationTest.java | 122 +++++++++++++++++-
 .../java/org/mage/test/player/TestPlayer.java |  12 +-
 Mage/src/main/java/mage/game/stack/Spell.java |  34 ++---
 .../main/java/mage/players/PlayerImpl.java    |   8 +-
 6 files changed, 149 insertions(+), 45 deletions(-)

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BestowTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BestowTest.java
index 5427cb8460..6dd7e5e397 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BestowTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BestowTest.java
@@ -11,7 +11,6 @@ import org.junit.Test;
 import org.mage.test.serverside.base.CardTestPlayerBase;
 
 /**
- *
  * @author LevelX2
  */
 public class BestowTest extends CardTestPlayerBase {
@@ -20,7 +19,6 @@ public class BestowTest extends CardTestPlayerBase {
      * Tests that if from bestow permanent targeted creature gets protection
      * from the color of the bestow permanent, the bestow permanent becomes a
      * creature on the battlefield.
-     *
      */
 
     /* Silent Artisan
@@ -157,7 +155,7 @@ public class BestowTest extends CardTestPlayerBase {
      * // Away casting both sides, will the creature that has bestow come in
      * time for it to be sacrificed or does it fully resolve before the creature
      * comes in?
-     *
+     * <p>
      * Bestowed creature can be used to sacrifice a creature for the Away part.
      * http://www.mtgsalvation.com/forums/magic-fundamentals/magic-rulings/magic-rulings-archives/513828-bestow-far-away
      */
@@ -188,12 +186,16 @@ public class BestowTest extends CardTestPlayerBase {
 
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Nyxborn Rollicker using bestow", "Cyclops of One-Eyed Pass");
 
-        castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "fused Far // Away", "Cyclops of One-Eyed Pass");
-        addTarget(playerB, playerA);
+        castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "fused Far // Away");
+        addTarget(playerB, "Cyclops of One-Eyed Pass"); // Far
+        addTarget(playerB, playerA); // Away
         addTarget(playerA, "Nyxborn Rollicker");
 
+
+        setStrictChooseMode(true);
         setStopAt(1, PhaseStep.END_TURN);
         execute();
+        assertAllCommandsUsed();
 
         assertHandCount(playerA, "Cyclops of One-Eyed Pass", 1);
         assertHandCount(playerB, 0);
@@ -246,8 +248,6 @@ public class BestowTest extends CardTestPlayerBase {
     }
 
     /**
-     *
-     *
      *
      */
     @Test
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/TappedForManaFromMultipleEffects.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/TappedForManaFromMultipleEffects.java
index e892764104..91f3f82a04 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/TappedForManaFromMultipleEffects.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/TappedForManaFromMultipleEffects.java
@@ -55,8 +55,8 @@ public class TappedForManaFromMultipleEffects extends CardTestPlayerBase {
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Nyxbloom Ancient");
         // TODO: TAPPED_FOR_MANA replace event called from checkTappedForManaReplacement and start to choose replace events (is that problem?)
         // use case (that test): comment one 1-2 choices to fail in 1-2 calls
-        setChoice(playerA, "Nyxbloom Ancient: If you tap a permanent"); // getPlayable... checkTappedForManaReplacement... chooseReplacementEffect
-        setChoice(playerA, "Nyxbloom Ancient: If you tap a permanent"); // playManaAbility... resolve... checkToFirePossibleEvents... chooseReplacementEffect
+        setChoice(playerA, "Nyxbloom Ancient"); // getPlayable... checkTappedForManaReplacement... chooseReplacementEffect
+        setChoice(playerA, "Nyxbloom Ancient"); // playManaAbility... resolve... checkToFirePossibleEvents... chooseReplacementEffect
 
         // cast chloro
         castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Chlorophant");
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithCostModificationTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithCostModificationTest.java
index ae6f1a97d6..0e01666d15 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithCostModificationTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithCostModificationTest.java
@@ -1,8 +1,11 @@
 package org.mage.test.cards.split;
 
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.effects.common.cost.SpellsCostReductionAllEffect;
 import mage.constants.PhaseStep;
 import mage.constants.Zone;
-import org.junit.Ignore;
+import mage.filter.FilterCard;
+import mage.filter.predicate.mageobject.NamePredicate;
 import org.junit.Test;
 import org.mage.test.serverside.base.CardTestPlayerBase;
 
@@ -11,6 +14,118 @@ import org.mage.test.serverside.base.CardTestPlayerBase;
  */
 public class CastSplitCardsWithCostModificationTest extends CardTestPlayerBase {
 
+    private void prepareReduceEffect(String cardNameToReduce, int reduceAmount) {
+        FilterCard filter = new FilterCard();
+        filter.add(new NamePredicate(cardNameToReduce));
+        addCustomCardWithAbility("reduce", playerA, new SimpleStaticAbility(
+                new SpellsCostReductionAllEffect(filter, reduceAmount))
+        );
+    }
+
+    @Test
+    public void test_Playable_Left() {
+        // cost reduce for easy test
+        prepareReduceEffect("Armed", 3);
+
+        // Armed {1}{R} Target creature gets +1/+1 and gains double strike until end of turn.
+        // Dangerous {3}{G} All creatures able to block target creature this turn do so.
+        addCard(Zone.HAND, playerA, "Armed // Dangerous", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
+        //addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1);
+
+        checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Armed", true);
+        checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Dangerous", false);
+        checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast fused Armed // Dangerous", false);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Armed", "Balduvian Bears");
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        assertGraveyardCount(playerA, "Armed // Dangerous", 1);
+    }
+
+    @Test
+    public void test_Playable_Right() {
+        // cost reduce for easy test
+        prepareReduceEffect("Dangerous", 3);
+
+        // Armed {1}{R} Target creature gets +1/+1 and gains double strike until end of turn.
+        // Dangerous {3}{G} All creatures able to block target creature this turn do so.
+        addCard(Zone.HAND, playerA, "Armed // Dangerous", 1);
+        //addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1);
+
+        checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Armed", false);
+        checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Dangerous", true);
+        checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast fused Armed // Dangerous", false);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dangerous", "Balduvian Bears");
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        assertGraveyardCount(playerA, "Armed // Dangerous", 1);
+    }
+
+    @Test
+    public void test_Playable_Fused_Left() {
+        // cost reduce for easy test
+        prepareReduceEffect("Armed", 4);
+
+        // Armed {1}{R} Target creature gets +1/+1 and gains double strike until end of turn.
+        // Dangerous {3}{G} All creatures able to block target creature this turn do so.
+        addCard(Zone.HAND, playerA, "Armed // Dangerous", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1);
+
+        checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Armed", true);
+        checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Dangerous", false);
+        checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast fused Armed // Dangerous", true);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "fused Armed // Dangerous");
+        addTarget(playerA, "Balduvian Bears");
+        addTarget(playerA, "Balduvian Bears");
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        assertGraveyardCount(playerA, "Armed // Dangerous", 1);
+    }
+
+    @Test
+    public void test_Playable_Fused_Right() {
+        // cost reduce for easy test
+        prepareReduceEffect("Dangerous", 4);
+
+        // Armed {1}{R} Target creature gets +1/+1 and gains double strike until end of turn.
+        // Dangerous {3}{G} All creatures able to block target creature this turn do so.
+        addCard(Zone.HAND, playerA, "Armed // Dangerous", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1);
+
+        checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Armed", true); // no reduced, but have basic lands ({G}{R})
+        checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Dangerous", true);
+        checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast fused Armed // Dangerous", true);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "fused Armed // Dangerous");
+        addTarget(playerA, "Balduvian Bears");
+        addTarget(playerA, "Balduvian Bears");
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        assertGraveyardCount(playerA, "Armed // Dangerous", 1);
+    }
+
     @Test
     public void test_CostReduction_Simple() {
         // {2}{W}{U}
@@ -26,6 +141,8 @@ public class CastSplitCardsWithCostModificationTest extends CardTestPlayerBase {
         addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears", 1);
 
         // cast Council
+        activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {W}", 3);
+        activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}", 1);
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Council of the Absolute");
         setChoice(playerA, "Blastfire Bolt");
 
@@ -69,7 +186,6 @@ public class CastSplitCardsWithCostModificationTest extends CardTestPlayerBase {
         checkPlayableAbility("after reduction", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Armed", true);
         checkPlayableAbility("after reduction", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Dangerous", false);
         checkPlayableAbility("after reduction", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast fused Armed // Dangerous", false);
-        showAvaileableAbilities("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Armed", "Balduvian Bears");
 
         setStrictChooseMode(true);
@@ -118,7 +234,6 @@ public class CastSplitCardsWithCostModificationTest extends CardTestPlayerBase {
     }
 
     @Test
-    @Ignore // TODO: cost modification don't work for fused spells, only for one of the part, see https://github.com/magefree/mage/issues/6603
     public void test_CostReduction_SplitFused_ReduceRight() {
         // {2}{W}{U}
         // As Council of the Absolute enters the battlefield, choose a noncreature, nonland card name.
@@ -159,7 +274,6 @@ public class CastSplitCardsWithCostModificationTest extends CardTestPlayerBase {
     }
 
     @Test
-    @Ignore // TODO: cost modification don't work for fused spells, only for one of the part, see https://github.com/magefree/mage/issues/6603
     public void test_CostReduction_SplitFused_ReduceLeft() {
         // {2}{W}{U}
         // As Council of the Absolute enters the battlefield, choose a noncreature, nonland card name.
diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
index b43b200626..0cff76fc8d 100644
--- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
+++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
@@ -314,17 +314,7 @@ public class TestPlayer implements Player {
             if (group.startsWith("spell") || group.startsWith("!spell") || group.startsWith("target=null") || group.startsWith("manaInPool=")) {
                 break;
             }
-            if (ability instanceof SpellAbility && ((SpellAbility) ability).getSpellAbilityType() == SpellAbilityType.SPLIT_FUSED) {
-                if (group.contains("FuseLeft-")) {
-                    result = handleTargetString(group.substring(group.indexOf("FuseLeft-") + 9), ability, game);
-                } else if (group.startsWith("FuseRight-")) {
-                    result = handleTargetString(group.substring(group.indexOf("FuseRight-") + 10), ability, game);
-                } else {
-                    result = false;
-                }
-            } else {
-                result = handleTargetString(group, ability, game);
-            }
+            result = handleTargetString(group, ability, game);
         }
         return result;
     }
diff --git a/Mage/src/main/java/mage/game/stack/Spell.java b/Mage/src/main/java/mage/game/stack/Spell.java
index 42c6ba27ec..dd96e8421e 100644
--- a/Mage/src/main/java/mage/game/stack/Spell.java
+++ b/Mage/src/main/java/mage/game/stack/Spell.java
@@ -124,24 +124,28 @@ public class Spell extends StackObjImpl implements Card {
     }
 
     public boolean activate(Game game, boolean noMana) {
-        setDoneActivatingManaAbilities(false); // Used for e.g. improvise
-        if (!spellAbilities.get(0).activate(game, noMana)) {
+        setDoneActivatingManaAbilities(false); // used for e.g. improvise
+
+        if (!ability.activate(game, noMana)) {
             return false;
         }
-        if (spellAbilities.size() > 1) {
-            // if there are more abilities (fused split spell) or first ability added new abilities (splice), activate the additional abilities
-            boolean ignoreAbility = true;
+
+        // spell can contains multiple abilities to activate (fused split, splice)
+        for (SpellAbility spellAbility : spellAbilities) {
+            if (ability.equals(spellAbility)) {
+                // activated first
+                continue;
+            }
+
             boolean payNoMana = noMana;
-            for (SpellAbility spellAbility : spellAbilities) {
-                if (ignoreAbility) {
-                    ignoreAbility = false;
-                } else {
-                    // costs for spliced abilities were added to main spellAbility, so pay no mana for spliced abilities
-                    payNoMana |= spellAbility.getSpellAbilityType() == SpellAbilityType.SPLICE;
-                    if (!spellAbility.activate(game, payNoMana)) {
-                        return false;
-                    }
-                }
+            // costs for spliced abilities were added to main spellAbility, so pay no mana for spliced abilities
+            payNoMana |= spellAbility.getSpellAbilityType() == SpellAbilityType.SPLICE;
+            // costs for fused ability pay on first spell activate, so all parts must be without mana
+            // see https://github.com/magefree/mage/issues/6603
+            payNoMana |= ability.getSpellAbilityType() == SpellAbilityType.SPLIT_FUSED;
+
+            if (!spellAbility.activate(game, payNoMana)) {
+                return false;
             }
         }
         setDoneActivatingManaAbilities(true); // can be activated again maybe during the resolution of the spell (e.g. Metallic Rebuke)
diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java
index 95d6c8b320..dbdd179cad 100644
--- a/Mage/src/main/java/mage/players/PlayerImpl.java
+++ b/Mage/src/main/java/mage/players/PlayerImpl.java
@@ -2699,11 +2699,7 @@ public abstract class PlayerImpl implements Player, Serializable {
                         (event.isWinnable() ? "(You called " + event.getChosenName() + ")" : null),
                         "Heads", "Tails", source, game
                 ));
-            } else if (canChooseHeads) {
-                event.setResult(true);
-            } else {
-                event.setResult(false);
-            }
+            } else event.setResult(canChooseHeads);
             game.informPlayers(getLogName() + " chose to keep " + CardUtil.booleanToFlipName(event.getResult()));
         }
         if (event.isWinnable()) {
@@ -2935,7 +2931,7 @@ public abstract class PlayerImpl implements Player, Serializable {
      */
     protected boolean canPlay(ActivatedAbility ability, ManaOptions available, MageObject sourceObject, Game game) {
         if (!(ability instanceof ActivatedManaAbilityImpl)) {
-            ActivatedAbility copy = ability.copy(); // Copy is needed because cost reduction effects modify e.g. the mana to activate/cast the ability
+            ActivatedAbility copy = ability.copy(); // copy is needed because cost reduction effects modify e.g. the mana to activate/cast the ability
             if (!copy.canActivate(playerId, game).canActivate()) {
                 return false;
             }

From a75d08283fb8f9ea143363bda0940167dc0ca504 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Wed, 10 Jun 2020 09:58:21 +0400
Subject: [PATCH 188/586] * Split cards - added spliced effects support for
 fused spells, no more double splice pays (#6493, #6549);

---
 .../split/CastSplitCardsWithSpliceTest.java   | 99 +++++++++++++++++++
 .../java/org/mage/test/player/TestPlayer.java |  1 +
 .../main/java/mage/abilities/AbilityImpl.java | 10 +-
 .../abilities/effects/ContinuousEffects.java  | 15 ++-
 Mage/src/main/java/mage/util/CardUtil.java    | 24 +++++
 5 files changed, 144 insertions(+), 5 deletions(-)
 create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithSpliceTest.java

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithSpliceTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithSpliceTest.java
new file mode 100644
index 0000000000..e9d2b3d5b1
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithSpliceTest.java
@@ -0,0 +1,99 @@
+package org.mage.test.cards.split;
+
+import mage.constants.PhaseStep;
+import mage.constants.Zone;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestPlayerBase;
+
+/**
+ * @author JayDi85
+ */
+public class CastSplitCardsWithSpliceTest extends CardTestPlayerBase {
+
+    // splice cost applies for one fused spell, not to every part
+    // https://github.com/magefree/mage/issues/6493
+
+    @Test
+    public void test_ThaliaGuardianOfThraben_CostModification_Fused() {
+        removeAllCardsFromHand(playerA);
+
+        // Noncreature spells cost {1} more to cast.
+        addCard(Zone.BATTLEFIELD, playerA, "Thalia, Guardian of Thraben", 1);
+        //
+        // Wear {1}{R} Destroy target artifact.
+        // Tear {W} Destroy target enchantment.
+        addCard(Zone.HAND, playerA, "Wear // Tear", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2 + 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 1);
+        addCard(Zone.BATTLEFIELD, playerB, "Bident of Thassa", 1); // Legendary Enchantment Artifact
+        addCard(Zone.BATTLEFIELD, playerB, "Bow of Nylea", 1); // Legendary Enchantment Artifact
+
+        // cast fused
+        // old bug: https://github.com/magefree/mage/issues/6493
+        // * getPlayable checks fused cost (+1 modification)
+        // * activate checks fused cost (+1 modification) andeach card's part (+1 modification)
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "fused Wear // Tear");
+        addTarget(playerA, "Bident of Thassa");
+        addTarget(playerA, "Bow of Nylea");
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertGraveyardCount(playerA, "Wear // Tear", 1);
+        assertGraveyardCount(playerB, "Bident of Thassa", 1);
+        assertGraveyardCount(playerB, "Bow of Nylea", 1);
+        assertHandCount(playerA, 0);
+
+        // must used all mana
+        assertTappedCount("Mountain", true, 3);
+        assertTappedCount("Plains", true, 1);
+    }
+
+    @Test
+    public void test_ThaliaGuardianOfThraben_CostModification_FusedWithSplice() {
+        removeAllCardsFromHand(playerA);
+
+        // Noncreature spells cost {1} more to cast.
+        addCard(Zone.BATTLEFIELD, playerA, "Thalia, Guardian of Thraben", 1);
+        //
+        // Draw a card.
+        // Splice onto instant or sorcery {2}{U}
+        addCard(Zone.HAND, playerA, "Everdream", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 3); // for splice
+        //
+        // Wear {1}{R} Destroy target artifact.
+        // Tear {W} Destroy target enchantment.
+        addCard(Zone.HAND, playerA, "Wear // Tear", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2 + 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 1);
+        addCard(Zone.BATTLEFIELD, playerB, "Bident of Thassa", 1); // Legendary Enchantment Artifact
+        addCard(Zone.BATTLEFIELD, playerB, "Bow of Nylea", 1); // Legendary Enchantment Artifact
+
+        // cast fused with splice
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "fused Wear // Tear");
+        setChoice(playerA, "Yes"); // use splice
+        addTarget(playerA, "Everdream"); // card to splice
+        addTarget(playerA, "Bident of Thassa"); // target left
+        addTarget(playerA, "Bow of Nylea"); // target right
+
+        // must used all mana
+        //showAvaileableMana("after", 1, PhaseStep.POSTCOMBAT_MAIN, playerA);
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertGraveyardCount(playerA, "Wear // Tear", 1);
+        assertGraveyardCount(playerB, "Bident of Thassa", 1);
+        assertGraveyardCount(playerB, "Bow of Nylea", 1);
+        assertHandCount(playerA, 2); // splice card + draw effect from spliced
+
+        // must used all mana
+        assertTappedCount("Island", true, 3);
+        assertTappedCount("Mountain", true, 3);
+        assertTappedCount("Plains", true, 1);
+    }
+}
diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
index 0cff76fc8d..a4627fe0f8 100644
--- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
+++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
@@ -1077,6 +1077,7 @@ public class TestPlayer implements Player {
         List<String> data = abilities.stream()
                 .map(a -> (a.getZone() + " -> "
                         + a.getSourceObject(game).getIdName() + " -> "
+                        + (a.toString().startsWith("Cast ") ? "[" + a.getManaCostsToPay().getText() + "] -> " : "") // printed cost, not modified
                         + (a.toString().length() > 0
                         ? a.toString().substring(0, Math.min(20, a.toString().length()))
                         : a.getClass().getSimpleName())
diff --git a/Mage/src/main/java/mage/abilities/AbilityImpl.java b/Mage/src/main/java/mage/abilities/AbilityImpl.java
index f09b94f616..62a2d3fea1 100644
--- a/Mage/src/main/java/mage/abilities/AbilityImpl.java
+++ b/Mage/src/main/java/mage/abilities/AbilityImpl.java
@@ -24,6 +24,7 @@ import mage.players.Player;
 import mage.target.Target;
 import mage.target.Targets;
 import mage.target.targetadjustment.TargetAdjuster;
+import mage.util.CardUtil;
 import mage.util.GameLog;
 import mage.util.ThreadLocalStringBuilder;
 import mage.watchers.Watcher;
@@ -352,8 +353,15 @@ public abstract class AbilityImpl implements Ability {
             return false;
         }
 
+        // fused spell contains 3 abilities (fused, left, right)
+        // fused cost added to fused ability, so no need cost modification for other parts
+        boolean needCostModification = true;
+        if (CardUtil.isFusedPartAbility(this, game)) {
+            needCostModification = false;
+        }
+
         //20101001 - 601.2e
-        if (sourceObject != null) {
+        if (needCostModification && sourceObject != null) {
             sourceObject.adjustCosts(this, game); // still needed
             game.getContinuousEffects().costModification(this, game);
         }
diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java b/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java
index bf7d2beed1..022eefef31 100644
--- a/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java
+++ b/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java
@@ -4,7 +4,6 @@ import mage.MageObject;
 import mage.MageObjectReference;
 import mage.abilities.Ability;
 import mage.abilities.MageSingleton;
-import mage.abilities.SpellAbility;
 import mage.abilities.StaticAbility;
 import mage.abilities.effects.common.continuous.BecomesFaceDownCreatureEffect;
 import mage.abilities.effects.common.continuous.CommanderReplacementEffect;
@@ -24,6 +23,7 @@ import mage.game.stack.Spell;
 import mage.players.ManaPoolItem;
 import mage.players.Player;
 import mage.target.common.TargetCardInHand;
+import mage.util.CardUtil;
 import org.apache.log4j.Logger;
 
 import java.io.Serializable;
@@ -229,7 +229,7 @@ public class ContinuousEffects implements Serializable {
      * "actual" meaning it becomes turned on that is defined by
      * Ability.#isInUseableZone(Game, boolean) method in
      * #getLayeredEffects(Game).
-     *
+     * <p>
      * It must be called with different timestamp group name (otherwise sort order will be changed for add/remove effects, see Urborg and Bloodmoon test)
      *
      * @param layerEffects
@@ -703,10 +703,17 @@ public class ContinuousEffects implements Serializable {
      * @param game
      */
     public void applySpliceEffects(Ability abilityToModify, Game game) {
-        if (((SpellAbility) abilityToModify).getSpellAbilityType() == SpellAbilityType.SPLICE) {
-            // on a spliced ability of a spell can't be spliced again
+        // add effects from splice card to spell ability on activate/cast
+
+        // splice spell - spell can't be spliced again
+        if (CardUtil.isSpliceAbility(abilityToModify, game)) {
             return;
         }
+        // fused spell - can be spliced only to main fused ability, not to parts
+        if (CardUtil.isFusedPartAbility(abilityToModify, game)) {
+            return;
+        }
+
         List<SpliceCardEffect> spliceEffects = getApplicableSpliceCardEffects(game, abilityToModify.getControllerId());
         // get the applyable splice abilities
         List<Ability> spliceAbilities = new ArrayList<>();
diff --git a/Mage/src/main/java/mage/util/CardUtil.java b/Mage/src/main/java/mage/util/CardUtil.java
index b418ad2fd8..1d1b72f079 100644
--- a/Mage/src/main/java/mage/util/CardUtil.java
+++ b/Mage/src/main/java/mage/util/CardUtil.java
@@ -10,12 +10,14 @@ import mage.cards.Card;
 import mage.constants.ColoredManaSymbol;
 import mage.constants.EmptyNames;
 import mage.constants.ManaType;
+import mage.constants.SpellAbilityType;
 import mage.filter.Filter;
 import mage.filter.predicate.mageobject.NamePredicate;
 import mage.game.CardState;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
 import mage.game.permanent.token.Token;
+import mage.game.stack.Spell;
 import mage.util.functions.CopyTokenFunction;
 
 import java.io.UnsupportedEncodingException;
@@ -767,4 +769,26 @@ public final class CardUtil {
 
         return signedP + "/" + signedT;
     }
+
+    public static boolean isSpliceAbility(Ability ability, Game game) {
+        if (ability instanceof SpellAbility) {
+            return ((SpellAbility) ability).getSpellAbilityType() == SpellAbilityType.SPLICE;
+        }
+        return false;
+    }
+
+    public static boolean isFusedPartAbility(Ability ability, Game game) {
+        // TODO: is works fine with copies of spells on stack?
+        if (ability instanceof SpellAbility) {
+            Spell mainSpell = game.getSpell(ability.getId());
+            if (mainSpell == null) {
+                return true;
+            } else {
+                SpellAbility mainSpellAbility = mainSpell.getSpellAbility();
+                return mainSpellAbility.getSpellAbilityType() == SpellAbilityType.SPLIT_FUSED
+                        && !ability.equals(mainSpellAbility);
+            }
+        }
+        return false;
+    }
 }

From 5c65ee31d996b7ce0b6ee16cae4661161dc55bcb Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Wed, 10 Jun 2020 10:17:55 +0200
Subject: [PATCH 189/586] * Mirari - Fixed that the triggering spell was not
 copied (fixes #6338).

---
 Mage.Sets/src/mage/cards/m/Mirari.java                    | 8 +-------
 Mage.Sets/src/mage/cards/p/Putrefy.java                   | 2 +-
 .../java/org/mage/test/cards/copy/SpelltwineTest.java     | 7 ++-----
 Mage/src/main/java/mage/game/stack/StackObjImpl.java      | 2 +-
 4 files changed, 5 insertions(+), 14 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/m/Mirari.java b/Mage.Sets/src/mage/cards/m/Mirari.java
index 7a209fa01a..20482a095f 100644
--- a/Mage.Sets/src/mage/cards/m/Mirari.java
+++ b/Mage.Sets/src/mage/cards/m/Mirari.java
@@ -79,13 +79,7 @@ class MirariTriggeredAbility extends TriggeredAbilityImpl {
         if (event.getPlayerId().equals(this.getControllerId())) {
             Spell spell = game.getStack().getSpell(event.getTargetId());
             if (isControlledInstantOrSorcery(spell)) {
-                for (Effect effect : getEffects()) {
-                    if (effect instanceof DoIfCostPaid) {
-                        for (Effect execEffect : ((DoIfCostPaid) effect).getExecutingEffects()) {
-                            execEffect.setTargetPointer(new FixedTarget(spell.getId()));
-                        }
-                    }
-                }
+                getEffects().setTargetPointer(new FixedTarget(spell.getId()));
                 return true;
             }
         }
diff --git a/Mage.Sets/src/mage/cards/p/Putrefy.java b/Mage.Sets/src/mage/cards/p/Putrefy.java
index b0b3c0f39b..f467c3a875 100644
--- a/Mage.Sets/src/mage/cards/p/Putrefy.java
+++ b/Mage.Sets/src/mage/cards/p/Putrefy.java
@@ -27,7 +27,7 @@ public final class Putrefy extends CardImpl {
     public Putrefy (UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{B}{G}");
 
-
+        // Destroy target artifact or creature. It can't be regenerated.
         this.getSpellAbility().addTarget(new TargetPermanent(filter));
         this.getSpellAbility().addEffect(new DestroyTargetEffect(true));
     }
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/SpelltwineTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/SpelltwineTest.java
index f4859dc486..b6e792f528 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/SpelltwineTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/SpelltwineTest.java
@@ -48,7 +48,6 @@ public class SpelltwineTest extends CardTestPlayerBase {
      * after this, failing to be in the stack box or resolve all.
      */
     @Test
-    @Ignore // TODO: test is wrong -- mirari exile cards and must cast their copies, on copies cast mirari triggers again (two times).
     public void testCopyCardsMirari() {
         addCard(Zone.BATTLEFIELD, playerA, "Island", 9);
         // Exile target instant or sorcery card from your graveyard and target instant or sorcery card from an opponent's graveyard.
@@ -69,10 +68,8 @@ public class SpelltwineTest extends CardTestPlayerBase {
 
         // cast spellwin
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Spelltwine");
-        addTarget(playerA, "Impulse"); // target 1 to excile
-        addTarget(playerA, "Blasphemous Act"); // target 2 to excile
-
-
+        addTarget(playerA, "Impulse"); // target 1 to exile
+        addTarget(playerA, "Blasphemous Act"); // target 2 to exile
 
         setChoice(playerA, "Yes"); //  pay {3} and copy spell
         setChoice(playerA, "Yes"); // Change targets
diff --git a/Mage/src/main/java/mage/game/stack/StackObjImpl.java b/Mage/src/main/java/mage/game/stack/StackObjImpl.java
index 8d7130458a..2bfcbbf5c2 100644
--- a/Mage/src/main/java/mage/game/stack/StackObjImpl.java
+++ b/Mage/src/main/java/mage/game/stack/StackObjImpl.java
@@ -272,7 +272,7 @@ public abstract class StackObjImpl implements StackObject {
                 name = targetPlayer.getLogName();
             }
         } else {
-            name = object.getName();
+            name = object.getIdName();
         }
         return name;
     }

From 0a6b693691f7b24d5b3466e238f65906afa37c38 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Wed, 10 Jun 2020 12:19:10 +0200
Subject: [PATCH 190/586] * Fixed not working casting cost reduction of 17
 creatures.

---
 Mage.Sets/src/mage/cards/a/AcademyJourneymage.java     |  2 +-
 Mage.Sets/src/mage/cards/a/AvatarOfMight.java          |  2 +-
 Mage.Sets/src/mage/cards/a/AvatarOfWill.java           |  2 +-
 Mage.Sets/src/mage/cards/a/AvatarOfWoe.java            |  2 +-
 Mage.Sets/src/mage/cards/d/Draco.java                  |  2 +-
 Mage.Sets/src/mage/cards/d/DuskFeaster.java            |  4 ++--
 Mage.Sets/src/mage/cards/e/EmryLurkerOfTheLoch.java    |  2 +-
 Mage.Sets/src/mage/cards/g/GateColossus.java           |  2 +-
 Mage.Sets/src/mage/cards/g/GearseekerSerpent.java      |  2 +-
 Mage.Sets/src/mage/cards/g/GhaltaPrimalHunger.java     |  2 +-
 Mage.Sets/src/mage/cards/k/KaradorGhostChieftain.java  |  2 +-
 Mage.Sets/src/mage/cards/k/KhalniHydra.java            | 10 +++++++---
 Mage.Sets/src/mage/cards/l/LiciaSanguineTribune.java   |  2 +-
 Mage.Sets/src/mage/cards/m/MarshmistTitan.java         |  2 +-
 Mage.Sets/src/mage/cards/m/MetalworkColossus.java      |  2 +-
 Mage.Sets/src/mage/cards/s/Stratadon.java              |  2 +-
 Mage.Sets/src/mage/cards/t/TorgaarFamineIncarnate.java |  2 +-
 17 files changed, 24 insertions(+), 20 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/a/AcademyJourneymage.java b/Mage.Sets/src/mage/cards/a/AcademyJourneymage.java
index cb65e5f8f5..239620e279 100644
--- a/Mage.Sets/src/mage/cards/a/AcademyJourneymage.java
+++ b/Mage.Sets/src/mage/cards/a/AcademyJourneymage.java
@@ -38,7 +38,7 @@ public final class AcademyJourneymage extends CardImpl {
         this.toughness = new MageInt(2);
 
         // This spell costs {1} less to cast if you control a Wizard.
-        Ability ability = new SimpleStaticAbility(Zone.STACK, new SpellCostReductionSourceEffect(1, new PermanentsOnTheBattlefieldCondition(filter)));
+        Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(1, new PermanentsOnTheBattlefieldCondition(filter)));
         ability.setRuleAtTheTop(true);
         this.addAbility(ability);
 
diff --git a/Mage.Sets/src/mage/cards/a/AvatarOfMight.java b/Mage.Sets/src/mage/cards/a/AvatarOfMight.java
index 8c127b2a30..c35a03c79b 100644
--- a/Mage.Sets/src/mage/cards/a/AvatarOfMight.java
+++ b/Mage.Sets/src/mage/cards/a/AvatarOfMight.java
@@ -29,7 +29,7 @@ public final class AvatarOfMight extends CardImpl {
         this.toughness = new MageInt(8);
 
         // If an opponent controls at least four more creatures than you, Avatar of Might costs {6} less to cast.
-        this.addAbility(new SimpleStaticAbility(Zone.STACK, new AvatarOfMightCostReductionEffect()));
+        this.addAbility(new SimpleStaticAbility(Zone.ALL, new AvatarOfMightCostReductionEffect()));
 
         // Trample
         this.addAbility(TrampleAbility.getInstance());
diff --git a/Mage.Sets/src/mage/cards/a/AvatarOfWill.java b/Mage.Sets/src/mage/cards/a/AvatarOfWill.java
index 89651d2328..c57877756a 100644
--- a/Mage.Sets/src/mage/cards/a/AvatarOfWill.java
+++ b/Mage.Sets/src/mage/cards/a/AvatarOfWill.java
@@ -28,7 +28,7 @@ public final class AvatarOfWill extends CardImpl {
         this.toughness = new MageInt(6);
 
         // If an opponent has no cards in hand, Avatar of Will costs {6} less to cast.
-        this.addAbility(new SimpleStaticAbility(Zone.STACK, new AvatarOfWillCostReductionEffect()));
+        this.addAbility(new SimpleStaticAbility(Zone.ALL, new AvatarOfWillCostReductionEffect()));
 
         // Flying
         this.addAbility(FlyingAbility.getInstance());
diff --git a/Mage.Sets/src/mage/cards/a/AvatarOfWoe.java b/Mage.Sets/src/mage/cards/a/AvatarOfWoe.java
index a8f9ca1893..02115afba8 100644
--- a/Mage.Sets/src/mage/cards/a/AvatarOfWoe.java
+++ b/Mage.Sets/src/mage/cards/a/AvatarOfWoe.java
@@ -32,7 +32,7 @@ public final class AvatarOfWoe extends CardImpl {
         this.toughness = new MageInt(5);
 
         // If there are ten or more creature cards total in all graveyards, Avatar of Woe costs {6} less to cast.
-        this.addAbility(new SimpleStaticAbility(Zone.STACK, new AvatarOfWoeCostReductionEffect()));
+        this.addAbility(new SimpleStaticAbility(Zone.ALL, new AvatarOfWoeCostReductionEffect()));
 
         // Fear
         this.addAbility(FearAbility.getInstance());
diff --git a/Mage.Sets/src/mage/cards/d/Draco.java b/Mage.Sets/src/mage/cards/d/Draco.java
index 973d2433ef..736a234e7a 100644
--- a/Mage.Sets/src/mage/cards/d/Draco.java
+++ b/Mage.Sets/src/mage/cards/d/Draco.java
@@ -35,7 +35,7 @@ public final class Draco extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
 
         // Domain - Draco costs {2} less to cast for each basic land type among lands you control.
-        this.addAbility(new SimpleStaticAbility(Zone.STACK, new DracoCostReductionEffect()));
+        this.addAbility(new SimpleStaticAbility(Zone.ALL, new DracoCostReductionEffect()));
 
         // Domain - At the beginning of your upkeep, sacrifice Draco unless you pay {10}. This cost is reduced by {2} for each basic land type among lands you control.
         this.addAbility(new BeginningOfUpkeepTriggeredAbility(new DracoSacrificeUnlessPaysEffect(), TargetController.YOU, false));
diff --git a/Mage.Sets/src/mage/cards/d/DuskFeaster.java b/Mage.Sets/src/mage/cards/d/DuskFeaster.java
index 96c7fbaea3..8d586d0640 100644
--- a/Mage.Sets/src/mage/cards/d/DuskFeaster.java
+++ b/Mage.Sets/src/mage/cards/d/DuskFeaster.java
@@ -29,8 +29,8 @@ public final class DuskFeaster extends CardImpl {
         this.power = new MageInt(4);
         this.toughness = new MageInt(5);
 
-        // <i>Delirium</i> &mdash; Dusk Feaster costs {2} less to cast if there are four or more card types among cards in your graveyard.
-        this.addAbility(new SimpleStaticAbility(Zone.STACK, new DuskFeasterCostReductionEffect()));
+        // Delirium - Dusk Feaster costs {2} less to cast if there are four or more card types among cards in your graveyard.
+        this.addAbility(new SimpleStaticAbility(Zone.ALL, new DuskFeasterCostReductionEffect()));
 
         // Flying
         this.addAbility(FlyingAbility.getInstance());
diff --git a/Mage.Sets/src/mage/cards/e/EmryLurkerOfTheLoch.java b/Mage.Sets/src/mage/cards/e/EmryLurkerOfTheLoch.java
index d8338673a1..b1cc1188f4 100644
--- a/Mage.Sets/src/mage/cards/e/EmryLurkerOfTheLoch.java
+++ b/Mage.Sets/src/mage/cards/e/EmryLurkerOfTheLoch.java
@@ -35,7 +35,7 @@ public final class EmryLurkerOfTheLoch extends CardImpl {
         this.toughness = new MageInt(2);
 
         // This spell costs {1} less to cast for each artifact you control.
-        this.addAbility(new SimpleStaticAbility(Zone.STACK, new EmryLurkerOfTheLochCostReductionEffect()));
+        this.addAbility(new SimpleStaticAbility(Zone.ALL, new EmryLurkerOfTheLochCostReductionEffect()));
 
         // When Emry, Lurker of the Loch enters the battlefield, put the top four cards of your library into your graveyard.
         this.addAbility(new EntersBattlefieldTriggeredAbility(
diff --git a/Mage.Sets/src/mage/cards/g/GateColossus.java b/Mage.Sets/src/mage/cards/g/GateColossus.java
index 42420c65f4..b1c013f495 100644
--- a/Mage.Sets/src/mage/cards/g/GateColossus.java
+++ b/Mage.Sets/src/mage/cards/g/GateColossus.java
@@ -39,7 +39,7 @@ public final class GateColossus extends CardImpl {
         this.toughness = new MageInt(8);
 
         // This spell costs {1} less to cast for each Gate you control.
-        this.addAbility(new SimpleStaticAbility(Zone.STACK, new GateColossusCostReductionEffect())
+        this.addAbility(new SimpleStaticAbility(Zone.ALL, new GateColossusCostReductionEffect())
                 .addHint(GateYouControlHint.instance));
 
         // Gate Colossus can't be blocked by creatures with power 2 or less.
diff --git a/Mage.Sets/src/mage/cards/g/GearseekerSerpent.java b/Mage.Sets/src/mage/cards/g/GearseekerSerpent.java
index f4fffd3401..b350bc7107 100644
--- a/Mage.Sets/src/mage/cards/g/GearseekerSerpent.java
+++ b/Mage.Sets/src/mage/cards/g/GearseekerSerpent.java
@@ -29,7 +29,7 @@ public final class GearseekerSerpent extends CardImpl {
         this.toughness = new MageInt(6);
 
         // Gearseeker Serpent costs {1} less to cast for each artifact you control
-        this.addAbility(new SimpleStaticAbility(Zone.STACK, new GearseekerSerpentCostReductionEffect()));
+        this.addAbility(new SimpleStaticAbility(Zone.ALL, new GearseekerSerpentCostReductionEffect()));
 
         // 5U: Gearseeker Serpent can't be blocked this turn.
         this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD,
diff --git a/Mage.Sets/src/mage/cards/g/GhaltaPrimalHunger.java b/Mage.Sets/src/mage/cards/g/GhaltaPrimalHunger.java
index e60680a5b6..c4dcf0929c 100644
--- a/Mage.Sets/src/mage/cards/g/GhaltaPrimalHunger.java
+++ b/Mage.Sets/src/mage/cards/g/GhaltaPrimalHunger.java
@@ -40,7 +40,7 @@ public final class GhaltaPrimalHunger extends CardImpl {
         this.toughness = new MageInt(12);
 
         // Ghalta, Primal Hunger costs {X} less to cast, where X is the total power of creatures you control.
-        this.addAbility(new SimpleStaticAbility(Zone.STACK, new GhaltaPrimalHungerCostReductionEffect()));
+        this.addAbility(new SimpleStaticAbility(Zone.ALL, new GhaltaPrimalHungerCostReductionEffect()));
         // Trample
         this.addAbility(TrampleAbility.getInstance());
 
diff --git a/Mage.Sets/src/mage/cards/k/KaradorGhostChieftain.java b/Mage.Sets/src/mage/cards/k/KaradorGhostChieftain.java
index b5ac0e5a96..f5345fe00d 100644
--- a/Mage.Sets/src/mage/cards/k/KaradorGhostChieftain.java
+++ b/Mage.Sets/src/mage/cards/k/KaradorGhostChieftain.java
@@ -38,7 +38,7 @@ public final class KaradorGhostChieftain extends CardImpl {
         this.toughness = new MageInt(4);
 
         // Karador, Ghost Chieftain costs {1} less to cast for each creature card in your graveyard.
-        this.addAbility(new SimpleStaticAbility(Zone.STACK,
+        this.addAbility(new SimpleStaticAbility(Zone.ALL,
                 new KaradorGhostChieftainCostReductionEffect()));
 
         // During each of your turns, you may cast one creature card from your graveyard.
diff --git a/Mage.Sets/src/mage/cards/k/KhalniHydra.java b/Mage.Sets/src/mage/cards/k/KhalniHydra.java
index 0421a15082..b0d7f0a841 100644
--- a/Mage.Sets/src/mage/cards/k/KhalniHydra.java
+++ b/Mage.Sets/src/mage/cards/k/KhalniHydra.java
@@ -39,7 +39,11 @@ public final class KhalniHydra extends CardImpl {
 
         this.power = new MageInt(8);
         this.toughness = new MageInt(8);
-        this.addAbility(new SimpleStaticAbility(Zone.STACK, new KhalniHydraCostReductionEffect()));
+        
+        // This spell costs {G} less to cast for each green creature you control.
+        this.addAbility(new SimpleStaticAbility(Zone.ALL, new KhalniHydraCostReductionEffect()));
+        
+        // Trample
         this.addAbility(TrampleAbility.getInstance());
     }
 
@@ -54,8 +58,8 @@ public final class KhalniHydra extends CardImpl {
         Iterator<ManaCost> iter = ability.getManaCostsToPay().iterator();
 
         while ( reductionAmount > 0 && iter.hasNext() ) {
-            ManaCost manaCost = iter.next();
-            if (manaCost.getMana().getGreen() > 0) { // in case another effect adds additional mana cost
+            ManaCost manaCostEntry = iter.next();
+            if (manaCostEntry.getMana().getGreen() > 0) { // in case another effect adds additional mana cost
                 iter.remove();
                 reductionAmount--;
             }
diff --git a/Mage.Sets/src/mage/cards/l/LiciaSanguineTribune.java b/Mage.Sets/src/mage/cards/l/LiciaSanguineTribune.java
index aa1c372205..67e9c624b5 100644
--- a/Mage.Sets/src/mage/cards/l/LiciaSanguineTribune.java
+++ b/Mage.Sets/src/mage/cards/l/LiciaSanguineTribune.java
@@ -40,7 +40,7 @@ public final class LiciaSanguineTribune extends CardImpl {
         this.toughness = new MageInt(4);
 
         // Licia, Sanguine Tribune costs 1 less to cast for each 1 life you gained this turn.
-        this.addAbility(new SimpleStaticAbility(Zone.STACK, new LiciaSanguineTribuneCostReductionEffect()), new PlayerGainedLifeWatcher());
+        this.addAbility(new SimpleStaticAbility(Zone.ALL, new LiciaSanguineTribuneCostReductionEffect()), new PlayerGainedLifeWatcher());
 
         // First strike
         this.addAbility(FirstStrikeAbility.getInstance());
diff --git a/Mage.Sets/src/mage/cards/m/MarshmistTitan.java b/Mage.Sets/src/mage/cards/m/MarshmistTitan.java
index 71df001f00..a3c86710c0 100644
--- a/Mage.Sets/src/mage/cards/m/MarshmistTitan.java
+++ b/Mage.Sets/src/mage/cards/m/MarshmistTitan.java
@@ -27,7 +27,7 @@ public final class MarshmistTitan extends CardImpl {
         this.toughness = new MageInt(5);
 
         // Marshmist Titan costs {X} less to cast, where X is your devotion to black.
-        this.addAbility(new SimpleStaticAbility(Zone.STACK, new MarshmistTitanCostReductionEffect())
+        this.addAbility(new SimpleStaticAbility(Zone.ALL, new MarshmistTitanCostReductionEffect())
                 .addHint(DevotionCount.B.getHint()));
     }
 
diff --git a/Mage.Sets/src/mage/cards/m/MetalworkColossus.java b/Mage.Sets/src/mage/cards/m/MetalworkColossus.java
index 6052cf4bb7..47895a12a5 100644
--- a/Mage.Sets/src/mage/cards/m/MetalworkColossus.java
+++ b/Mage.Sets/src/mage/cards/m/MetalworkColossus.java
@@ -39,7 +39,7 @@ public final class MetalworkColossus extends CardImpl {
         this.toughness = new MageInt(10);
 
         // Metalwork Colossus costs {X} less to cast, where X is the total converted mana cost of noncreature artifacts you control.
-        this.addAbility(new SimpleStaticAbility(Zone.STACK, new MetalworkColossusCostReductionEffect()));
+        this.addAbility(new SimpleStaticAbility(Zone.ALL, new MetalworkColossusCostReductionEffect()));
 
         // Sacrifice two artifacts: Return Metalwork Colossus from your graveyard to your hand.
         this.addAbility(new SimpleActivatedAbility(Zone.GRAVEYARD, new ReturnSourceFromGraveyardToHandEffect(), new SacrificeTargetCost(new TargetControlledPermanent(2, 2, new FilterControlledArtifactPermanent("two artifacts"), true))));
diff --git a/Mage.Sets/src/mage/cards/s/Stratadon.java b/Mage.Sets/src/mage/cards/s/Stratadon.java
index 3394c00fb7..ce3a704473 100644
--- a/Mage.Sets/src/mage/cards/s/Stratadon.java
+++ b/Mage.Sets/src/mage/cards/s/Stratadon.java
@@ -32,7 +32,7 @@ public final class Stratadon extends CardImpl {
         this.toughness = new MageInt(5);
 
         // Domain - Stratadon costs {1} less to cast for each basic land type among lands you control.
-        this.addAbility(new SimpleStaticAbility(Zone.STACK, new StratadonCostReductionEffect()));
+        this.addAbility(new SimpleStaticAbility(Zone.ALL, new StratadonCostReductionEffect()));
         // Trample
         this.addAbility(TrampleAbility.getInstance());
     }
diff --git a/Mage.Sets/src/mage/cards/t/TorgaarFamineIncarnate.java b/Mage.Sets/src/mage/cards/t/TorgaarFamineIncarnate.java
index e98b05af41..9cdaf57aba 100644
--- a/Mage.Sets/src/mage/cards/t/TorgaarFamineIncarnate.java
+++ b/Mage.Sets/src/mage/cards/t/TorgaarFamineIncarnate.java
@@ -45,7 +45,7 @@ public final class TorgaarFamineIncarnate extends CardImpl {
         cost.setText("As an additional cost to cast this spell, you may sacrifice any number of creatures");
         this.getSpellAbility().addCost(cost);
         // This spell costs {2} less to cast for each creature sacrificed this way.
-        this.addAbility(new SimpleStaticAbility(Zone.STACK, new TorgaarFamineIncarnateEffectCostReductionEffect()));
+        this.addAbility(new SimpleStaticAbility(Zone.ALL, new TorgaarFamineIncarnateEffectCostReductionEffect()));
 
         // When Torgaar, Famine Incarnate enters the battlefield, up to one target player's life total becomes half their starting life total, rounded down.
         Ability ability = new EntersBattlefieldTriggeredAbility(new TorgaarFamineIncarnateEffect(), false);

From 2d2b39aaa30f54c43ea80fc32d0e2501bbf9d6a0 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Wed, 10 Jun 2020 07:42:15 -0400
Subject: [PATCH 191/586] updated M21 spoiler and reprints

---
 Mage.Sets/src/mage/sets/CoreSet2021.java | 1 +
 Utils/mtg-cards-data.txt                 | 8 ++++++--
 2 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 2ca4ea903d..ba0582fa24 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -81,6 +81,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Mangara, the Diplomat", 27, Rarity.MYTHIC, mage.cards.m.MangaraTheDiplomat.class));
         cards.add(new SetCardInfo("Massacre Wurm", 114, Rarity.MYTHIC, mage.cards.m.MassacreWurm.class));
         cards.add(new SetCardInfo("Mazemind Tome", 232, Rarity.RARE, mage.cards.m.MazemindTome.class));
+        cards.add(new SetCardInfo("Meteorite", 233, Rarity.UNCOMMON, mage.cards.m.Meteorite.class));
         cards.add(new SetCardInfo("Mountain", 312, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Mystic Skyfish", 326, Rarity.COMMON, mage.cards.m.MysticSkyfish.class));
         cards.add(new SetCardInfo("Necromentia", 116, Rarity.RARE, mage.cards.n.Necromentia.class));
diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt
index cfc856b13f..5b279971e4 100644
--- a/Utils/mtg-cards-data.txt
+++ b/Utils/mtg-cards-data.txt
@@ -37446,7 +37446,8 @@ Basri's Acolyte|Core Set 2021|8|C|{2}{W}{W}|Creature - Cat Cleric|2|3|Lifelink$W
 Basri's Lieutenant|Core Set 2021|9|R|{3}{W}|Creature - Human Knight|3|4|Vigilance, protection from multicolored$When Basri's Lieutenant enters the battlefield, put a +1/+1 counter on target creature you control.$Whenever Basri's Lieutenant or another creature you control dies, if it had a +1/+1 counter on it, create a 2/2 white Knight creature token with vigilance.|
 Basri's Solidarity|Core Set 2021|10|U|{1}{W}|Sorcery|||Put a +1/+1 counter on each creature you control.|
 Faith's Fetters|Core Set 2021|17|U|{3}{W}|Enchantment - Aura|||Enchant permanent$When Faith's Fetters enters the battlefield, you gain 4 life.$Enchanted permanent can't attack or block, and its activated abilities can't be activated unless they're mana abilities.|
-Idol of Endurance|Core Set 2021|23|R|{2}{W}|Artifact|||When Idol of Endurance enters the battlefield, exile all creature cards with converted mana cost 3 or less from your graveyard until Idol of Endurance leaves the battlefield.${1}{W}, {T}: Until end of turn, you may cast a creature spell from among the cards exiled with Idol of Endurance without payingg its mana cost.|
+Idol of Endurance|Core Set 2021|23|R|{2}{W}|Artifact|||When Idol of Endurance enters the battlefield, exile all creature cards with converted mana cost 3 or less from your graveyard until Idol of Endurance leaves the battlefield.${1}{W}, {T}: Until end of turn, you may cast a creature spell from among the cards exiled with Idol of Endurance without paying its mana cost.|
+Light of Promise|Core Set 2021|25|U|{2}{W}|Enchantment - Aura|||Enchant creature$Enchanted creature has "Whenever you gain life, put that many +1/+1 counters on this creature."|
 Mangara, the Diplomat|Core Set 2021|27|M|{3}{W}|Legendary Creature - Human Cleric|2|4|Lifelink$Whenever an opponent attacks with creatures, if two or more of those creatures are attacking you and/or a planeswalker you control, draw a card.$Whenever an opponent casts their second spell each turn, draw a card.|
 Pack Leader|Core Set 2021|29|R|{1}{W}|Creature - Dog|2|2|Other Dogs you control get +1/+1.$Whenever Pack Leader attacks, prevent all combat damage that would be dealt this turn to Dogs you control.|
 Revitalize|Core Set 2021|31|C|{1}{W}|Instant|||You gain 3 life.$Draw a card.|
@@ -37503,11 +37504,14 @@ Llanowar Visionary|Core Set 2021|193|C|{2}{G}|Creature - Elf Druid|2|2|When Llan
 Pridemalkin|Core Set 2021|196|C|{2}{G}|Creature - Cat|2|1|When Pridemalkin enters the battlefield, put a +1/+1 counter on target creature you control.$Each creature you control with a +1/+1 counter on it has trample.|
 Primal Might|Core Set 2021|197|R|{X}{G}|Sorcery|||Target creature you control gets +X/+X until end of turn. Then it fights up to one target creature you don't control.|
 Quirion Dryad|Core Set 2021|198|U|{1}{G}|Creature - Dryad|1|1|Whenever you cast a white, blue, black, or red spell, put a +1/+1 counter on Quirion Dryad.|
+Warden of the Woods|Core Set 2021|213|U|{4}{G}{G}|Creature - Treefolk|5|7|Vigilance$Whenever Warden of the Woods becomes the target of a spell or ability an opponent controls, you may draw two cards.|
+Alpine Houndmaster|Core Set 2021|215|U|{R}{W}|Creature - Human Warrior|2|2|When Alpine Houndmaster enters the battlefield, you may search your library for a card named Alpine Watchdog and/or a card named Igneous Cur, reveal them, put them into your hand, then shuffle your library.$Whenever Alpine Houndmaster attacks, it gets +X/+0 until end of turn, where X is the number of other attacking creatures.|
 Indulging Patrician|Core Set 2021|219|U|{1}{W}{B}|Creature - Vampire Noble|1|4|Flying$Lifelink$At the beginning of your end step, if you gained 3 or more life this turn, each opponent loses 3 life.|
 Lorescale Coatl|Core Set 2021|221|U|{1}{G}{U}|Creature - Snake|2|2|Whenever you draw a card, you may put a +1/+1 counter on Lorescale Coatl.|
 Obsessive Stitcher|Core Set 2021|223|U|{1}{U}{B}|Creature - Human Wizard|0|3|{T}: Draw a card, then discard a card.${2}{U}{B}, {T}, Sacrifice Obsessive Stitcher: Return target creature card from your graveyard to the battlefield.|
 Chromatic Orrery|Core Set 2021|228|M|{7}|Legendary Artifact|||You may spend mana as though it were mana of any color.${T}: Add {C}{C}{C}{C}{C}.${5}, {T}: Draw a card for each color among permanents you control.|
-Mazemind Tome|Core Set 2021|232|R|{2}|Artifact|||{T}, Put a page counter on Mazemind Tome: Scry 1.${2}, {T}, Put a page counter on Mazemind Tome: Draw a card.$Whenever there are four or more page counters on Mazemind Tome, exile it. If you do, you gain 4 life.|
+Mazemind Tome|Core Set 2021|232|R|{2}|Artifact|||{T}, Put a page counter on Mazemind Tome: Scry 1.${2}, {T}, Put a page counter on Mazemind Tome: Draw a card.$When there are four or more page counters on Mazemind Tome, exile it. If you do, you gain 4 life.|
+Meteorite|Core Set 2021|233|U|{5}|Artifact|||When Meteorite enters the battlefield, it deals 2 damage to any target.${T}: Add one mana of any color.|
 Solemn Simulacrum|Core Set 2021|239|R|{4}|Artifact Creature - Golem|2|2|When Solemn Simulacrum enters the battlefield, you may search your library for a basic land card, put that card onto the battlefield tapped, then shuffle your library.$When Solemn Simulacrum dies, you may draw a card.|
 Sparkhunter Masticore|Core Set 2021|240|R|{3}|Artifact Creature - Masticore|3|4|As an additional cost to cast this spell, discard a card.$Protection from planeswalkers${1}: Sparkhunter Masticore deals 1 damage to target planeswalker.${3}: Sparkhunter Masticore gains indestructible until end of turn.|
 Tormod's Crypt|Core Set 2021|241|U|{0}|Artifact|||{T}, Sacrifice Tormod's Crypt: Exile all cards from target player's graveyard.|

From 3a3f04d233b7a546feee294c8ab1175a4ea7383c Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Wed, 10 Jun 2020 09:10:03 -0400
Subject: [PATCH 192/586] Implemented Alpine Houndmaster

---
 .../src/mage/cards/a/AlpineHoundmaster.java   | 140 ++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |   1 +
 2 files changed, 141 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/a/AlpineHoundmaster.java

diff --git a/Mage.Sets/src/mage/cards/a/AlpineHoundmaster.java b/Mage.Sets/src/mage/cards/a/AlpineHoundmaster.java
new file mode 100644
index 0000000000..adbd5c2f3a
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/a/AlpineHoundmaster.java
@@ -0,0 +1,140 @@
+package mage.cards.a;
+
+import mage.MageInt;
+import mage.MageObject;
+import mage.abilities.Ability;
+import mage.abilities.common.AttacksTriggeredAbility;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.dynamicvalue.DynamicValue;
+import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
+import mage.abilities.dynamicvalue.common.StaticValue;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.continuous.BoostSourceEffect;
+import mage.cards.*;
+import mage.constants.*;
+import mage.filter.FilterCard;
+import mage.filter.FilterPermanent;
+import mage.filter.common.FilterAttackingCreature;
+import mage.filter.predicate.Predicates;
+import mage.filter.predicate.mageobject.NamePredicate;
+import mage.filter.predicate.permanent.AnotherPredicate;
+import mage.game.Game;
+import mage.players.Player;
+import mage.target.common.TargetCardInLibrary;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class AlpineHoundmaster extends CardImpl {
+
+    private static final FilterPermanent filter = new FilterAttackingCreature();
+
+    static {
+        filter.add(AnotherPredicate.instance);
+    }
+
+    private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter);
+
+    public AlpineHoundmaster(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}{W}");
+
+        this.subtype.add(SubType.HUMAN);
+        this.subtype.add(SubType.WARRIOR);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(2);
+
+        // When Alpine Houndmaster enters the battlefield, you may search your library for a card named Alpine Watchdog and/or a card named Igneous Cur, reveal them, put them into your hand, then shuffle your library.
+        this.addAbility(new EntersBattlefieldTriggeredAbility(new AlpineHoundmasterEffect(), true));
+
+        // Whenever Alpine Houndmaster attacks, it gets +X/+0 until end of turn, where X is the number of other attacking creatures.
+        this.addAbility(new AttacksTriggeredAbility(new BoostSourceEffect(
+                xValue, StaticValue.get(0), Duration.EndOfTurn
+        ), false, "it gets +X/+0 until end of turn, where X is the number of other attacking creatures"));
+    }
+
+    private AlpineHoundmaster(final AlpineHoundmaster card) {
+        super(card);
+    }
+
+    @Override
+    public AlpineHoundmaster copy() {
+        return new AlpineHoundmaster(this);
+    }
+}
+
+class AlpineHoundmasterEffect extends OneShotEffect {
+
+    AlpineHoundmasterEffect() {
+        super(Outcome.Benefit);
+        staticText = "search your library for a card named Alpine Watchdog and/or a card named Igneous Cur, " +
+                "reveal them, put them into your hand, then shuffle your library";
+    }
+
+    private AlpineHoundmasterEffect(final AlpineHoundmasterEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public AlpineHoundmasterEffect copy() {
+        return new AlpineHoundmasterEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Player player = game.getPlayer(source.getControllerId());
+        if (player == null) {
+            return false;
+        }
+        TargetCardInLibrary target = new AlpineHoundmasterTarget();
+        player.searchLibrary(target, source, game);
+        Cards cards = new CardsImpl(target.getTargets());
+        player.revealCards(source, cards, game);
+        player.moveCards(cards, Zone.HAND, source, game);
+        player.shuffleLibrary(source, game);
+        return true;
+    }
+}
+
+class AlpineHoundmasterTarget extends TargetCardInLibrary {
+
+    private static final FilterCard filter
+            = new FilterCard("card named Alpine Watchdog and/or a card named Igneous Cur");
+
+    static {
+        filter.add(Predicates.or(
+                new NamePredicate("Alpine Watchdog"),
+                new NamePredicate("Igneous Cur")
+        ));
+    }
+
+    AlpineHoundmasterTarget() {
+        super(0, 2, filter);
+    }
+
+    private AlpineHoundmasterTarget(final AlpineHoundmasterTarget target) {
+        super(target);
+    }
+
+    @Override
+    public AlpineHoundmasterTarget copy() {
+        return new AlpineHoundmasterTarget(this);
+    }
+
+    @Override
+    public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
+        if (!super.canTarget(controllerId, id, source, game)) {
+            return false;
+        }
+        Card card = game.getCard(id);
+        if (card == null) {
+            return false;
+        }
+        return this.getTargets()
+                .stream()
+                .map(game::getCard)
+                .map(MageObject::getName)
+                .noneMatch(card.getName()::equals);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index ba0582fa24..789bf68f63 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -34,6 +34,7 @@ public final class CoreSet2021 extends ExpansionSet {
         this.maxCardNumberInBooster = 274;
 
         cards.add(new SetCardInfo("Adherent of Hope", 321, Rarity.COMMON, mage.cards.a.AdherentOfHope.class));
+        cards.add(new SetCardInfo("Alpine Houndmaster", 215, Rarity.UNCOMMON, mage.cards.a.AlpineHoundmaster.class));
         cards.add(new SetCardInfo("Angelic Ascension", 3, Rarity.UNCOMMON, mage.cards.a.AngelicAscension.class));
         cards.add(new SetCardInfo("Azusa, Lost but Seeking", 173, Rarity.RARE, mage.cards.a.AzusaLostButSeeking.class));
         cards.add(new SetCardInfo("Bad Deal", 89, Rarity.UNCOMMON, mage.cards.b.BadDeal.class));

From 5a17b9665ee560f5a1231e8c9c48d3968f2f3d6c Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Wed, 10 Jun 2020 17:17:40 +0200
Subject: [PATCH 193/586] * Fixed text generation for cards going from library
 to graveyard.

---
 .../effects/common/PutLibraryIntoGraveTargetEffect.java       | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Mage/src/main/java/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java
index 412e1ae355..c0933fbfb2 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java
@@ -64,14 +64,14 @@ public class PutLibraryIntoGraveTargetEffect extends OneShotEffect {
         } else {
             sb.append("that target");
         }
-
-        sb.append(" mills ");
+        sb.append(" puts the top ");
         if (message.isEmpty()) {
             if (amount.toString().equals("1")) {
                 sb.append("a card");
             } else {
                 sb.append(CardUtil.numberToText(amount.toString())).append(" cards");
             }
+            sb.append(" of their library into their graveyard");
         } else {
             sb.append(" X cards, where X is the number of ");
         }

From 1ef8aeb7ba688d05174c9ccc629e8398f801856c Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Wed, 10 Jun 2020 17:20:52 +0200
Subject: [PATCH 194/586] * Biomancer's Familiar - Fixed cost reduction effect
 working wrongly for up to 2 mana and creating exceptions. * Training Grounds
 - Fixed exception during get playable method.

---
 .../src/mage/cards/b/BiomancersFamiliar.java  | 14 +----------
 .../src/mage/cards/t/TrainingGrounds.java     | 25 +++++++++++--------
 2 files changed, 16 insertions(+), 23 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/b/BiomancersFamiliar.java b/Mage.Sets/src/mage/cards/b/BiomancersFamiliar.java
index b6e4df29f6..eb1b89e658 100644
--- a/Mage.Sets/src/mage/cards/b/BiomancersFamiliar.java
+++ b/Mage.Sets/src/mage/cards/b/BiomancersFamiliar.java
@@ -86,19 +86,7 @@ class BiomancersFamiliarCostReductionEffect extends CostModificationEffectImpl {
         if (reduceMax <= 0) {
             return true;
         }
-        ChoiceImpl choice = new ChoiceImpl(true);
-        Set<String> set = new LinkedHashSet<>();
-
-        for (int i = 0; i <= reduceMax; i++) {
-            set.add(String.valueOf(i));
-        }
-        choice.setChoices(set);
-        choice.setMessage("Reduce ability cost");
-        if (!controller.choose(Outcome.Benefit, choice, game)) {
-            return false;
-        }
-        int reduce = Integer.parseInt(choice.getChoice());
-        CardUtil.reduceCost(abilityToModify, reduce);
+        CardUtil.reduceCost(abilityToModify, reduceMax);
         return true;
 
     }
diff --git a/Mage.Sets/src/mage/cards/t/TrainingGrounds.java b/Mage.Sets/src/mage/cards/t/TrainingGrounds.java
index ee1fb79b69..ea756b6413 100644
--- a/Mage.Sets/src/mage/cards/t/TrainingGrounds.java
+++ b/Mage.Sets/src/mage/cards/t/TrainingGrounds.java
@@ -42,8 +42,8 @@ public final class TrainingGrounds extends CardImpl {
 
 class TrainingGroundsEffect extends CostModificationEffectImpl {
 
-    private static final String effectText = "Activated abilities of creatures you control cost {2} less to activate. " +
-            "This effect can't reduce the mana in that cost to less than one mana";
+    private static final String effectText = "Activated abilities of creatures you control cost up to {2} less to activate. "
+            + "This effect can't reduce the mana in that cost to less than one mana";
 
     TrainingGroundsEffect() {
         super(Duration.Custom, Outcome.Benefit, CostModificationType.REDUCE_COST);
@@ -74,15 +74,20 @@ class TrainingGroundsEffect extends CostModificationEffectImpl {
         ChoiceImpl choice = new ChoiceImpl(true);
         Set<String> set = new LinkedHashSet<>();
 
-        for (int i = 0; i <= reduceMax; i++) {
-            set.add(String.valueOf(i));
+        int reduce;
+        if (game.inCheckPlayableState()) {
+            reduce = reduceMax;
+        } else {
+            for (int i = 0; i <= reduceMax; i++) {
+                set.add(String.valueOf(i));
+            }
+            choice.setChoices(set);
+            choice.setMessage("Reduce ability cost");
+            if (!controller.choose(Outcome.Benefit, choice, game)) {
+                return false;
+            }
+            reduce = Integer.parseInt(choice.getChoice());
         }
-        choice.setChoices(set);
-        choice.setMessage("Reduce ability cost");
-        if (!controller.choose(Outcome.Benefit, choice, game)) {
-            return false;
-        }
-        int reduce = Integer.parseInt(choice.getChoice());
         CardUtil.reduceCost(abilityToModify, reduce);
         return true;
 

From 25802dc10526099800d76c3195552965af1b6a6b Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Wed, 10 Jun 2020 22:08:51 +0200
Subject: [PATCH 195/586] * Updated some failing tests with old text after fix
 of 5a17b9665ee560f5a1231e8c9c48d3968f2f3d6c.

---
 .../test/cards/continuous/MasterThiefTest.java  |  1 +
 .../cards/copy/VolrathsShapshifterTest.java     | 17 ++++++++---------
 .../common/PutLibraryIntoGraveTargetEffect.java |  2 +-
 3 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/MasterThiefTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/MasterThiefTest.java
index 8bfe2420bd..af26181472 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/MasterThiefTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/MasterThiefTest.java
@@ -34,6 +34,7 @@ public class MasterThiefTest extends CardTestPlayerBase {
     @Test
     public void testMasterThief_LostControlOnSacrifice() {
         addCard(Zone.BATTLEFIELD, playerA, "Island", 10);
+        // When Master Thief enters the battlefield, gain control of target artifact for as long as you control Master Thief.
         addCard(Zone.HAND, playerA, "Master Thief", 3);
         addCard(Zone.BATTLEFIELD, playerB, "Accorder's Shield", 1);
         addCard(Zone.BATTLEFIELD, playerA, "Bearer of the Heavens", 1);
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/VolrathsShapshifterTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/VolrathsShapshifterTest.java
index 9b3a196084..912088ef2f 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/VolrathsShapshifterTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/VolrathsShapshifterTest.java
@@ -1,15 +1,14 @@
 package org.mage.test.cards.copy;
 
 import mage.abilities.Ability;
-import mage.constants.SubType;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.mana.GenericManaCost;
 import mage.abilities.effects.common.discard.DiscardControllerEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.constants.PhaseStep;
+import mage.constants.SubType;
 import mage.constants.Zone;
 import mage.game.permanent.Permanent;
-
 import org.junit.Assert;
 import org.junit.Test;
 import org.mage.test.serverside.base.CardTestPlayerBase;
@@ -39,7 +38,7 @@ public class VolrathsShapshifterTest extends CardTestPlayerBase {
 
         setStopAt(1, PhaseStep.END_TURN);
         setStrictChooseMode(true);
-        execute();        
+        execute();
         assertAllCommandsUsed();
 
         assertPermanentCount(playerA, "Assault Griffin", 1);
@@ -59,7 +58,7 @@ public class VolrathsShapshifterTest extends CardTestPlayerBase {
     public void testLosingCopy() {
         addCard(Zone.BATTLEFIELD, playerA, "Volrath's Shapeshifter", 1);
         // Codex Shredder - Artifact
-        // {T}: Target player mills a card.
+        // {T}: {T}: Target player puts the top card of their library into their graveyard.
         // {5}, {T}, Sacrifice Codex Shredder: Return target card from your graveyard to your hand.
         addCard(Zone.BATTLEFIELD, playerA, "Codex Shredder", 1);
 
@@ -68,11 +67,11 @@ public class VolrathsShapshifterTest extends CardTestPlayerBase {
         addCard(Zone.LIBRARY, playerA, "Forest", 1);
         skipInitShuffling();
 
-        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills a card.", playerA);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player puts the top card of their library into their graveyard.", playerA);
 
         setStopAt(1, PhaseStep.END_TURN);
         setStrictChooseMode(true);
-        execute();        
+        execute();
         assertAllCommandsUsed();
 
         assertPermanentCount(playerA, "Volrath's Shapeshifter", 1);
@@ -99,11 +98,11 @@ public class VolrathsShapshifterTest extends CardTestPlayerBase {
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mind Control", "Assault Griffin");
 
         setStopAt(1, PhaseStep.END_TURN);
-        
+
         setStrictChooseMode(true);
-        execute();        
+        execute();
         assertAllCommandsUsed();
-        
+
         assertPermanentCount(playerA, "Dutiful Thrull", 1);
         assertPowerToughness(playerA, "Dutiful Thrull", 1, 1);
 
diff --git a/Mage/src/main/java/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java
index c0933fbfb2..0783542142 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java
@@ -67,7 +67,7 @@ public class PutLibraryIntoGraveTargetEffect extends OneShotEffect {
         sb.append(" puts the top ");
         if (message.isEmpty()) {
             if (amount.toString().equals("1")) {
-                sb.append("a card");
+                sb.append("card");
             } else {
                 sb.append(CardUtil.numberToText(amount.toString())).append(" cards");
             }

From 1e36b394345709ea31cde9c339471e5780de3422 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Wed, 10 Jun 2020 22:28:23 +0200
Subject: [PATCH 196/586] - Reworked
 SourceOnBattlefieldControlUnchangedCondition checking now the LOST_CONTROL
 event which solves the problem with the old code to not be able to detect all
 controller changes of layered changeController effects when applied later. -
 Simplified and fixed some problems of the handling of the "Until end of your
 next turn" duration. - Fixed that some continous effects changed controller
 but shouldn't dependant from their duration type. Controller chnage will now
 done duration type dependant.   (that change fixes #6581 in a more general
 way undoing the effect specific changes of
 2e8ece1dbd4618f5e6bccaaeb6f334d55c915bba).

---
 Mage.Sets/src/mage/cards/a/AegisAngel.java    |   3 +-
 Mage.Sets/src/mage/cards/a/Aladdin.java       |   5 +-
 .../src/mage/cards/d/DragonlordSilumgar.java  |   5 +-
 Mage.Sets/src/mage/cards/m/MasterThief.java   |   7 +-
 .../src/mage/cards/m/MeriekeRiBerit.java      |   3 +-
 .../src/mage/cards/o/OrcishSquatters.java     |   5 +-
 .../src/mage/cards/q/QuicksmithRebel.java     |   5 +-
 Mage.Sets/src/mage/cards/q/QuicksmithSpy.java |   5 +-
 Mage.Sets/src/mage/cards/r/RoilElemental.java |   3 +-
 Mage.Sets/src/mage/cards/t/TheWretched.java   |  55 +++--
 .../src/mage/cards/t/ThrullChampion.java      |   5 +-
 Mage.Sets/src/mage/cards/w/Willbreaker.java   |   8 +-
 Mage.Sets/src/mage/cards/w/WillowSatyr.java   |  13 +-
 .../cards/abilities/keywords/GoadTest.java    | 201 +++++++++++-------
 .../cards/conditional/TheWretchedTest.java    |  41 ++--
 .../cards/copy/LazavDimirMastermindTest.java  |  97 +++++----
 .../java/org/mage/test/player/TestPlayer.java |  86 ++++----
 ...nBattlefieldControlUnchangedCondition.java |  38 ++--
 .../abilities/effects/ContinuousEffect.java   |  11 +-
 .../effects/ContinuousEffectImpl.java         |  30 +--
 .../abilities/effects/ContinuousEffects.java  |  69 +++---
 .../effects/ContinuousEffectsList.java        |  23 +-
 .../common/combat/CantAttackYouEffect.java    |  16 +-
 .../common/combat/GoadTargetEffect.java       |   6 +-
 .../main/java/mage/constants/Duration.java    |  33 +--
 Mage/src/main/java/mage/game/GameImpl.java    |  32 ++-
 Mage/src/main/java/mage/game/GameState.java   |  24 +--
 .../main/java/mage/game/combat/Combat.java    |  34 +--
 .../main/java/mage/players/PlayerImpl.java    | 101 ++++-----
 .../watchers/common/LostControlWatcher.java   |  39 ++++
 30 files changed, 534 insertions(+), 469 deletions(-)
 create mode 100644 Mage/src/main/java/mage/watchers/common/LostControlWatcher.java

diff --git a/Mage.Sets/src/mage/cards/a/AegisAngel.java b/Mage.Sets/src/mage/cards/a/AegisAngel.java
index 7222995d67..bef99e72ea 100644
--- a/Mage.Sets/src/mage/cards/a/AegisAngel.java
+++ b/Mage.Sets/src/mage/cards/a/AegisAngel.java
@@ -1,4 +1,3 @@
-
 package mage.cards.a;
 
 import java.util.UUID;
@@ -18,6 +17,7 @@ import mage.constants.SubType;
 import mage.filter.FilterPermanent;
 import mage.filter.predicate.permanent.AnotherPredicate;
 import mage.target.TargetPermanent;
+import mage.watchers.common.LostControlWatcher;
 
 /**
  * @author Loki
@@ -47,6 +47,7 @@ public final class AegisAngel extends CardImpl {
                 "another target permanent is indestructible for as long as you control Aegis Angel");
         Ability ability = new EntersBattlefieldTriggeredAbility(effect, false);
         ability.addTarget(new TargetPermanent(filter));
+        ability.addWatcher(new LostControlWatcher());
         this.addAbility(ability);
     }
 
diff --git a/Mage.Sets/src/mage/cards/a/Aladdin.java b/Mage.Sets/src/mage/cards/a/Aladdin.java
index 7456422621..58b44e3ae8 100644
--- a/Mage.Sets/src/mage/cards/a/Aladdin.java
+++ b/Mage.Sets/src/mage/cards/a/Aladdin.java
@@ -1,4 +1,3 @@
-
 package mage.cards.a;
 
 import java.util.UUID;
@@ -17,6 +16,7 @@ import mage.constants.Duration;
 import mage.constants.SubType;
 import mage.constants.Zone;
 import mage.target.common.TargetArtifactPermanent;
+import mage.watchers.common.LostControlWatcher;
 
 /**
  *
@@ -25,7 +25,7 @@ import mage.target.common.TargetArtifactPermanent;
 public final class Aladdin extends CardImpl {
 
     public Aladdin(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{R}{R}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{R}");
         this.subtype.add(SubType.HUMAN);
         this.subtype.add(SubType.ROGUE);
         this.power = new MageInt(1);
@@ -39,6 +39,7 @@ public final class Aladdin extends CardImpl {
         Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("{1}{R}{R}"));
         ability.addCost(new TapSourceCost());
         ability.addTarget(new TargetArtifactPermanent());
+        ability.addWatcher(new LostControlWatcher());
         this.addAbility(ability);
     }
 
diff --git a/Mage.Sets/src/mage/cards/d/DragonlordSilumgar.java b/Mage.Sets/src/mage/cards/d/DragonlordSilumgar.java
index e4b4464ed3..ccf9f82913 100644
--- a/Mage.Sets/src/mage/cards/d/DragonlordSilumgar.java
+++ b/Mage.Sets/src/mage/cards/d/DragonlordSilumgar.java
@@ -1,4 +1,3 @@
-
 package mage.cards.d;
 
 import java.util.UUID;
@@ -16,9 +15,9 @@ import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.SubType;
 import mage.constants.Duration;
 import mage.constants.Outcome;
+import mage.constants.SubType;
 import mage.constants.SuperType;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
@@ -26,6 +25,7 @@ import mage.players.Player;
 import mage.target.common.TargetCreatureOrPlaneswalker;
 import mage.util.CardUtil;
 import mage.util.GameLog;
+import mage.watchers.common.LostControlWatcher;
 
 /**
  *
@@ -50,6 +50,7 @@ public final class DragonlordSilumgar extends CardImpl {
         // When Dragonlord Silumgar enters the battlefield, gain control of target creature or planeswalker for as long as you control Dragonlord Silumgar.
         Ability ability = new EntersBattlefieldTriggeredAbility(new DragonlordSilumgarEffect(), false);
         ability.addTarget(new TargetCreatureOrPlaneswalker());
+        ability.addWatcher(new LostControlWatcher());
         this.addAbility(ability);
 
     }
diff --git a/Mage.Sets/src/mage/cards/m/MasterThief.java b/Mage.Sets/src/mage/cards/m/MasterThief.java
index f712566544..c71f95d1a2 100644
--- a/Mage.Sets/src/mage/cards/m/MasterThief.java
+++ b/Mage.Sets/src/mage/cards/m/MasterThief.java
@@ -1,4 +1,3 @@
-
 package mage.cards.m;
 
 import java.util.UUID;
@@ -11,9 +10,10 @@ import mage.abilities.effects.common.continuous.GainControlTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.SubType;
 import mage.constants.Duration;
+import mage.constants.SubType;
 import mage.target.common.TargetArtifactPermanent;
+import mage.watchers.common.LostControlWatcher;
 
 /**
  * @author Loki, JayDi85
@@ -33,9 +33,10 @@ public final class MasterThief extends CardImpl {
                 new GainControlTargetEffect(Duration.Custom),
                 new SourceOnBattlefieldControlUnchangedCondition(),
                 "gain control of target artifact for as long as you control {this}"),
-        false);
+                false);
 
         ability.addTarget(new TargetArtifactPermanent());
+        ability.addWatcher(new LostControlWatcher());
         this.addAbility(ability);
     }
 
diff --git a/Mage.Sets/src/mage/cards/m/MeriekeRiBerit.java b/Mage.Sets/src/mage/cards/m/MeriekeRiBerit.java
index 1e2a0c4a5f..7e20fbf928 100644
--- a/Mage.Sets/src/mage/cards/m/MeriekeRiBerit.java
+++ b/Mage.Sets/src/mage/cards/m/MeriekeRiBerit.java
@@ -1,4 +1,3 @@
-
 package mage.cards.m;
 
 import java.util.UUID;
@@ -25,6 +24,7 @@ import mage.game.events.ZoneChangeEvent;
 import mage.game.permanent.Permanent;
 import mage.target.TargetPermanent;
 import mage.target.targetpointer.FixedTarget;
+import mage.watchers.common.LostControlWatcher;
 
 /**
  *
@@ -50,6 +50,7 @@ public final class MeriekeRiBerit extends CardImpl {
         Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, MeriekeRiBeritGainControlEffect, new TapSourceCost());
         ability.addTarget(new TargetPermanent(new FilterCreaturePermanent("target creature")));
         ability.addEffect(new MeriekeRiBeritCreateDelayedTriggerEffect());
+        ability.addWatcher(new LostControlWatcher());
         this.addAbility(ability);
 
     }
diff --git a/Mage.Sets/src/mage/cards/o/OrcishSquatters.java b/Mage.Sets/src/mage/cards/o/OrcishSquatters.java
index fd20a8044b..cfcbddab65 100644
--- a/Mage.Sets/src/mage/cards/o/OrcishSquatters.java
+++ b/Mage.Sets/src/mage/cards/o/OrcishSquatters.java
@@ -1,4 +1,3 @@
-
 package mage.cards.o;
 
 import java.util.UUID;
@@ -9,14 +8,15 @@ import mage.abilities.condition.common.SourceOnBattlefieldControlUnchangedCondit
 import mage.abilities.decorator.ConditionalContinuousEffect;
 import mage.abilities.effects.common.continuous.AssignNoCombatDamageSourceEffect;
 import mage.abilities.effects.common.continuous.GainControlTargetEffect;
-import mage.constants.SubType;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.Duration;
+import mage.constants.SubType;
 import mage.filter.common.FilterLandPermanent;
 import mage.filter.predicate.permanent.DefendingPlayerControlsPredicate;
 import mage.target.TargetPermanent;
+import mage.watchers.common.LostControlWatcher;
 
 /**
  *
@@ -45,6 +45,7 @@ public final class OrcishSquatters extends CardImpl {
         ), true);
         ability.addEffect(new AssignNoCombatDamageSourceEffect(Duration.EndOfTurn, true));
         ability.addTarget(new TargetPermanent(filter));
+        ability.addWatcher(new LostControlWatcher());
         this.addAbility(ability);
     }
 
diff --git a/Mage.Sets/src/mage/cards/q/QuicksmithRebel.java b/Mage.Sets/src/mage/cards/q/QuicksmithRebel.java
index 87b0caca71..38c7600aa8 100644
--- a/Mage.Sets/src/mage/cards/q/QuicksmithRebel.java
+++ b/Mage.Sets/src/mage/cards/q/QuicksmithRebel.java
@@ -1,4 +1,3 @@
-
 package mage.cards.q;
 
 import java.util.UUID;
@@ -14,12 +13,13 @@ import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.SubType;
 import mage.constants.Duration;
+import mage.constants.SubType;
 import mage.constants.Zone;
 import mage.filter.common.FilterControlledArtifactPermanent;
 import mage.target.TargetPermanent;
 import mage.target.common.TargetAnyTarget;
+import mage.watchers.common.LostControlWatcher;
 
 /**
  *
@@ -44,6 +44,7 @@ public final class QuicksmithRebel extends CardImpl {
                 "target artifact you control gains \"{T}: This artifact deals 2 damage to any target\" for as long as you control {this}");
         Ability ability = new EntersBattlefieldTriggeredAbility(effect, false);
         ability.addTarget(new TargetPermanent(new FilterControlledArtifactPermanent()));
+        ability.addWatcher(new LostControlWatcher());
         this.addAbility(ability);
     }
 
diff --git a/Mage.Sets/src/mage/cards/q/QuicksmithSpy.java b/Mage.Sets/src/mage/cards/q/QuicksmithSpy.java
index 2b69c073e1..d52dfb6b07 100644
--- a/Mage.Sets/src/mage/cards/q/QuicksmithSpy.java
+++ b/Mage.Sets/src/mage/cards/q/QuicksmithSpy.java
@@ -1,4 +1,3 @@
-
 package mage.cards.q;
 
 import java.util.UUID;
@@ -14,11 +13,12 @@ import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.SubType;
 import mage.constants.Duration;
+import mage.constants.SubType;
 import mage.constants.Zone;
 import mage.filter.common.FilterControlledArtifactPermanent;
 import mage.target.TargetPermanent;
+import mage.watchers.common.LostControlWatcher;
 
 /**
  *
@@ -42,6 +42,7 @@ public final class QuicksmithSpy extends CardImpl {
                 "target artifact you control gains \"{T}: Draw a card\" for as long as you control {this}");
         Ability ability = new EntersBattlefieldTriggeredAbility(effect, false);
         ability.addTarget(new TargetPermanent(new FilterControlledArtifactPermanent()));
+        ability.addWatcher(new LostControlWatcher());
         this.addAbility(ability);
     }
 
diff --git a/Mage.Sets/src/mage/cards/r/RoilElemental.java b/Mage.Sets/src/mage/cards/r/RoilElemental.java
index 4a2b3c44c5..a4e01d142e 100644
--- a/Mage.Sets/src/mage/cards/r/RoilElemental.java
+++ b/Mage.Sets/src/mage/cards/r/RoilElemental.java
@@ -1,4 +1,3 @@
-
 package mage.cards.r;
 
 import java.util.UUID;
@@ -13,6 +12,7 @@ import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.*;
 import mage.target.common.TargetCreaturePermanent;
+import mage.watchers.common.LostControlWatcher;
 
 /**
  *
@@ -36,6 +36,7 @@ public final class RoilElemental extends CardImpl {
         ConditionalContinuousEffect effect = new ConditionalContinuousEffect(new GainControlTargetEffect(Duration.Custom), new SourceOnBattlefieldControlUnchangedCondition(), rule);
         Ability ability = new LandfallAbility(Zone.BATTLEFIELD, effect, true);
         ability.addTarget(new TargetCreaturePermanent());
+        ability.addWatcher(new LostControlWatcher());
         this.addAbility(ability);
     }
 
diff --git a/Mage.Sets/src/mage/cards/t/TheWretched.java b/Mage.Sets/src/mage/cards/t/TheWretched.java
index 61cf1d51ce..1adfa0bd43 100644
--- a/Mage.Sets/src/mage/cards/t/TheWretched.java
+++ b/Mage.Sets/src/mage/cards/t/TheWretched.java
@@ -1,6 +1,6 @@
-
 package mage.cards.t;
 
+import java.util.Objects;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
@@ -13,44 +13,51 @@ import mage.abilities.effects.common.continuous.GainControlTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.SubType;
 import mage.constants.Duration;
 import mage.constants.Outcome;
+import mage.constants.SubType;
 import mage.game.Game;
 import mage.game.combat.CombatGroup;
 import mage.game.permanent.Permanent;
 import mage.target.targetpointer.FixedTarget;
 import mage.watchers.common.BlockedAttackerWatcher;
+import mage.watchers.common.LostControlWatcher;
 
 /**
  *
  * @author jeffwadsworth
  *
-5/1/2009 	The ability grants you control of all creatures that are blocking it as the ability resolves. This will include
-*               any creatures that were put onto the battlefield blocking it.
-5/1/2009 	Any blocking creatures that regenerated during combat will have been removed from combat. Since such creatures
-*               are no longer in combat, they cannot be blocking The Wretched, which means you won't be able to gain control of them.
-5/1/2009 	If The Wretched itself regenerated during combat, then it will have been removed from combat. Since it is no longer
-*               in combat, there cannot be any creatures blocking it, which means you won't be able to gain control of any creatures.
-10/1/2009 	The Wretched's ability triggers only if it's still on the battlefield when the end of combat step begins (after the
-*               combat damage step). For example, if it's blocked by a 7/7 creature and is destroyed, its ability won't trigger at all.
-10/1/2009 	If The Wretched leaves the battlefield, you no longer control it, so the duration of its control-change effect ends.
-10/1/2009 	If you lose control of The Wretched before its ability resolves, you won't gain control of the creatures blocking it at all.
-10/1/2009 	Once the ability resolves, it doesn't care whether the permanents you gained control of remain creatures, only that
-*               they remain on the battlefield.
+ * 5/1/2009 The ability grants you control of all creatures that are blocking it
+ * as the ability resolves. This will include any creatures that were put onto
+ * the battlefield blocking it. 5/1/2009 Any blocking creatures that regenerated
+ * during combat will have been removed from combat. Since such creatures are no
+ * longer in combat, they cannot be blocking The Wretched, which means you won't
+ * be able to gain control of them. 5/1/2009 If The Wretched itself regenerated
+ * during combat, then it will have been removed from combat. Since it is no
+ * longer in combat, there cannot be any creatures blocking it, which means you
+ * won't be able to gain control of any creatures. 10/1/2009 The Wretched's
+ * ability triggers only if it's still on the battlefield when the end of combat
+ * step begins (after the combat damage step). For example, if it's blocked by a
+ * 7/7 creature and is destroyed, its ability won't trigger at all. 10/1/2009 If
+ * The Wretched leaves the battlefield, you no longer control it, so the
+ * duration of its control-change effect ends. 10/1/2009 If you lose control of
+ * The Wretched before its ability resolves, you won't gain control of the
+ * creatures blocking it at all. 10/1/2009 Once the ability resolves, it doesn't
+ * care whether the permanents you gained control of remain creatures, only that
+ * they remain on the battlefield.
  */
-
 public final class TheWretched extends CardImpl {
 
     public TheWretched(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}{B}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}");
         this.subtype.add(SubType.DEMON);
         this.power = new MageInt(2);
         this.toughness = new MageInt(5);
 
         // At end of combat, gain control of all creatures blocking The Wretched for as long as you control The Wretched.
-        this.addAbility(new EndOfCombatTriggeredAbility(new TheWretchedEffect(), false), new BlockedAttackerWatcher());
-
+        Ability ability = new EndOfCombatTriggeredAbility(new TheWretchedEffect(), false);
+        this.addAbility(ability, new BlockedAttackerWatcher());
+        ability.addWatcher(new LostControlWatcher());
     }
 
     public TheWretched(final TheWretched card) {
@@ -83,16 +90,20 @@ class TheWretchedEffect extends OneShotEffect {
         if (theWretched.isRemovedFromCombat() || !theWretched.isAttacking()) {
             return false;
         }
-        if (!new SourceOnBattlefieldControlUnchangedCondition().apply(game, source)) {
+        // Check if control of source has changed since ability triggered????? (does it work is it neccessary???)
+        Permanent permanent = game.getBattlefield().getPermanent(source.getSourceId());
+        if (permanent != null && !Objects.equals(permanent.getControllerId(), source.getControllerId())) {
             return false;
         }
 
-        for (CombatGroup combatGroup :game.getCombat().getGroups()) {
+        for (CombatGroup combatGroup : game.getCombat().getGroups()) {
             if (combatGroup.getAttackers().contains(source.getSourceId())) {
-                for(UUID creatureId: combatGroup.getBlockers()) {
+                for (UUID creatureId : combatGroup.getBlockers()) {
                     Permanent blocker = game.getPermanent(creatureId);
                     if (blocker != null && blocker.getBlocking() > 0) {
-                        ContinuousEffect effect = new ConditionalContinuousEffect(new GainControlTargetEffect(Duration.Custom, source.getControllerId()), new SourceOnBattlefieldControlUnchangedCondition(), "");
+                        ContinuousEffect effect = new ConditionalContinuousEffect(
+                                new GainControlTargetEffect(Duration.Custom, source.getControllerId()),
+                                new SourceOnBattlefieldControlUnchangedCondition(), "");
                         effect.setTargetPointer(new FixedTarget(blocker.getId()));
                         game.addEffect(effect, source);
 
diff --git a/Mage.Sets/src/mage/cards/t/ThrullChampion.java b/Mage.Sets/src/mage/cards/t/ThrullChampion.java
index e5bed4d947..a200e4607c 100644
--- a/Mage.Sets/src/mage/cards/t/ThrullChampion.java
+++ b/Mage.Sets/src/mage/cards/t/ThrullChampion.java
@@ -1,4 +1,3 @@
-
 package mage.cards.t;
 
 import java.util.UUID;
@@ -19,6 +18,7 @@ import mage.constants.SubType;
 import mage.constants.Zone;
 import mage.filter.common.FilterCreaturePermanent;
 import mage.target.TargetPermanent;
+import mage.watchers.common.LostControlWatcher;
 
 /**
  *
@@ -33,7 +33,7 @@ public final class ThrullChampion extends CardImpl {
     }
 
     public ThrullChampion(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{B}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}");
         this.subtype.add(SubType.THRULL);
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
@@ -47,6 +47,7 @@ public final class ThrullChampion extends CardImpl {
                 "Gain control of target Thrull for as long as you control {this}");
         Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, ThrullChampionGainControlEffect, new TapSourceCost());
         ability.addTarget(new TargetPermanent(filter));
+        ability.addWatcher(new LostControlWatcher());
         this.addAbility(ability);
     }
 
diff --git a/Mage.Sets/src/mage/cards/w/Willbreaker.java b/Mage.Sets/src/mage/cards/w/Willbreaker.java
index 0c1faff0ba..3a34901480 100644
--- a/Mage.Sets/src/mage/cards/w/Willbreaker.java
+++ b/Mage.Sets/src/mage/cards/w/Willbreaker.java
@@ -1,4 +1,3 @@
-
 package mage.cards.w;
 
 import java.util.UUID;
@@ -11,8 +10,8 @@ import mage.abilities.effects.common.continuous.GainControlTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.SubType;
 import mage.constants.Duration;
+import mage.constants.SubType;
 import mage.constants.Zone;
 import mage.game.Game;
 import mage.game.events.GameEvent;
@@ -20,6 +19,7 @@ import mage.game.events.GameEvent.EventType;
 import mage.game.permanent.Permanent;
 import mage.players.Player;
 import mage.target.targetpointer.FixedTarget;
+import mage.watchers.common.LostControlWatcher;
 
 /**
  *
@@ -28,7 +28,7 @@ import mage.target.targetpointer.FixedTarget;
 public final class Willbreaker extends CardImpl {
 
     public Willbreaker(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{U}{U}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{U}");
         this.subtype.add(SubType.HUMAN);
         this.subtype.add(SubType.WIZARD);
         this.power = new MageInt(2);
@@ -37,7 +37,7 @@ public final class Willbreaker extends CardImpl {
         // Whenever a creature an opponent controls becomes the target of a spell or ability you control, gain control of that creature for as long as you control Willbreaker.
         ConditionalContinuousEffect effect = new ConditionalContinuousEffect(new GainControlTargetEffect(Duration.Custom), new SourceOnBattlefieldControlUnchangedCondition(), null);
         effect.setText("gain control of that creature for as long as you control {this}");
-        this.addAbility(new WillbreakerTriggeredAbility(effect));
+        this.addAbility(new WillbreakerTriggeredAbility(effect), new LostControlWatcher());
     }
 
     public Willbreaker(final Willbreaker card) {
diff --git a/Mage.Sets/src/mage/cards/w/WillowSatyr.java b/Mage.Sets/src/mage/cards/w/WillowSatyr.java
index 55c3eadd1d..fdc4d91a6b 100644
--- a/Mage.Sets/src/mage/cards/w/WillowSatyr.java
+++ b/Mage.Sets/src/mage/cards/w/WillowSatyr.java
@@ -1,4 +1,3 @@
-
 package mage.cards.w;
 
 import java.util.UUID;
@@ -15,19 +14,20 @@ import mage.abilities.effects.common.continuous.GainControlTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.SubType;
 import mage.constants.Duration;
+import mage.constants.SubType;
 import mage.constants.SuperType;
 import mage.constants.Zone;
 import mage.filter.common.FilterCreaturePermanent;
 import mage.target.common.TargetCreaturePermanent;
+import mage.watchers.common.LostControlWatcher;
 
 /**
  *
  * @author fireshoes
  */
 public final class WillowSatyr extends CardImpl {
-    
+
     private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("legendary creature");
 
     static {
@@ -35,7 +35,7 @@ public final class WillowSatyr extends CardImpl {
     }
 
     public WillowSatyr(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{G}{G}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{G}");
         this.subtype.add(SubType.SATYR);
         this.power = new MageInt(1);
         this.toughness = new MageInt(1);
@@ -44,10 +44,11 @@ public final class WillowSatyr extends CardImpl {
         this.addAbility(new SkipUntapOptionalAbility());
         // {tap}: Gain control of target legendary creature for as long as you control Willow Satyr and Willow Satyr remains tapped.
         ConditionalContinuousEffect effect = new ConditionalContinuousEffect(
-            new GainControlTargetEffect(Duration.Custom), new CompoundCondition(SourceTappedCondition.instance, new SourceOnBattlefieldControlUnchangedCondition()),
-            "Gain control of target legendary creature for as long as you control {this} and {this} remains tapped");
+                new GainControlTargetEffect(Duration.Custom), new CompoundCondition(SourceTappedCondition.instance, new SourceOnBattlefieldControlUnchangedCondition()),
+                "Gain control of target legendary creature for as long as you control {this} and {this} remains tapped");
         Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new TapSourceCost());
         ability.addTarget(new TargetCreaturePermanent(filter));
+        ability.addWatcher(new LostControlWatcher());
         this.addAbility(ability);
     }
 
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/GoadTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/GoadTest.java
index b644006b87..02ef1d7332 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/GoadTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/GoadTest.java
@@ -1,81 +1,120 @@
-/*
- * To change this license header, choose License Headers in Project Properties.
- * To change this template file, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.mage.test.cards.abilities.keywords;
-
-import java.io.FileNotFoundException;
-import mage.constants.MultiplayerAttackOption;
-import mage.constants.PhaseStep;
-import mage.constants.RangeOfInfluence;
-import mage.constants.Zone;
-import mage.game.FreeForAll;
-import mage.game.Game;
-import mage.game.GameException;
-import mage.game.mulligan.MulliganType;
-import org.junit.Test;
-import org.mage.test.serverside.base.CardTestMultiPlayerBase;
-
-/**
- *
- * @author LevelX2
- */
-public class GoadTest extends CardTestMultiPlayerBase {
-
-    @Override
-    protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
-        Game game = new FreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, MulliganType.GAME_DEFAULT.getMulligan(0), 40);
-        // Player order: A -> D -> C -> B
-        playerA = createPlayer(game, playerA, "PlayerA");
-        playerB = createPlayer(game, playerB, "PlayerB");
-        playerC = createPlayer(game, playerC, "PlayerC");
-        playerD = createPlayer(game, playerD, "PlayerD");
-        return game;
-    }
-
-    /**
-     * In a game of commander, my opponent gained control of Marisi, Breaker of
-     * Coils (until end of turn) and did combat damage to another player. This
-     * caused the creatures damaged by Marisi's controller to be goaded.
-     * However, when the goaded creatures went to attack, they could not attack
-     * me but could attack the (former) controller of Marisi.
-     */
-    @Test
-    public void goadWithNotOwnedCreatureTest() {
-        // Your opponents can't cast spells during combat.
-        // Whenever a creature you control deals combat damage to a player, goad each creature that player controls
-        // (Until your next turn, that creature attacks each combat if able and attacks a player other than you if able.)
-        addCard(Zone.BATTLEFIELD, playerA, "Marisi, Breaker of the Coil", 1); // Creature 5/4
-
-        // Untap target creature an opponent controls and gain control of it until end of turn. 
-        // That creature gains haste until end of turn. 
-        // When you lose control of the creature, tap it.
-        addCard(Zone.HAND, playerD, "Ray of Command"); // Instant {3}{U}
-        addCard(Zone.BATTLEFIELD, playerD, "Island", 4);
-        
-        addCard(Zone.BATTLEFIELD, playerC, "Silvercoat Lion", 3); // Creature 2/2
-        
-        castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerD, "Ray of Command", "Marisi, Breaker of the Coil");
-        
-        attack(2, playerD, "Marisi, Breaker of the Coil", playerC);
-        
-        attack(3, playerC, "Silvercoat Lion", playerA);
-        attack(3, playerC, "Silvercoat Lion", playerB);
-        attack(3, playerC, "Silvercoat Lion", playerD);
-        
-        setStopAt(4, PhaseStep.BEGIN_COMBAT);
-        execute();
-
-        
-        assertGraveyardCount(playerD, "Ray of Command", 1);
-        assertPermanentCount(playerA, "Marisi, Breaker of the Coil", 1);
-                
-        assertLife(playerC, 35);
-
-        assertLife(playerB, 38);        
-        assertLife(playerA, 38);
-        assertLife(playerD, 38);                
-    }
-
-}
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.mage.test.cards.abilities.keywords;
+
+import java.io.FileNotFoundException;
+import mage.constants.MultiplayerAttackOption;
+import mage.constants.PhaseStep;
+import mage.constants.RangeOfInfluence;
+import mage.constants.Zone;
+import mage.game.FreeForAll;
+import mage.game.Game;
+import mage.game.GameException;
+import mage.game.mulligan.MulliganType;
+import mage.game.permanent.Permanent;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestMultiPlayerBase;
+
+/**
+ *
+ * @author LevelX2
+ */
+public class GoadTest extends CardTestMultiPlayerBase {
+
+    @Override
+    protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
+        Game game = new FreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, MulliganType.GAME_DEFAULT.getMulligan(0), 40);
+        // Player order: A -> D -> C -> B
+        playerA = createPlayer(game, playerA, "PlayerA");
+        playerB = createPlayer(game, playerB, "PlayerB");
+        playerC = createPlayer(game, playerC, "PlayerC");
+        playerD = createPlayer(game, playerD, "PlayerD");
+        return game;
+    }
+
+    @Test
+    public void goadWithOwnedCreatureTest() {
+        // Your opponents can't cast spells during combat.
+        // Whenever a creature you control deals combat damage to a player, goad each creature that player controls
+        // (Until your next turn, that creature attacks each combat if able and attacks a player other than you if able.)
+        addCard(Zone.BATTLEFIELD, playerD, "Marisi, Breaker of the Coil", 1); // Creature 5/4
+
+        addCard(Zone.BATTLEFIELD, playerC, "Abbey Griffin", 3); // Creature 2/2
+
+        attack(2, playerD, "Marisi, Breaker of the Coil", playerC);
+
+        setStopAt(3, PhaseStep.BEGIN_COMBAT);
+
+        setStrictChooseMode(true);
+        execute();
+        assertAllCommandsUsed();
+
+        Permanent griffinPermanent = getPermanent("Abbey Griffin");
+
+        Assert.assertFalse("Griffin can attack playerD but should not be able",
+                griffinPermanent.canAttack(playerD.getId(), currentGame));
+        Assert.assertTrue("Griffin can't attack playerA but should be able",
+                griffinPermanent.canAttack(playerA.getId(), currentGame));
+        Assert.assertTrue("Griffin can't attack playerB but should be able",
+                griffinPermanent.canAttack(playerB.getId(), currentGame));
+
+        assertLife(playerC, 35);
+        assertLife(playerD, 40); // player D can not be attacked from C because the creatures are goaded
+        assertLife(playerA, 40);
+        assertLife(playerB, 40);
+
+    }
+
+    /**
+     * In a game of commander, my opponent gained control of Marisi, Breaker of
+     * Coils (until end of turn) and did combat damage to another player. This
+     * caused the creatures damaged by Marisi's controller to be goaded.
+     * However, when the goaded creatures went to attack, they could not attack
+     * me but could attack the (former) controller of Marisi.
+     */
+    @Test
+    public void goadWithNotOwnedCreatureTest() {
+        // Your opponents can't cast spells during combat.
+        // Whenever a creature you control deals combat damage to a player, goad each creature that player controls
+        // (Until your next turn, that creature attacks each combat if able and attacks a player other than you if able.)
+        addCard(Zone.BATTLEFIELD, playerA, "Marisi, Breaker of the Coil", 1); // Creature 5/4
+
+        // Untap target creature an opponent controls and gain control of it until end of turn.
+        // That creature gains haste until end of turn.
+        // When you lose control of the creature, tap it.
+        addCard(Zone.HAND, playerD, "Ray of Command"); // Instant {3}{U}
+        addCard(Zone.BATTLEFIELD, playerD, "Island", 4);
+
+        addCard(Zone.BATTLEFIELD, playerC, "Silvercoat Lion", 1); // Creature 2/2
+
+        castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerD, "Ray of Command", "Marisi, Breaker of the Coil");
+
+        attack(2, playerD, "Marisi, Breaker of the Coil", playerC);
+
+        setStopAt(3, PhaseStep.BEGIN_COMBAT);
+        setStrictChooseMode(true);
+        execute();
+
+        assertAllCommandsUsed();
+
+        assertGraveyardCount(playerD, "Ray of Command", 1);
+        assertPermanentCount(playerA, "Marisi, Breaker of the Coil", 1);
+
+        Permanent lion = getPermanent("Silvercoat Lion", playerC);
+
+        Assert.assertFalse("Silvercoat lion shouldn't be able to attack player D but can", lion.canAttack(playerD.getId(), currentGame));
+        Assert.assertTrue("Silvercoat lion should be able to attack player A but can't", lion.canAttack(playerA.getId(), currentGame));
+        Assert.assertTrue("Silvercoat lion should be able to attack player B but can't", lion.canAttack(playerB.getId(), currentGame));
+
+        assertLife(playerD, 40);
+        assertLife(playerC, 35);
+        assertLife(playerA, 40);
+        assertLife(playerB, 40);
+
+    }
+
+}
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/conditional/TheWretchedTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/conditional/TheWretchedTest.java
index 7a4f461f51..b18bdaeacc 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/conditional/TheWretchedTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/conditional/TheWretchedTest.java
@@ -22,11 +22,11 @@ public class TheWretchedTest extends CardTestPlayerBase {
         addCard(Zone.BATTLEFIELD, playerA, "The Wretched");
         addCard(Zone.BATTLEFIELD, playerB, "Wall of Pine Needles"); // a 3/3 with regeneration
         addCard(Zone.BATTLEFIELD, playerB, "Living Wall"); // 0/6 Wall with regeneration
-        
+
         attack(3, playerA, "The Wretched");
         block(3, playerB, "Wall of Pine Needles", "The Wretched");
         block(3, playerB, "Living Wall", "The Wretched");
-        setStopAt(3, PhaseStep.POSTCOMBAT_MAIN);
+        setStopAt(3, PhaseStep.END_TURN);
         execute();
 
         assertPermanentCount(playerA, "The Wretched", 1);
@@ -39,17 +39,17 @@ public class TheWretchedTest extends CardTestPlayerBase {
 
         addCard(Zone.BATTLEFIELD, playerA, "The Wretched");
         addCard(Zone.BATTLEFIELD, playerA, "Bad Moon"); // +1/+1 for black creatures
-        
+
         addCard(Zone.BATTLEFIELD, playerB, "Forest"); // a 3/3 with regeneration
         addCard(Zone.BATTLEFIELD, playerB, "Wall of Pine Needles"); // a 3/3 with regeneration
         addCard(Zone.BATTLEFIELD, playerB, "Living Wall"); // 0/6 Wall with regeneration
-        
+
         // The Wretched
         // Creature — Demon - Demon  2/5
         attack(3, playerA, "The Wretched");
         block(3, playerB, "Wall of Pine Needles", "The Wretched");
         block(3, playerB, "Living Wall", "The Wretched");
-        
+
         activateAbility(3, PhaseStep.DECLARE_BLOCKERS, playerB, "{G}: Regenerate {this}."); // Wall of Pine Needles
 
         setStopAt(3, PhaseStep.POSTCOMBAT_MAIN);
@@ -57,53 +57,52 @@ public class TheWretchedTest extends CardTestPlayerBase {
 
         assertPermanentCount(playerA, "The Wretched", 1);
         assertPermanentCount(playerA, "Living Wall", 1);
-        
+
         assertPermanentCount(playerB, "Wall of Pine Needles", 1);
-        
 
     }
-    
+
     @Test
     public void testLoseControlOfTheWretched() {
-
+        // At end of combat, gain control of all creatures blocking The Wretched for as long as you control The Wretched.
         addCard(Zone.BATTLEFIELD, playerA, "The Wretched");
-        
+
         addCard(Zone.BATTLEFIELD, playerB, "Wall of Pine Needles"); // a 3/3 with regeneration
         addCard(Zone.BATTLEFIELD, playerB, "Living Wall"); // 0/6 Wall with regeneration
         addCard(Zone.BATTLEFIELD, playerB, "Island", 4);
         addCard(Zone.HAND, playerB, "Control Magic");
-        
+
         attack(3, playerA, "The Wretched");
         block(3, playerB, "Wall of Pine Needles", "The Wretched");
         block(3, playerB, "Living Wall", "The Wretched");
-        
+
         castSpell(4, PhaseStep.POSTCOMBAT_MAIN, playerB, "Control Magic", "The Wretched");
 
         setStopAt(4, PhaseStep.END_TURN);
         execute();
 
         assertPermanentCount(playerB, "The Wretched", 1);
+        assertPermanentCount(playerA, "Wall of Pine Needles", 0);
         assertPermanentCount(playerB, "Wall of Pine Needles", 1);
         assertPermanentCount(playerB, "Living Wall", 1);
     }
-    
+
     @Test
     public void testRegenTheWretchedThusRemovingFromCombat() {
 
         addCard(Zone.BATTLEFIELD, playerA, "The Wretched");
-        addCard(Zone.HAND, playerA, "Regenerate"); 
-        addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); 
-        
+        addCard(Zone.HAND, playerA, "Regenerate");
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 2);
+
         addCard(Zone.BATTLEFIELD, playerB, "Wall of Pine Needles"); // a 3/3 with regeneration
-        addCard(Zone.BATTLEFIELD, playerB, "Wall of Spears"); // 3/2 
-        
-        
+        addCard(Zone.BATTLEFIELD, playerB, "Wall of Spears"); // 3/2
+
         attack(3, playerA, "The Wretched");
         block(3, playerB, "Wall of Pine Needles", "The Wretched");
         block(3, playerB, "Wall of Spears", "The Wretched");
-        
+
         castSpell(3, PhaseStep.DECLARE_BLOCKERS, playerA, "Regenerate", "The Wretched");
-        
+
         setStopAt(3, PhaseStep.POSTCOMBAT_MAIN);
         execute();
 
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/LazavDimirMastermindTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/LazavDimirMastermindTest.java
index c5b357266a..c2bf7afa6e 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/LazavDimirMastermindTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/LazavDimirMastermindTest.java
@@ -1,9 +1,9 @@
 package org.mage.test.cards.copy;
 
 import mage.abilities.keyword.DeathtouchAbility;
-import mage.constants.SubType;
 import mage.abilities.keyword.FlyingAbility;
 import mage.constants.PhaseStep;
+import mage.constants.SubType;
 import mage.constants.Zone;
 import mage.game.permanent.Permanent;
 import org.junit.Assert;
@@ -14,12 +14,11 @@ import org.mage.test.serverside.base.CardTestPlayerBase;
  *
  * Lazav, Dimir Mastermind
  *
- * Legendary Creature — Shapeshifter 3/3, UUBB
- * Hexproof
- * Whenever a creature card is put into an opponent's graveyard from anywhere, you may have
- * Lazav, Dimir Mastermind become a copy of that card except its name is still
- * Lazav, Dimir Mastermind, it's legendary in addition to its other types, and
- * it gains hexproof and this ability.
+ * Legendary Creature — Shapeshifter 3/3, UUBB Hexproof Whenever a creature card
+ * is put into an opponent's graveyard from anywhere, you may have Lazav, Dimir
+ * Mastermind become a copy of that card except its name is still Lazav, Dimir
+ * Mastermind, it's legendary in addition to its other types, and it gains
+ * hexproof and this ability.
  *
  * @author LevelX2
  */
@@ -32,25 +31,26 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase {
     public void testCopySimpleCreature() {
         addCard(Zone.BATTLEFIELD, playerA, "Lazav, Dimir Mastermind", 1);
         // Codex Shredder - Artifact
-        // {T}: Target player mills a card.
+        // {T}: Target player puts the top card of their library into their graveyard.
         // {5}, {T}, Sacrifice Codex Shredder: Return target card from your graveyard to your hand.
         addCard(Zone.BATTLEFIELD, playerA, "Codex Shredder", 1);
 
         // Flying 3/2
-        addCard(Zone.LIBRARY, playerB, "Assault Griffin",5);
+        addCard(Zone.LIBRARY, playerB, "Assault Griffin", 5);
         skipInitShuffling();
 
-        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills a card.", playerB);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player puts the top card", playerB);
 
         setStopAt(1, PhaseStep.END_TURN);
         execute();
+        assertAllCommandsUsed();
 
         assertPermanentCount(playerA, "Lazav, Dimir Mastermind", 1);
         assertPowerToughness(playerA, "Lazav, Dimir Mastermind", 3, 2);
 
         Permanent lazav = getPermanent("Lazav, Dimir Mastermind", playerA.getId());
         Assert.assertTrue(lazav.getSubtype(currentGame).contains(SubType.GRIFFIN));
-        Assert.assertTrue("Lazav, Dimir Mastermind must have flying",lazav.getAbilities().contains(FlyingAbility.getInstance()));
+        Assert.assertTrue("Lazav, Dimir Mastermind must have flying", lazav.getAbilities().contains(FlyingAbility.getInstance()));
     }
 
     /**
@@ -64,10 +64,10 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase {
 
         // Whenever another nontoken creature dies, you may put a 1/1 black Rat creature token onto the battlefield.
         // Rats you control have deathtouch.
-        addCard(Zone.LIBRARY, playerB, "Ogre Slumlord",5);
+        addCard(Zone.LIBRARY, playerB, "Ogre Slumlord", 5);
         skipInitShuffling();
 
-        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills a card.", playerB);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player puts the top card", playerB);
 
         setStopAt(1, PhaseStep.END_TURN);
         execute();
@@ -86,11 +86,10 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase {
     /**
      * Tests copy Nightveil Specter
      *
-     * Nightveil Specter
-     * Creature — Specter 2/3, {U/B}{U/B}{U/B}
-     * Flying
-     * Whenever Nightveil Specter deals combat damage to a player, that player exiles the top card of their library.
-     * You may play cards exiled with Nightveil Specter.
+     * Nightveil Specter Creature — Specter 2/3, {U/B}{U/B}{U/B} Flying Whenever
+     * Nightveil Specter deals combat damage to a player, that player exiles the
+     * top card of their library. You may play cards exiled with Nightveil
+     * Specter.
      *
      */
     @Test
@@ -99,11 +98,11 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase {
         addCard(Zone.BATTLEFIELD, playerA, "Lazav, Dimir Mastermind", 1);
         addCard(Zone.BATTLEFIELD, playerA, "Codex Shredder", 1);
 
-        addCard(Zone.LIBRARY, playerB, "Silvercoat Lion",2);
-        addCard(Zone.LIBRARY, playerB, "Nightveil Specter",1);
+        addCard(Zone.LIBRARY, playerB, "Silvercoat Lion", 2);
+        addCard(Zone.LIBRARY, playerB, "Nightveil Specter", 1);
         skipInitShuffling();
 
-        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills a card.", playerB);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player puts the top card", playerB);
 
         attack(3, playerA, "Lazav, Dimir Mastermind");
 
@@ -130,15 +129,15 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase {
         addCard(Zone.BATTLEFIELD, playerA, "Lazav, Dimir Mastermind", 1);
         addCard(Zone.BATTLEFIELD, playerA, "Codex Shredder", 1);
 
-        addCard(Zone.LIBRARY, playerB, "Silvercoat Lion",2);
-        addCard(Zone.LIBRARY, playerB, "Nightveil Specter",1);
+        addCard(Zone.LIBRARY, playerB, "Silvercoat Lion", 2);
+        addCard(Zone.LIBRARY, playerB, "Nightveil Specter", 1);
         skipInitShuffling();
 
         // Lazav becomes a Nightveil Specter
-        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills a card.", playerB);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player puts the top card", playerB);
 
         // Lazav becomes a Silvercoat Lion
-        activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills a card.", playerB);
+        activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player puts the top card", playerB);
 
         setStopAt(3, PhaseStep.END_TURN);
         execute();
@@ -151,52 +150,52 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase {
         Assert.assertTrue(lazav.isLegendary());
 
     }
+
     /**
      * Tests old copy is discarded after reanmiation of Lazav
      */
     @Test
     public void testCopyAfterReanimation() {
-        addCard(Zone.BATTLEFIELD, playerA ,"Swamp");        
+        addCard(Zone.BATTLEFIELD, playerA, "Swamp");
         // Put target creature card from a graveyard onto the battlefield under your control. You lose life equal to its converted mana cost.
-        addCard(Zone.HAND, playerA ,"Reanimate");        
+        addCard(Zone.HAND, playerA, "Reanimate");
         addCard(Zone.BATTLEFIELD, playerA, "Lazav, Dimir Mastermind", 1);
         // Codex Shredder - Artifact
-        // {T}: Target player mills a card.
+        // {T}: Target player puts the top card of their library into their graveyard.
         // {5}, {T}, Sacrifice Codex Shredder: Return target card from your graveyard to your hand.
         addCard(Zone.BATTLEFIELD, playerA, "Codex Shredder", 1);
 
-        addCard(Zone.BATTLEFIELD,    playerB ,"Swamp", 3);
+        addCard(Zone.BATTLEFIELD, playerB, "Swamp", 3);
         // Flying 3/2
-        addCard(Zone.LIBRARY, playerB, "Assault Griffin",1);
+        addCard(Zone.LIBRARY, playerB, "Assault Griffin", 1);
         // Target opponent sacrifices a creature. You gain life equal to that creature's toughness.
-        addCard(Zone.HAND, playerB ,"Tribute to Hunger");
-        
+        addCard(Zone.HAND, playerB, "Tribute to Hunger");
+
         skipInitShuffling();
 
-        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills a card.", playerB);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player puts the top card", playerB);
 
         castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Tribute to Hunger");
-        
+
         castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Reanimate", "Lazav, Dimir Mastermind");
-        
+
         setStopAt(1, PhaseStep.END_TURN);
         execute();
 
         assertGraveyardCount(playerB, "Tribute to Hunger", 1);
         assertGraveyardCount(playerA, "Reanimate", 1);
-        
+
         assertLife(playerA, 16); // -4 from Reanmiate
-        assertLife(playerB, 22); // +3 from Tribute to Hunger because Lazav is 3/2 
-        
+        assertLife(playerB, 22); // +3 from Tribute to Hunger because Lazav is 3/2
+
         assertPermanentCount(playerA, "Lazav, Dimir Mastermind", 1);
         assertPowerToughness(playerA, "Lazav, Dimir Mastermind", 3, 3);
         Permanent lazav = getPermanent("Lazav, Dimir Mastermind", playerA.getId());
         Assert.assertFalse(lazav.getSubtype(currentGame).contains(SubType.GRIFFIN)); // no Griffin type
-        Assert.assertFalse("Lazav, Dimir Mastermind must have flying",lazav.getAbilities().contains(FlyingAbility.getInstance()));
-        
-        
+        Assert.assertFalse("Lazav, Dimir Mastermind must have flying", lazav.getAbilities().contains(FlyingAbility.getInstance()));
+
     }
-    
+
     /**
      * Tests if Lazav remains a copy of the creature after it is exiled
      */
@@ -204,20 +203,20 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase {
     public void testCopyCreatureExiled() {
         addCard(Zone.BATTLEFIELD, playerA, "Lazav, Dimir Mastermind", 1);
         // Codex Shredder - Artifact
-        // {T}: Target player mills a card.
+        // {T}: Target player puts the top card of their library into their graveyard.
         // {5}, {T}, Sacrifice Codex Shredder: Return target card from your graveyard to your hand.
         addCard(Zone.BATTLEFIELD, playerA, "Codex Shredder", 1);
-        
+
         addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
-        
+
         addCard(Zone.HAND, playerA, "Rest in Peace", 1);
 
         // Flying 3/2
-        addCard(Zone.LIBRARY, playerB, "Assault Griffin",5);
+        addCard(Zone.LIBRARY, playerB, "Assault Griffin", 5);
         skipInitShuffling();
 
-        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills a card.", playerB);
-        
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player puts the top card", playerB);
+
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Rest in Peace");
 
         setStopAt(1, PhaseStep.END_TURN);
@@ -228,6 +227,6 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase {
 
         Permanent lazav = getPermanent("Lazav, Dimir Mastermind", playerA.getId());
         Assert.assertTrue(lazav.getSubtype(currentGame).contains(SubType.GRIFFIN));
-        Assert.assertTrue("Lazav, Dimir Mastermind must have flying",lazav.getAbilities().contains(FlyingAbility.getInstance()));
+        Assert.assertTrue("Lazav, Dimir Mastermind must have flying", lazav.getAbilities().contains(FlyingAbility.getInstance()));
     }
 }
diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
index a4627fe0f8..f15f7c1363 100644
--- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
+++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
@@ -1,5 +1,10 @@
 package org.mage.test.player;
 
+import java.io.Serializable;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 import mage.MageItem;
 import mage.MageObject;
 import mage.MageObjectReference;
@@ -57,13 +62,6 @@ import mage.util.CardUtil;
 import org.apache.log4j.Logger;
 import org.junit.Assert;
 import org.junit.Ignore;
-
-import java.io.Serializable;
-import java.util.*;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.stream.Collectors;
-
 import static org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl.*;
 
 /**
@@ -193,7 +191,7 @@ public class TestPlayer implements Player {
 
     /**
      * @param maxCallsWithoutAction max number of priority passes a player may
-     *                              have for this test (default = 100)
+     * have for this test (default = 100)
      */
     public void setMaxCallsWithoutAction(int maxCallsWithoutAction) {
         this.maxCallsWithoutAction = maxCallsWithoutAction;
@@ -1046,13 +1044,13 @@ public class TestPlayer implements Player {
 
         List<String> data = cards.stream()
                 .map(c -> (((c instanceof PermanentToken) ? "[T] " : "[C] ")
-                        + c.getIdName()
-                        + (c.isCopy() ? " [copy of " + c.getCopyFrom().getId().toString().substring(0, 3) + "]" : "")
-                        + " - " + c.getPower().getValue() + "/" + c.getToughness().getValue()
-                        + (c.isPlaneswalker() ? " - L" + c.getCounters(game).getCount(CounterType.LOYALTY) : "")
-                        + ", " + (c.isTapped() ? "Tapped" : "Untapped")
-                        + getPrintableAliases(", [", c.getId(), "]")
-                        + (c.getAttachedTo() == null ? "" : ", attached to " + game.getPermanent(c.getAttachedTo()).getIdName())))
+                + c.getIdName()
+                + (c.isCopy() ? " [copy of " + c.getCopyFrom().getId().toString().substring(0, 3) + "]" : "")
+                + " - " + c.getPower().getValue() + "/" + c.getToughness().getValue()
+                + (c.isPlaneswalker() ? " - L" + c.getCounters(game).getCount(CounterType.LOYALTY) : "")
+                + ", " + (c.isTapped() ? "Tapped" : "Untapped")
+                + getPrintableAliases(", [", c.getId(), "]")
+                + (c.getAttachedTo() == null ? "" : ", attached to " + game.getPermanent(c.getAttachedTo()).getIdName())))
                 .sorted()
                 .collect(Collectors.toList());
 
@@ -1076,12 +1074,12 @@ public class TestPlayer implements Player {
 
         List<String> data = abilities.stream()
                 .map(a -> (a.getZone() + " -> "
-                        + a.getSourceObject(game).getIdName() + " -> "
-                        + (a.toString().startsWith("Cast ") ? "[" + a.getManaCostsToPay().getText() + "] -> " : "") // printed cost, not modified
-                        + (a.toString().length() > 0
-                        ? a.toString().substring(0, Math.min(20, a.toString().length()))
-                        : a.getClass().getSimpleName())
-                        + "..."))
+                + a.getSourceObject(game).getIdName() + " -> "
+                + (a.toString().startsWith("Cast ") ? "[" + a.getManaCostsToPay().getText() + "] -> " : "") // printed cost, not modified
+                + (a.toString().length() > 0
+                ? a.toString().substring(0, Math.min(20, a.toString().length()))
+                : a.getClass().getSimpleName())
+                + "..."))
                 .sorted()
                 .collect(Collectors.toList());
 
@@ -1446,7 +1444,7 @@ public class TestPlayer implements Player {
         UUID defenderId = null;
         boolean mustAttackByAction = false;
         boolean madeAttackByAction = false;
-        for (Iterator<org.mage.test.player.PlayerAction> it = actions.iterator(); it.hasNext(); ) {
+        for (Iterator<org.mage.test.player.PlayerAction> it = actions.iterator(); it.hasNext();) {
             PlayerAction action = it.next();
 
             // aiXXX commands
@@ -2021,7 +2019,7 @@ public class TestPlayer implements Player {
             // skip targets
             if (targets.get(0).equals(TARGET_SKIP)) {
                 Assert.assertTrue("found skip target, but it require more targets, needs "
-                                + (target.getMinNumberOfTargets() - target.getTargets().size()) + " more",
+                        + (target.getMinNumberOfTargets() - target.getTargets().size()) + " more",
                         target.getTargets().size() >= target.getMinNumberOfTargets());
                 targets.remove(0);
                 return true;
@@ -2326,7 +2324,7 @@ public class TestPlayer implements Player {
 
         this.chooseStrictModeFailed("choice", game,
                 "Triggered list (total " + abilities.size() + "):\n"
-                        + abilities.stream().map(a -> getInfo(a, game)).collect(Collectors.joining("\n")));
+                + abilities.stream().map(a -> getInfo(a, game)).collect(Collectors.joining("\n")));
         return computerPlayer.chooseTriggeredAbility(abilities, game);
     }
 
@@ -3496,7 +3494,7 @@ public class TestPlayer implements Player {
 
     @Override
     public boolean choose(Outcome outcome, Target target,
-                          UUID sourceId, Game game
+            UUID sourceId, Game game
     ) {
         // needed to call here the TestPlayer because it's overwitten
         return choose(outcome, target, sourceId, game, null);
@@ -3504,7 +3502,7 @@ public class TestPlayer implements Player {
 
     @Override
     public boolean choose(Outcome outcome, Cards cards,
-                          TargetCard target, Game game
+            TargetCard target, Game game
     ) {
         assertAliasSupportInChoices(false);
         if (!choices.isEmpty()) {
@@ -3541,7 +3539,7 @@ public class TestPlayer implements Player {
 
     @Override
     public boolean chooseTargetAmount(Outcome outcome, TargetAmount target,
-                                      Ability source, Game game
+            Ability source, Game game
     ) {
         // chooseTargetAmount calls for EACH target cycle (e.g. one target per click, see TargetAmount)
         // if use want to stop choosing then chooseTargetAmount must return false (example: up to xxx)
@@ -3554,7 +3552,7 @@ public class TestPlayer implements Player {
             // skip targets
             if (targets.get(0).equals(TARGET_SKIP)) {
                 Assert.assertTrue("found skip target, but it require more targets, needs "
-                                + (target.getMinNumberOfTargets() - target.getTargets().size()) + " more",
+                        + (target.getMinNumberOfTargets() - target.getTargets().size()) + " more",
                         target.getTargets().size() >= target.getMinNumberOfTargets());
                 targets.remove(0);
                 return false; // false in chooseTargetAmount = stop to choose
@@ -3607,15 +3605,15 @@ public class TestPlayer implements Player {
 
     @Override
     public boolean choosePile(Outcome outcome, String message,
-                              List<? extends Card> pile1, List<? extends Card> pile2,
-                              Game game
+            List<? extends Card> pile1, List<? extends Card> pile2,
+            Game game
     ) {
         return computerPlayer.choosePile(outcome, message, pile1, pile2, game);
     }
 
     @Override
     public boolean playMana(Ability ability, ManaCost unpaid,
-                            String promptText, Game game
+            String promptText, Game game
     ) {
         groupsForTargetHandling = null;
 
@@ -3665,15 +3663,15 @@ public class TestPlayer implements Player {
 
     @Override
     public UUID chooseBlockerOrder(List<Permanent> blockers, CombatGroup combatGroup,
-                                   List<UUID> blockerOrder, Game game
+            List<UUID> blockerOrder, Game game
     ) {
         return computerPlayer.chooseBlockerOrder(blockers, combatGroup, blockerOrder, game);
     }
 
     @Override
     public void assignDamage(int damage, List<UUID> targets,
-                             String singleTargetName, UUID sourceId,
-                             Game game
+            String singleTargetName, UUID sourceId,
+            Game game
     ) {
         computerPlayer.assignDamage(damage, targets, singleTargetName, sourceId, game);
     }
@@ -3692,14 +3690,14 @@ public class TestPlayer implements Player {
 
     @Override
     public void pickCard(List<Card> cards, Deck deck,
-                         Draft draft
+            Draft draft
     ) {
         computerPlayer.pickCard(cards, deck, draft);
     }
 
     @Override
     public boolean scry(int value, Ability source,
-                        Game game
+            Game game
     ) {
         // Don't scry at the start of the game.
         if (game.getTurnNum() == 1 && game.getStep() == null) {
@@ -3710,44 +3708,44 @@ public class TestPlayer implements Player {
 
     @Override
     public boolean surveil(int value, Ability source,
-                           Game game
+            Game game
     ) {
         return computerPlayer.surveil(value, source, game);
     }
 
     @Override
     public boolean moveCards(Card card, Zone toZone,
-                             Ability source, Game game
+            Ability source, Game game
     ) {
         return computerPlayer.moveCards(card, toZone, source, game);
     }
 
     @Override
     public boolean moveCards(Card card, Zone toZone,
-                             Ability source, Game game,
-                             boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
+            Ability source, Game game,
+            boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
     ) {
         return computerPlayer.moveCards(card, toZone, source, game, tapped, faceDown, byOwner, appliedEffects);
     }
 
     @Override
     public boolean moveCards(Cards cards, Zone toZone,
-                             Ability source, Game game
+            Ability source, Game game
     ) {
         return computerPlayer.moveCards(cards, toZone, source, game);
     }
 
     @Override
     public boolean moveCards(Set<Card> cards, Zone toZone,
-                             Ability source, Game game
+            Ability source, Game game
     ) {
         return computerPlayer.moveCards(cards, toZone, source, game);
     }
 
     @Override
     public boolean moveCards(Set<Card> cards, Zone toZone,
-                             Ability source, Game game,
-                             boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
+            Ability source, Game game,
+            boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
     ) {
         return computerPlayer.moveCards(cards, toZone, source, game, tapped, faceDown, byOwner, appliedEffects);
     }
diff --git a/Mage/src/main/java/mage/abilities/condition/common/SourceOnBattlefieldControlUnchangedCondition.java b/Mage/src/main/java/mage/abilities/condition/common/SourceOnBattlefieldControlUnchangedCondition.java
index b4d46d2ade..d1e9235db0 100644
--- a/Mage/src/main/java/mage/abilities/condition/common/SourceOnBattlefieldControlUnchangedCondition.java
+++ b/Mage/src/main/java/mage/abilities/condition/common/SourceOnBattlefieldControlUnchangedCondition.java
@@ -1,31 +1,43 @@
-
 package mage.abilities.condition.common;
 
-import java.util.Objects;
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.condition.Condition;
 import mage.game.Game;
-import mage.game.permanent.Permanent;
+import mage.watchers.common.LostControlWatcher;
 
 /**
- * This condition remembers controller on the first apply.
- * As long as this controller keeps unchanged and the source is
- * on the battlefield, the condition is true.
+ * This condition checks if ever since first call of the apply method the
+ * controller of the source has changed
+ *
+ * Monitoring the LOST_CONTROL event has the advantage that also all layered
+ * effects can correctly check for controller change because comparing old and
+ * new controller during their apply time does not take into account layered
+ * cahnge control effects that will be applied later.
+ *
+ * This condition needs the LostControlWatcher, so be sure to add it to the card
+ * that uses the condition
  *
  * @author LevelX2
  */
 public class SourceOnBattlefieldControlUnchangedCondition implements Condition {
-    
-    private UUID controllerId;
+
+    private Long checkingSince;
+    private int startingZoneChangeCounter;
 
     @Override
     public boolean apply(Game game, Ability source) {
-        if (controllerId == null) {
-            controllerId = source.getControllerId();
+        if (checkingSince == null) {
+            checkingSince = System.currentTimeMillis() - 1;
+            startingZoneChangeCounter = game.getState().getZoneChangeCounter(source.getSourceId());
         }
-        Permanent permanent = game.getBattlefield().getPermanent(source.getSourceId());
-        return (permanent != null && Objects.equals(controllerId, source.getControllerId()));
+        if (game.getState().getZoneChangeCounter(source.getSourceId()) > startingZoneChangeCounter) {
+            return false;
+        }
+        LostControlWatcher watcher = game.getState().getWatcher(LostControlWatcher.class);
+        if (watcher != null) {
+            return checkingSince > watcher.getOrderOfLastLostControl(source.getSourceId());
+        }
+        throw new UnsupportedOperationException("LostControlWatcher not found!");
     }
 
 }
diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousEffect.java b/Mage/src/main/java/mage/abilities/effects/ContinuousEffect.java
index cdea7ccc33..11a001c527 100644
--- a/Mage/src/main/java/mage/abilities/effects/ContinuousEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/ContinuousEffect.java
@@ -1,5 +1,9 @@
 package mage.abilities.effects;
 
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
 import mage.MageObjectReference;
 import mage.abilities.Ability;
 import mage.constants.DependencyType;
@@ -9,11 +13,6 @@ import mage.constants.SubLayer;
 import mage.game.Game;
 import mage.target.targetpointer.TargetPointer;
 
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Set;
-import java.util.UUID;
-
 /**
  * @author BetaSteward_at_googlemail.com
  */
@@ -67,8 +66,6 @@ public interface ContinuousEffect extends Effect {
 
     UUID getStartingController();
 
-    void incYourTurnNumPlayed();
-
     boolean isYourNextTurn(Game game);
 
     @Override
diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousEffectImpl.java b/Mage/src/main/java/mage/abilities/effects/ContinuousEffectImpl.java
index fd3f20e163..e2f8e2f159 100644
--- a/Mage/src/main/java/mage/abilities/effects/ContinuousEffectImpl.java
+++ b/Mage/src/main/java/mage/abilities/effects/ContinuousEffectImpl.java
@@ -1,5 +1,6 @@
 package mage.abilities.effects;
 
+import java.util.*;
 import mage.MageObjectReference;
 import mage.abilities.Ability;
 import mage.abilities.CompoundAbility;
@@ -19,8 +20,6 @@ import mage.game.stack.StackObject;
 import mage.players.Player;
 import mage.target.targetpointer.TargetPointer;
 
-import java.util.*;
-
 /**
  * @author BetaSteward_at_googlemail.com, JayDi85
  */
@@ -47,9 +46,9 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
     protected boolean characterDefining = false;
 
     // until your next turn or until end of your next turn
-    private UUID startingControllerId; // player to checkss turns (can't different with real controller ability)
-    private boolean startingTurnWasActive;
-    private int yourTurnNumPlayed = 0; // turnes played after effect was created
+    private UUID startingControllerId; // player to check for turn duration (can't different with real controller ability)
+    private boolean startingTurnWasActive; // effect started during related players turn and related players turn was already active
+    private int effectStartingOnTurn = 0; // turn the effect started
 
     public ContinuousEffectImpl(Duration duration, Outcome outcome) {
         super(outcome);
@@ -79,7 +78,7 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
         this.temporary = effect.temporary;
         this.startingControllerId = effect.startingControllerId;
         this.startingTurnWasActive = effect.startingTurnWasActive;
-        this.yourTurnNumPlayed = effect.yourTurnNumPlayed;
+        this.effectStartingOnTurn = effect.effectStartingOnTurn;
         this.dependencyTypes = effect.dependencyTypes;
         this.dependendToTypes = effect.dependendToTypes;
         this.characterDefining = effect.characterDefining;
@@ -191,23 +190,13 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
         this.startingControllerId = startingController;
         this.startingTurnWasActive = activePlayerId != null
                 && activePlayerId.equals(startingController); // you can't use "game" for active player cause it's called from tests/cheat too
-        this.yourTurnNumPlayed = 0;
-    }
-
-    @Override
-    public void incYourTurnNumPlayed() {
-        yourTurnNumPlayed++;
+        this.effectStartingOnTurn = game.getTurnNum();
     }
 
     @Override
     public boolean isYourNextTurn(Game game) {
-        if (this.startingTurnWasActive) {
-            return yourTurnNumPlayed == 1
-                    && game.isActivePlayer(startingControllerId);
-        } else {
-            return yourTurnNumPlayed == 0
-                    && game.isActivePlayer(startingControllerId);
-        }
+        return effectStartingOnTurn < game.getTurnNum()
+                && game.isActivePlayer(startingControllerId);
     }
 
     @Override
@@ -367,6 +356,9 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
     /**
      * Auto-generates dependencies on different effects (what's apply first and
      * what's apply second)
+     *
+     * @param abilityToGain
+     * @param filterToSearch
      */
     public void generateGainAbilityDependencies(Ability abilityToGain, Filter filterToSearch) {
         this.addDependencyType(DependencyType.AddingAbility);
diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java b/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java
index 022eefef31..516cee562b 100644
--- a/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java
+++ b/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java
@@ -1,5 +1,9 @@
 package mage.abilities.effects;
 
+import java.io.Serializable;
+import java.util.*;
+import java.util.Map.Entry;
+import java.util.stream.Collectors;
 import mage.MageObject;
 import mage.MageObjectReference;
 import mage.abilities.Ability;
@@ -26,11 +30,6 @@ import mage.target.common.TargetCardInHand;
 import mage.util.CardUtil;
 import org.apache.log4j.Logger;
 
-import java.io.Serializable;
-import java.util.*;
-import java.util.Map.Entry;
-import java.util.stream.Collectors;
-
 /**
  * @author BetaSteward_at_googlemail.com
  */
@@ -163,28 +162,17 @@ public class ContinuousEffects implements Serializable {
         spliceCardEffects.removeInactiveEffects(game);
     }
 
-    public synchronized void incYourTurnNumPlayed(Game game) {
-        layeredEffects.incYourTurnNumPlayed(game);
-        continuousRuleModifyingEffects.incYourTurnNumPlayed(game);
-        replacementEffects.incYourTurnNumPlayed(game);
-        preventionEffects.incYourTurnNumPlayed(game);
-        requirementEffects.incYourTurnNumPlayed(game);
-        restrictionEffects.incYourTurnNumPlayed(game);
-        for (ContinuousEffectsList asThoughtlist : asThoughEffectsMap.values()) {
-            asThoughtlist.incYourTurnNumPlayed(game);
-        }
-        costModificationEffects.incYourTurnNumPlayed(game);
-        spliceCardEffects.incYourTurnNumPlayed(game);
-    }
-
     public synchronized List<ContinuousEffect> getLayeredEffects(Game game) {
         return getLayeredEffects(game, "main");
     }
 
     /**
-     * Return effects list ordered by timestamps (timestamps are automaticity generates from new/old lists on same layer)
+     * Return effects list ordered by timestamps (timestamps are automaticity
+     * generates from new/old lists on same layer)
      *
-     * @param timestampGroupName workaround to fix broken timestamps on effect's add/remove between different layers
+     * @param game
+     * @param timestampGroupName workaround to fix broken timestamps on effect's
+     * add/remove between different layers
      * @return effects list ordered by timestamp
      */
     public synchronized List<ContinuousEffect> getLayeredEffects(Game game, String timestampGroupName) {
@@ -229,8 +217,10 @@ public class ContinuousEffects implements Serializable {
      * "actual" meaning it becomes turned on that is defined by
      * Ability.#isInUseableZone(Game, boolean) method in
      * #getLayeredEffects(Game).
-     * <p>
-     * It must be called with different timestamp group name (otherwise sort order will be changed for add/remove effects, see Urborg and Bloodmoon test)
+     *
+     * It must be called with different timestamp group name (otherwise sort
+     * order will be changed for add/remove effects, see Urborg and Bloodmoon
+     * test)
      *
      * @param layerEffects
      */
@@ -359,7 +349,7 @@ public class ContinuousEffects implements Serializable {
         }
         // boolean checkLKI = event.getType().equals(EventType.ZONE_CHANGE) || event.getType().equals(EventType.DESTROYED_PERMANENT);
         //get all applicable transient Replacement effects
-        for (Iterator<ReplacementEffect> iterator = replacementEffects.iterator(); iterator.hasNext(); ) {
+        for (Iterator<ReplacementEffect> iterator = replacementEffects.iterator(); iterator.hasNext();) {
             ReplacementEffect effect = iterator.next();
             if (!effect.checksEventType(event, game)) {
                 continue;
@@ -392,7 +382,7 @@ public class ContinuousEffects implements Serializable {
             }
         }
 
-        for (Iterator<PreventionEffect> iterator = preventionEffects.iterator(); iterator.hasNext(); ) {
+        for (Iterator<PreventionEffect> iterator = preventionEffects.iterator(); iterator.hasNext();) {
             PreventionEffect effect = iterator.next();
             if (!effect.checksEventType(event, game)) {
                 continue;
@@ -768,8 +758,8 @@ public class ContinuousEffects implements Serializable {
      * @param event
      * @param targetAbility ability the event is attached to. can be null.
      * @param game
-     * @param silentMode    true if the event does not really happen but it's
-     *                      checked if the event would be replaced
+     * @param silentMode true if the event does not really happen but it's
+     * checked if the event would be replaced
      * @return
      */
     public boolean preventedByRuleModification(GameEvent event, Ability targetAbility, Game game, boolean silentMode) {
@@ -817,7 +807,7 @@ public class ContinuousEffects implements Serializable {
         do {
             Map<ReplacementEffect, Set<Ability>> rEffects = getApplicableReplacementEffects(event, game);
             // Remove all consumed effects (ability dependant)
-            for (Iterator<ReplacementEffect> it1 = rEffects.keySet().iterator(); it1.hasNext(); ) {
+            for (Iterator<ReplacementEffect> it1 = rEffects.keySet().iterator(); it1.hasNext();) {
                 ReplacementEffect entry = it1.next();
                 if (consumed.containsKey(entry.getId()) /*&& !(entry instanceof CommanderReplacementEffect) */) { // 903.9.
                     Set<UUID> consumedAbilitiesIds = consumed.get(entry.getId());
@@ -1002,7 +992,7 @@ public class ContinuousEffects implements Serializable {
                             .entrySet()
                             .stream()
                             .filter(entry -> dependentTo.contains(entry.getKey().getId())
-                                    && entry.getValue().contains(effect.getId()))
+                            && entry.getValue().contains(effect.getId()))
                             .forEach(entry -> {
                                 entry.getValue().remove(effect.getId());
                                 dependentTo.remove(entry.getKey().getId());
@@ -1036,7 +1026,7 @@ public class ContinuousEffects implements Serializable {
                     continue;
                 }
                 // check if waiting effects can be applied now
-                for (Iterator<Map.Entry<ContinuousEffect, Set<UUID>>> iterator = waitingEffects.entrySet().iterator(); iterator.hasNext(); ) {
+                for (Iterator<Map.Entry<ContinuousEffect, Set<UUID>>> iterator = waitingEffects.entrySet().iterator(); iterator.hasNext();) {
                     Map.Entry<ContinuousEffect, Set<UUID>> entry = iterator.next();
                     if (!appliedEffects.containsAll(entry.getValue())) { // all dependent to effects are applied now so apply the effect itself
                         continue;
@@ -1283,18 +1273,19 @@ public class ContinuousEffects implements Serializable {
     }
 
     private void setControllerForEffect(ContinuousEffectsList<?> effects, UUID sourceId, UUID controllerId) {
-        for (Effect effect : effects) {
-            Set<Ability> abilities = effects.getAbility(effect.getId());
-            for (Ability ability : abilities) {
-                if (ability.getSourceId() != null) {
-                    if (ability.getSourceId().equals(sourceId)) {
-                        ability.setControllerId(controllerId);
+        for (ContinuousEffect effect : effects) {
+            if (!effect.getDuration().isFixedController()) {
+                Set<Ability> abilities = effects.getAbility(effect.getId());
+                for (Ability ability : abilities) {
+                    if (ability.getSourceId() != null) {
+                        if (ability.getSourceId().equals(sourceId)) {
+                            ability.setControllerId(controllerId);
+                        }
+                    } else if (ability.getZone() != Zone.COMMAND) {
+                        logger.fatal("Continuous effect for ability with no sourceId Ability: " + ability);
                     }
-                } else if (ability.getZone() != Zone.COMMAND) {
-                    logger.fatal("Continuous effect for ability with no sourceId Ability: " + ability);
                 }
             }
-
         }
     }
 
diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousEffectsList.java b/Mage/src/main/java/mage/abilities/effects/ContinuousEffectsList.java
index 26e9288654..803cdc5bc1 100644
--- a/Mage/src/main/java/mage/abilities/effects/ContinuousEffectsList.java
+++ b/Mage/src/main/java/mage/abilities/effects/ContinuousEffectsList.java
@@ -1,5 +1,6 @@
 package mage.abilities.effects;
 
+import java.util.*;
 import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.MageSingleton;
@@ -10,8 +11,6 @@ import mage.game.Game;
 import mage.players.Player;
 import org.apache.log4j.Logger;
 
-import java.util.*;
-
 /**
  * @param <T>
  * @author BetaSteward_at_googlemail.com
@@ -47,7 +46,7 @@ public class ContinuousEffectsList<T extends ContinuousEffect> extends ArrayList
     public void removeEndOfTurnEffects(Game game) {
         // calls every turn on cleanup step (only end of turn duration)
         // rules 514.2
-        for (Iterator<T> i = this.iterator(); i.hasNext(); ) {
+        for (Iterator<T> i = this.iterator(); i.hasNext();) {
             T entry = i.next();
             boolean canRemove = false;
             switch (entry.getDuration()) {
@@ -67,7 +66,7 @@ public class ContinuousEffectsList<T extends ContinuousEffect> extends ArrayList
 
     public void removeEndOfCombatEffects() {
 
-        for (Iterator<T> i = this.iterator(); i.hasNext(); ) {
+        for (Iterator<T> i = this.iterator(); i.hasNext();) {
             T entry = i.next();
             if (entry.getDuration() == Duration.EndOfCombat) {
                 i.remove();
@@ -77,7 +76,7 @@ public class ContinuousEffectsList<T extends ContinuousEffect> extends ArrayList
     }
 
     public void removeInactiveEffects(Game game) {
-        for (Iterator<T> i = this.iterator(); i.hasNext(); ) {
+        for (Iterator<T> i = this.iterator(); i.hasNext();) {
             T entry = i.next();
             if (isInactive(entry, game)) {
                 i.remove();
@@ -86,15 +85,6 @@ public class ContinuousEffectsList<T extends ContinuousEffect> extends ArrayList
         }
     }
 
-    public void incYourTurnNumPlayed(Game game) {
-        for (Iterator<T> i = this.iterator(); i.hasNext(); ) {
-            T entry = i.next();
-            if (game.isActivePlayer(entry.getStartingController())) {
-                entry.incYourTurnNumPlayed();
-            }
-        }
-    }
-
     private boolean isInactive(T effect, Game game) {
         // ends all inactive effects -- calls on player leave or apply new effect
         if (game.getState().isGameOver()) {
@@ -109,9 +99,8 @@ public class ContinuousEffectsList<T extends ContinuousEffect> extends ArrayList
         those objects are exiled. This is not a state-based action. It happens as soon as the player leaves the game.
         If the player who left the game had priority at the time they left, priority passes to the next player in turn
         order who’s still in the game.
-        */
+         */
         // objects removes doing in player.leave() call... effects removes is here
-
         Set<Ability> set = effectAbilityMap.get(effect.getId());
         if (set == null) {
             logger.debug("No abilities for effect found: " + effect.toString());
@@ -224,7 +213,7 @@ public class ContinuousEffectsList<T extends ContinuousEffect> extends ArrayList
             abilities.removeAll(abilitiesToRemove);
         }
         if (abilities == null || abilities.isEmpty()) {
-            for (Iterator<T> iterator = this.iterator(); iterator.hasNext(); ) {
+            for (Iterator<T> iterator = this.iterator(); iterator.hasNext();) {
                 ContinuousEffect effect = iterator.next();
                 if (effect.getId().equals(effectIdToRemove)) {
                     iterator.remove();
diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackYouEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackYouEffect.java
index 462394887b..d13c067608 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackYouEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/combat/CantAttackYouEffect.java
@@ -1,32 +1,23 @@
 package mage.abilities.effects.common.combat;
 
+import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.effects.RestrictionEffect;
 import mage.constants.Duration;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
 
-import java.util.UUID;
-
 /**
  * @author TheElk801
  */
 public class CantAttackYouEffect extends RestrictionEffect {
 
-    UUID controllerId;
-
     public CantAttackYouEffect(Duration duration) {
         super(duration);
     }
 
-    public CantAttackYouEffect(Duration duration, UUID controllerId) {
-        super(duration);
-        this.controllerId = controllerId;
-    }
-
     public CantAttackYouEffect(final CantAttackYouEffect effect) {
         super(effect);
-        this.controllerId = effect.controllerId;
     }
 
     @Override
@@ -44,9 +35,6 @@ public class CantAttackYouEffect extends RestrictionEffect {
         if (defenderId == null) {
             return true;
         }
-        if (controllerId == null) {
-            controllerId = source.getControllerId();
-        }
-        return !defenderId.equals(controllerId);
+        return !defenderId.equals(source.getControllerId());
     }
 }
diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/GoadTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/GoadTargetEffect.java
index fe5d9a8923..ffb6e25cbe 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/combat/GoadTargetEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/combat/GoadTargetEffect.java
@@ -41,18 +41,18 @@ public class GoadTargetEffect extends OneShotEffect {
         Permanent targetCreature = game.getPermanent(getTargetPointer().getFirst(game, source));
         Player controller = game.getPlayer(source.getControllerId());
         if (targetCreature != null && controller != null) {
-            // TODO: Allow goad to target controller, current AttacksIfAbleTargetEffect is not support it
+            // TODO: Allow goad to target controller, current AttacksIfAbleTargetEffect does not support it
             // https://github.com/magefree/mage/issues/5283
             /*
             If the creature doesn’t meet any of the above exceptions and can attack, it must attack a player other than
-            the controller of the spell or ability that goaded it if able. It the creature can’t attack any of those
+            the controller of the spell or ability that goaded it if able. If the creature can’t attack any of those
             players but could otherwise attack, it must attack an opposing planeswalker (controlled by any opponent)
             or the player that goaded it. (2016-08-23)
              */
             ContinuousEffect effect = new AttacksIfAbleTargetEffect(Duration.UntilYourNextTurn);
             effect.setTargetPointer(new FixedTarget(getTargetPointer().getFirst(game, source)));
             game.addEffect(effect, source);
-            effect = new CantAttackYouEffect(Duration.UntilYourNextTurn, source.getControllerId()); // remember current controller
+            effect = new CantAttackYouEffect(Duration.UntilYourNextTurn); // remember current controller
             effect.setTargetPointer(new FixedTarget(getTargetPointer().getFirst(game, source)));
             game.addEffect(effect, source);
             game.informPlayers(controller.getLogName() + " is goading " + targetCreature.getLogName());
diff --git a/Mage/src/main/java/mage/constants/Duration.java b/Mage/src/main/java/mage/constants/Duration.java
index 0ec5060ed0..1b857000ae 100644
--- a/Mage/src/main/java/mage/constants/Duration.java
+++ b/Mage/src/main/java/mage/constants/Duration.java
@@ -4,25 +4,27 @@ package mage.constants;
  * @author North
  */
 public enum Duration {
-    OneUse("", true),
-    EndOfGame("for the rest of the game", false),
-    WhileOnBattlefield("", false),
-    WhileOnStack("", false),
-    WhileInGraveyard("", false),
-    EndOfTurn("until end of turn", true),
-    UntilYourNextTurn("until your next turn", true),
-    UntilEndOfYourNextTurn("until the end of your next turn", true),
-    UntilSourceLeavesBattlefield("until {source} leaves the battlefield", true), // supported for continuous layered effects
-    EndOfCombat("until end of combat", true),
-    EndOfStep("until end of phase step", true),
-    Custom("", true);
+    OneUse("", true, true),
+    EndOfGame("for the rest of the game", false, false),
+    WhileOnBattlefield("", false, false),
+    WhileOnStack("", false, true),
+    WhileInGraveyard("", false, false),
+    EndOfTurn("until end of turn", true, true),
+    UntilYourNextTurn("until your next turn", true, true),
+    UntilEndOfYourNextTurn("until the end of your next turn", true, true),
+    UntilSourceLeavesBattlefield("until {source} leaves the battlefield", true, false), // supported for continuous layered effects
+    EndOfCombat("until end of combat", true, true),
+    EndOfStep("until end of phase step", true, true),
+    Custom("", true, true);
 
     private final String text;
-    private final boolean onlyValidIfNoZoneChange; // defines if an effect lasts only if the source has not chnaged zone since init of the effect
+    private final boolean onlyValidIfNoZoneChange; // defines if an effect lasts only if the source has not changed zone since init of the effect
+    private final boolean fixedController; // has the controller of the effect to change, if the controller of the source changes
 
-    Duration(String text, boolean onlyValidIfNoZoneChange) {
+    Duration(String text, boolean onlyValidIfNoZoneChange, boolean fixedController) {
         this.text = text;
         this.onlyValidIfNoZoneChange = onlyValidIfNoZoneChange;
+        this.fixedController = fixedController;
     }
 
     @Override
@@ -34,4 +36,7 @@ public enum Duration {
         return onlyValidIfNoZoneChange;
     }
 
+    public boolean isFixedController() {
+        return fixedController;
+    }
 }
diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java
index bed1000a87..a1f0d9e9cf 100644
--- a/Mage/src/main/java/mage/game/GameImpl.java
+++ b/Mage/src/main/java/mage/game/GameImpl.java
@@ -1,5 +1,9 @@
 package mage.game;
 
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.*;
+import java.util.Map.Entry;
 import mage.MageException;
 import mage.MageObject;
 import mage.abilities.*;
@@ -67,11 +71,6 @@ import mage.util.functions.ApplyToPermanent;
 import mage.watchers.common.*;
 import org.apache.log4j.Logger;
 
-import java.io.IOException;
-import java.io.Serializable;
-import java.util.*;
-import java.util.Map.Entry;
-
 public abstract class GameImpl implements Game, Serializable {
 
     private static final int ROLLBACK_TURNS_MAX = 4;
@@ -1549,7 +1548,7 @@ public abstract class GameImpl implements Game, Serializable {
     /**
      * @param emblem
      * @param sourceObject
-     * @param toPlayerId   controller and owner of the emblem
+     * @param toPlayerId controller and owner of the emblem
      */
     @Override
     public void addEmblem(Emblem emblem, MageObject sourceObject, UUID toPlayerId) {
@@ -1567,8 +1566,8 @@ public abstract class GameImpl implements Game, Serializable {
     /**
      * @param plane
      * @param sourceObject
-     * @param toPlayerId   controller and owner of the plane (may only be one per
-     *                     game..)
+     * @param toPlayerId controller and owner of the plane (may only be one per
+     * game..)
      * @return boolean - whether the plane was added successfully or not
      */
     @Override
@@ -1804,7 +1803,7 @@ public abstract class GameImpl implements Game, Serializable {
                     break;
                 }
                 // triggered abilities that don't use the stack have to be executed first (e.g. Banisher Priest Return exiled creature
-                for (Iterator<TriggeredAbility> it = abilities.iterator(); it.hasNext(); ) {
+                for (Iterator<TriggeredAbility> it = abilities.iterator(); it.hasNext();) {
                     TriggeredAbility triggeredAbility = it.next();
                     if (!triggeredAbility.isUsesStack()) {
                         state.removeTriggeredAbility(triggeredAbility);
@@ -1917,8 +1916,8 @@ public abstract class GameImpl implements Game, Serializable {
                      */
                     boolean usePowerInsteadOfToughnessForDamageLethality = usePowerInsteadOfToughnessForDamageLethalityFilters.stream()
                             .anyMatch(filter -> filter.match(perm, this));
-                    int lethalDamageThreshold = usePowerInsteadOfToughnessForDamageLethality ?
-                            // Zilortha, Strength Incarnate, 2020-04-17: A creature with 0 power isn’t destroyed unless it has at least 1 damage marked on it.
+                    int lethalDamageThreshold = usePowerInsteadOfToughnessForDamageLethality
+                            ? // Zilortha, Strength Incarnate, 2020-04-17: A creature with 0 power isn’t destroyed unless it has at least 1 damage marked on it.
                             Math.max(perm.getPower().getValue(), 1) : perm.getToughness().getValue();
                     if (lethalDamageThreshold <= perm.getDamage() || perm.isDeathtouched()) {
                         if (perm.destroy(null, this, false)) {
@@ -2249,7 +2248,6 @@ public abstract class GameImpl implements Game, Serializable {
         }
 
         //TODO: implement the rest
-
         return somethingHappened;
     }
 
@@ -2557,7 +2555,7 @@ public abstract class GameImpl implements Game, Serializable {
         }
         //20100423 - 800.4a
         Set<Card> toOutside = new HashSet<>();
-        for (Iterator<Permanent> it = getBattlefield().getAllPermanents().iterator(); it.hasNext(); ) {
+        for (Iterator<Permanent> it = getBattlefield().getAllPermanents().iterator(); it.hasNext();) {
             Permanent perm = it.next();
             if (perm.isOwnedBy(playerId)) {
                 if (perm.getAttachedTo() != null) {
@@ -2595,14 +2593,14 @@ public abstract class GameImpl implements Game, Serializable {
                 }
             }
         }
-        for(Card card : toOutside) {
+        for (Card card : toOutside) {
             rememberLKI(card.getId(), Zone.BATTLEFIELD, card);
         }
         // needed to send event that permanent leaves the battlefield to allow non stack effects to execute
         player.moveCards(toOutside, Zone.OUTSIDE, null, this);
         // triggered abilities that don't use the stack have to be executed
         List<TriggeredAbility> abilities = state.getTriggered(player.getId());
-        for (Iterator<TriggeredAbility> it = abilities.iterator(); it.hasNext(); ) {
+        for (Iterator<TriggeredAbility> it = abilities.iterator(); it.hasNext();) {
             TriggeredAbility triggeredAbility = it.next();
             if (!triggeredAbility.isUsesStack()) {
                 state.removeTriggeredAbility(triggeredAbility);
@@ -2622,7 +2620,7 @@ public abstract class GameImpl implements Game, Serializable {
 
         // Remove cards from the player in all exile zones
         for (ExileZone exile : this.getExile().getExileZones()) {
-            for (Iterator<UUID> it = exile.iterator(); it.hasNext(); ) {
+            for (Iterator<UUID> it = exile.iterator(); it.hasNext();) {
                 Card card = this.getCard(it.next());
                 if (card != null && card.isOwnedBy(playerId)) {
                     it.remove();
@@ -2632,7 +2630,7 @@ public abstract class GameImpl implements Game, Serializable {
 
         //Remove all commander/emblems/plane the player controls
         boolean addPlaneAgain = false;
-        for (Iterator<CommandObject> it = this.getState().getCommand().iterator(); it.hasNext(); ) {
+        for (Iterator<CommandObject> it = this.getState().getCommand().iterator(); it.hasNext();) {
             CommandObject obj = it.next();
             if (obj.isControlledBy(playerId)) {
                 if (obj instanceof Emblem) {
diff --git a/Mage/src/main/java/mage/game/GameState.java b/Mage/src/main/java/mage/game/GameState.java
index 1530fc0645..dabfb43700 100644
--- a/Mage/src/main/java/mage/game/GameState.java
+++ b/Mage/src/main/java/mage/game/GameState.java
@@ -1,5 +1,9 @@
 package mage.game;
 
+import java.io.Serializable;
+import java.util.*;
+import static java.util.Collections.emptyList;
+import java.util.stream.Collectors;
 import mage.MageObject;
 import mage.abilities.*;
 import mage.abilities.effects.ContinuousEffect;
@@ -35,12 +39,6 @@ import mage.util.ThreadLocalStringBuilder;
 import mage.watchers.Watcher;
 import mage.watchers.Watchers;
 
-import java.io.Serializable;
-import java.util.*;
-import java.util.stream.Collectors;
-
-import static java.util.Collections.emptyList;
-
 /**
  * @author BetaSteward_at_googlemail.com
  * <p>
@@ -179,8 +177,8 @@ public class GameState implements Serializable, Copyable<GameState> {
         this.copiedCards.putAll(state.copiedCards);
         this.permanentOrderNumber = state.permanentOrderNumber;
         this.applyEffectsCounter = state.applyEffectsCounter;
-        state.usePowerInsteadOfToughnessForDamageLethalityFilters.forEach((uuid, filter) ->
-                this.usePowerInsteadOfToughnessForDamageLethalityFilters.put(uuid, filter.copy()));
+        state.usePowerInsteadOfToughnessForDamageLethalityFilters.forEach((uuid, filter)
+                -> this.usePowerInsteadOfToughnessForDamageLethalityFilters.put(uuid, filter.copy()));
     }
 
     public void restoreForRollBack(GameState state) {
@@ -226,8 +224,8 @@ public class GameState implements Serializable, Copyable<GameState> {
         this.copiedCards = state.copiedCards;
         this.permanentOrderNumber = state.permanentOrderNumber;
         this.applyEffectsCounter = state.applyEffectsCounter;
-        state.usePowerInsteadOfToughnessForDamageLethalityFilters.forEach((uuid, filter) ->
-                this.usePowerInsteadOfToughnessForDamageLethalityFilters.put(uuid, filter.copy()));
+        state.usePowerInsteadOfToughnessForDamageLethalityFilters.forEach((uuid, filter)
+                -> this.usePowerInsteadOfToughnessForDamageLethalityFilters.put(uuid, filter.copy()));
     }
 
     @Override
@@ -605,7 +603,6 @@ public class GameState implements Serializable, Copyable<GameState> {
         delayed.removeEndOfTurnAbilities(game);
         exile.cleanupEndOfTurnZones(game);
         game.applyEffects();
-        effects.incYourTurnNumPlayed(game);
     }
 
     public void addEffect(ContinuousEffect effect, Ability source) {
@@ -623,7 +620,6 @@ public class GameState implements Serializable, Copyable<GameState> {
 //    public void addMessage(String message) {
 //        this.messages.add(message);
 //    }
-
     /**
      * Returns a list of all players of the game ignoring range or if a player
      * has lost or left the game.
@@ -797,7 +793,7 @@ public class GameState implements Serializable, Copyable<GameState> {
         for (Map.Entry<ZoneChangeData, List<GameEvent>> entry : eventsByKey.entrySet()) {
             Set<Card> movedCards = new LinkedHashSet<>();
             Set<PermanentToken> movedTokens = new LinkedHashSet<>();
-            for (Iterator<GameEvent> it = entry.getValue().iterator(); it.hasNext(); ) {
+            for (Iterator<GameEvent> it = entry.getValue().iterator(); it.hasNext();) {
                 GameEvent event = it.next();
                 ZoneChangeEvent castEvent = (ZoneChangeEvent) event;
                 UUID targetId = castEvent.getTargetId();
@@ -1030,7 +1026,7 @@ public class GameState implements Serializable, Copyable<GameState> {
      * @param attachedTo
      * @param ability
      * @param copyAbility copies non MageSingleton abilities before adding to
-     *                    state
+     * state
      */
     public void addOtherAbility(Card attachedTo, Ability ability, boolean copyAbility) {
         Ability newAbility;
diff --git a/Mage/src/main/java/mage/game/combat/Combat.java b/Mage/src/main/java/mage/game/combat/Combat.java
index 3dff44f399..a3c25098d5 100644
--- a/Mage/src/main/java/mage/game/combat/Combat.java
+++ b/Mage/src/main/java/mage/game/combat/Combat.java
@@ -1,5 +1,7 @@
 package mage.game.combat;
 
+import java.io.Serializable;
+import java.util.*;
 import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.effects.RequirementEffect;
@@ -33,9 +35,6 @@ import mage.util.Copyable;
 import mage.util.trace.TraceUtil;
 import org.apache.log4j.Logger;
 
-import java.io.Serializable;
-import java.util.*;
-
 /**
  * @author BetaSteward_at_googlemail.com
  */
@@ -346,8 +345,8 @@ public class Combat implements Serializable, Copyable<Combat> {
             if (game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARING_ATTACKERS, attackingPlayerId, attackingPlayerId))
                     || (!canBand && !canBandWithOther)
                     || !player.chooseUse(Outcome.Benefit,
-                    "Do you wish to " + (isBanded ? "band " + attacker.getLogName()
-                            + " with another " : "form a band with " + attacker.getLogName() + " and an ")
+                            "Do you wish to " + (isBanded ? "band " + attacker.getLogName()
+                                    + " with another " : "form a band with " + attacker.getLogName() + " and an ")
                             + "attacking creature?", null, game)) {
                 break;
             }
@@ -412,7 +411,8 @@ public class Combat implements Serializable, Copyable<Combat> {
         if (!isBanded) {
             return;
         }
-        StringBuilder sb = new StringBuilder(player.getLogName()).append(" formed a band with ").append((attacker.getBandedCards().size() + 1) + " creatures: ");
+        StringBuilder sb = new StringBuilder(player.getLogName()).append(" formed a band with ")
+                .append(attacker.getBandedCards().size()).append(1).append(" creatures: ");
         sb.append(attacker.getLogName());
         for (UUID id : attacker.getBandedCards()) {
             sb.append(", ");
@@ -565,7 +565,7 @@ public class Combat implements Serializable, Copyable<Combat> {
      * Handle the blocker selection process
      *
      * @param blockController player that controls how to block, if null the
-     *                        defender is the controller
+     * defender is the controller
      * @param game
      */
     public void selectBlockers(Player blockController, Game game) {
@@ -744,15 +744,15 @@ public class Combat implements Serializable, Copyable<Combat> {
     }
 
     /**
-     * 509.1c The defending player checks each creature they control to
-     * see whether it's affected by any requirements (effects that say a
-     * creature must block, or that it must block if some condition is met). If
-     * the number of requirements that are being obeyed is fewer than the
-     * maximum possible number of requirements that could be obeyed without
-     * disobeying any restrictions, the declaration of blockers is illegal. If a
-     * creature can't block unless a player pays a cost, that player is not
-     * required to pay that cost, even if blocking with that creature would
-     * increase the number of requirements being obeyed.
+     * 509.1c The defending player checks each creature they control to see
+     * whether it's affected by any requirements (effects that say a creature
+     * must block, or that it must block if some condition is met). If the
+     * number of requirements that are being obeyed is fewer than the maximum
+     * possible number of requirements that could be obeyed without disobeying
+     * any restrictions, the declaration of blockers is illegal. If a creature
+     * can't block unless a player pays a cost, that player is not required to
+     * pay that cost, even if blocking with that creature would increase the
+     * number of requirements being obeyed.
      * <p>
      * <p>
      * Example: A player controls one creature that "blocks if able" and another
@@ -1379,7 +1379,7 @@ public class Combat implements Serializable, Copyable<Combat> {
      * @param playerId
      * @param game
      * @param solveBanding check whether also add creatures banded with
-     *                     attackerId
+     * attackerId
      */
     public void addBlockingGroup(UUID blockerId, UUID attackerId, UUID playerId, Game game, boolean solveBanding) {
         Permanent blocker = game.getPermanent(blockerId);
diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java
index dbdd179cad..a816624f03 100644
--- a/Mage/src/main/java/mage/players/PlayerImpl.java
+++ b/Mage/src/main/java/mage/players/PlayerImpl.java
@@ -1,6 +1,9 @@
 package mage.players;
 
 import com.google.common.collect.ImmutableMap;
+import java.io.Serializable;
+import java.util.*;
+import java.util.Map.Entry;
 import mage.ConditionalMana;
 import mage.MageObject;
 import mage.MageObjectReference;
@@ -65,10 +68,6 @@ import mage.util.GameLog;
 import mage.util.RandomUtil;
 import org.apache.log4j.Logger;
 
-import java.io.Serializable;
-import java.util.*;
-import java.util.Map.Entry;
-
 public abstract class PlayerImpl implements Player, Serializable {
 
     private static final Logger logger = Logger.getLogger(PlayerImpl.class);
@@ -612,9 +611,9 @@ public abstract class PlayerImpl implements Player, Serializable {
                     && this.hasOpponent(sourceControllerId, game)
                     && game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, null, sourceControllerId, game) == null
                     && abilities.stream()
-                    .filter(HexproofBaseAbility.class::isInstance)
-                    .map(HexproofBaseAbility.class::cast)
-                    .anyMatch(ability -> ability.checkObject(source, game))) {
+                            .filter(HexproofBaseAbility.class::isInstance)
+                            .map(HexproofBaseAbility.class::cast)
+                            .anyMatch(ability -> ability.checkObject(source, game))) {
                 return false;
             }
 
@@ -654,7 +653,7 @@ public abstract class PlayerImpl implements Player, Serializable {
                 game.informPlayers(getLogName() + " discards down to "
                         + this.maxHandSize
                         + (this.maxHandSize == 1
-                        ? " hand card" : " hand cards"));
+                                ? " hand card" : " hand cards"));
             }
             discard(hand.size() - this.maxHandSize, false, null, game);
         }
@@ -803,7 +802,7 @@ public abstract class PlayerImpl implements Player, Serializable {
         }
         GameEvent gameEvent = GameEvent.getEvent(GameEvent.EventType.DISCARD_CARD,
                 card.getId(), source == null
-                        ? null : source.getSourceId(), playerId);
+                ? null : source.getSourceId(), playerId);
         gameEvent.setFlag(source != null); // event from effect or from cost (source == null)
         if (game.replaceEvent(gameEvent, source)) {
             return false;
@@ -1345,6 +1344,9 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean activateAbility(ActivatedAbility ability, Game game) {
+        if (ability == null) {
+            return false;
+        }
         boolean result;
         if (ability instanceof PassAbility) {
             pass(game);
@@ -1502,14 +1504,13 @@ public abstract class PlayerImpl implements Player, Serializable {
     @Override
     public LinkedHashMap<UUID, ActivatedAbility> getPlayableActivatedAbilities(MageObject object, Zone zone, Game game) {
         LinkedHashMap<UUID, ActivatedAbility> useable = new LinkedHashMap<>();
+        // It may not be possible to activate abilities of stack abilities
+        if (object instanceof StackAbility || object == null) {
+            return useable;
+        }
         boolean previousState = game.inCheckPlayableState();
         game.setCheckPlayableState(true);
         try {
-            // It may not be possible to activate abilities of stack abilities
-            if (object instanceof StackAbility) {
-                return useable;
-            }
-
             // collect and filter playable activated abilities
             // GUI: user clicks on card, but it must activate ability from any card's parts (main, left, right)
             UUID needId1, needId2, needId3;
@@ -1810,9 +1811,9 @@ public abstract class PlayerImpl implements Player, Serializable {
     }
 
     private List<Permanent> getPermanentsThatCanBeUntapped(Game game,
-                                                           List<Permanent> canBeUntapped,
-                                                           RestrictionUntapNotMoreThanEffect handledEffect,
-                                                           Map<Entry<RestrictionUntapNotMoreThanEffect, Set<Ability>>, Integer> notMoreThanEffectsUsage) {
+            List<Permanent> canBeUntapped,
+            RestrictionUntapNotMoreThanEffect handledEffect,
+            Map<Entry<RestrictionUntapNotMoreThanEffect, Set<Ability>>, Integer> notMoreThanEffectsUsage) {
         List<Permanent> leftForUntap = new ArrayList<>();
         // select permanents that can still be untapped
         for (Permanent permanent : canBeUntapped) {
@@ -2521,7 +2522,7 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean searchLibrary(TargetCardInLibrary target, Ability source, Game game, UUID targetPlayerId,
-                                 boolean triggerEvents) {
+            boolean triggerEvents) {
         //20091005 - 701.14c
         Library searchedLibrary = null;
         String searchInfo = null;
@@ -2699,7 +2700,9 @@ public abstract class PlayerImpl implements Player, Serializable {
                         (event.isWinnable() ? "(You called " + event.getChosenName() + ")" : null),
                         "Heads", "Tails", source, game
                 ));
-            } else event.setResult(canChooseHeads);
+            } else {
+                event.setResult(canChooseHeads);
+            }
             game.informPlayers(getLogName() + " chose to keep " + CardUtil.booleanToFlipName(event.getResult()));
         }
         if (event.isWinnable()) {
@@ -2721,7 +2724,7 @@ public abstract class PlayerImpl implements Player, Serializable {
     /**
      * @param game
      * @param appliedEffects
-     * @param numSides       Number of sides the dice has
+     * @param numSides Number of sides the dice has
      * @return the number that the player rolled
      */
     @Override
@@ -2758,16 +2761,16 @@ public abstract class PlayerImpl implements Player, Serializable {
     /**
      * @param game
      * @param appliedEffects
-     * @param numberChaosSides  The number of chaos sides the planar die
-     *                          currently has (normally 1 but can be 5)
+     * @param numberChaosSides The number of chaos sides the planar die
+     * currently has (normally 1 but can be 5)
      * @param numberPlanarSides The number of chaos sides the planar die
-     *                          currently has (normally 1)
+     * currently has (normally 1)
      * @return the outcome that the player rolled. Either ChaosRoll, PlanarRoll
      * or NilRoll
      */
     @Override
     public PlanarDieRoll rollPlanarDie(Game game, List<UUID> appliedEffects, int numberChaosSides,
-                                       int numberPlanarSides) {
+            int numberPlanarSides) {
         int result = RandomUtil.nextInt(9) + 1;
         PlanarDieRoll roll = PlanarDieRoll.NIL_ROLL;
         if (numberChaosSides + numberPlanarSides > 9) {
@@ -2924,14 +2927,14 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     /**
      * @param ability
-     * @param available    if null, it won't be checked if enough mana is available
+     * @param available if null, it won't be checked if enough mana is available
      * @param sourceObject
      * @param game
      * @return
      */
     protected boolean canPlay(ActivatedAbility ability, ManaOptions available, MageObject sourceObject, Game game) {
         if (!(ability instanceof ActivatedManaAbilityImpl)) {
-            ActivatedAbility copy = ability.copy(); // copy is needed because cost reduction effects modify e.g. the mana to activate/cast the ability
+            ActivatedAbility copy = ability.copy(); // Copy is needed because cost reduction effects modify e.g. the mana to activate/cast the ability
             if (!copy.canActivate(playerId, game).canActivate()) {
                 return false;
             }
@@ -3117,7 +3120,7 @@ public abstract class PlayerImpl implements Player, Serializable {
     }
 
     protected boolean canLandPlayAlternateSourceCostsAbility(Card sourceObject, ManaOptions available, Ability ability, Game game) {
-        if (!(sourceObject instanceof Permanent)) {
+        if (sourceObject != null && !(sourceObject instanceof Permanent)) {
             Ability sourceAbility = sourceObject.getAbilities().stream()
                     .filter(landAbility -> landAbility.getAbilityType() == AbilityType.PLAY_LAND)
                     .findFirst().orElse(null);
@@ -3158,7 +3161,7 @@ public abstract class PlayerImpl implements Player, Serializable {
     }
 
     private void getPlayableFromCardAll(Game game, Zone fromZone, Card card, ManaOptions availableMana, List<ActivatedAbility> output) {
-        if (fromZone == null) {
+        if (fromZone == null || card == null) {
             return;
         }
 
@@ -3187,7 +3190,6 @@ public abstract class PlayerImpl implements Player, Serializable {
             boolean isPlayLand = (ability instanceof PlayLandAbility);
 
             // as original controller
-
             // play land restrictions
             if (isPlayLand && game.getContinuousEffects().preventedByRuleModification(
                     GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, ability.getSourceId(),
@@ -3221,7 +3223,6 @@ public abstract class PlayerImpl implements Player, Serializable {
                     || (fromZone == Zone.GRAVEYARD && canPlayCardsFromGraveyard());
 
             // as affected controller
-
             UUID savedControllerId = ability.getControllerId();
             ability.setControllerId(this.getId());
             try {
@@ -3620,7 +3621,7 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean canPaySacrificeCost(Permanent permanent, UUID sourceId,
-                                       UUID controllerId, Game game
+            UUID controllerId, Game game
     ) {
         return sacrificeCostFilter == null || !sacrificeCostFilter.match(permanent, sourceId, controllerId, game);
     }
@@ -3773,8 +3774,8 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCards(Card card, Zone toZone,
-                             Ability source, Game game,
-                             boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
+            Ability source, Game game,
+            boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
     ) {
         Set<Card> cardList = new HashSet<>();
         if (card != null) {
@@ -3785,22 +3786,22 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCards(Cards cards, Zone toZone,
-                             Ability source, Game game
+            Ability source, Game game
     ) {
         return moveCards(cards.getCards(game), toZone, source, game);
     }
 
     @Override
     public boolean moveCards(Set<Card> cards, Zone toZone,
-                             Ability source, Game game
+            Ability source, Game game
     ) {
         return moveCards(cards, toZone, source, game, false, false, false, null);
     }
 
     @Override
     public boolean moveCards(Set<Card> cards, Zone toZone,
-                             Ability source, Game game,
-                             boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
+            Ability source, Game game,
+            boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
     ) {
         if (cards.isEmpty()) {
             return true;
@@ -3902,8 +3903,8 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCardsToExile(Card card, Ability source,
-                                    Game game, boolean withName, UUID exileId,
-                                    String exileZoneName
+            Game game, boolean withName, UUID exileId,
+            String exileZoneName
     ) {
         Set<Card> cards = new HashSet<>();
         cards.add(card);
@@ -3912,8 +3913,8 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCardsToExile(Set<Card> cards, Ability source,
-                                    Game game, boolean withName, UUID exileId,
-                                    String exileZoneName
+            Game game, boolean withName, UUID exileId,
+            String exileZoneName
     ) {
         if (cards.isEmpty()) {
             return true;
@@ -3929,14 +3930,14 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCardToHandWithInfo(Card card, UUID sourceId,
-                                          Game game
+            Game game
     ) {
         return this.moveCardToHandWithInfo(card, sourceId, game, true);
     }
 
     @Override
     public boolean moveCardToHandWithInfo(Card card, UUID sourceId,
-                                          Game game, boolean withName
+            Game game, boolean withName
     ) {
         boolean result = false;
         Zone fromZone = game.getState().getZone(card.getId());
@@ -3961,7 +3962,7 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public Set<Card> moveCardsToGraveyardWithInfo(Set<Card> allCards, Ability source,
-                                                  Game game, Zone fromZone
+            Game game, Zone fromZone
     ) {
         UUID sourceId = source == null ? null : source.getSourceId();
         Set<Card> movedCards = new LinkedHashSet<>();
@@ -3969,7 +3970,7 @@ public abstract class PlayerImpl implements Player, Serializable {
             // identify cards from one owner
             Cards cards = new CardsImpl();
             UUID ownerId = null;
-            for (Iterator<Card> it = allCards.iterator(); it.hasNext(); ) {
+            for (Iterator<Card> it = allCards.iterator(); it.hasNext();) {
                 Card card = it.next();
                 if (cards.isEmpty()) {
                     ownerId = card.getOwnerId();
@@ -4032,7 +4033,7 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCardToGraveyardWithInfo(Card card, UUID sourceId,
-                                               Game game, Zone fromZone
+            Game game, Zone fromZone
     ) {
         if (card == null) {
             return false;
@@ -4061,8 +4062,8 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCardToLibraryWithInfo(Card card, UUID sourceId,
-                                             Game game, Zone fromZone,
-                                             boolean toTop, boolean withName
+            Game game, Zone fromZone,
+            boolean toTop, boolean withName
     ) {
         if (card == null) {
             return false;
@@ -4127,7 +4128,7 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCardToExileWithInfo(Card card, UUID exileId, String exileName, UUID sourceId,
-                                           Game game, Zone fromZone, boolean withName) {
+            Game game, Zone fromZone, boolean withName) {
         if (card == null) {
             return false;
         }
@@ -4150,7 +4151,7 @@ public abstract class PlayerImpl implements Player, Serializable {
                 game.informPlayers(this.getLogName() + " moves " + (withName ? card.getLogName()
                         + (card.isCopy() ? " (Copy)" : "") : "a card face down") + ' '
                         + (fromZone != null ? "from " + fromZone.toString().toLowerCase(Locale.ENGLISH)
-                        + ' ' : "") + "to the exile zone");
+                                + ' ' : "") + "to the exile zone");
 
             }
             result = true;
diff --git a/Mage/src/main/java/mage/watchers/common/LostControlWatcher.java b/Mage/src/main/java/mage/watchers/common/LostControlWatcher.java
new file mode 100644
index 0000000000..86f8cf26dd
--- /dev/null
+++ b/Mage/src/main/java/mage/watchers/common/LostControlWatcher.java
@@ -0,0 +1,39 @@
+package mage.watchers.common;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import mage.constants.WatcherScope;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+import mage.watchers.Watcher;
+
+/**
+ *
+ * @author LevelX2
+ */
+public class LostControlWatcher extends Watcher {
+
+    private final Map<UUID, Long> lastLostControl = new HashMap<>();
+
+    public LostControlWatcher() {
+        super(WatcherScope.GAME);
+    }
+
+    @Override
+    public void watch(GameEvent event, Game game) {
+        if (event.getType() == GameEvent.EventType.LOST_CONTROL) {
+            lastLostControl.put(event.getTargetId(), System.currentTimeMillis());
+        }
+    }
+
+    @Override
+    public void reset() {
+        super.reset();
+        lastLostControl.clear();
+    }
+
+    public long getOrderOfLastLostControl(UUID sourceId) {
+        return lastLostControl.getOrDefault(sourceId, new Long(0));
+    }
+}

From 94c8b7a55801f8f83f6d61a0f66922ce0c7e969c Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Wed, 10 Jun 2020 22:37:47 +0200
Subject: [PATCH 197/586] * Some comment formatting.

---
 Mage.Sets/src/mage/cards/t/TheWretched.java   | 42 ++++++++++++-------
 .../cards/copy/LazavDimirMastermindTest.java  | 30 ++++++-------
 ...nBattlefieldControlUnchangedCondition.java |  4 +-
 3 files changed, 41 insertions(+), 35 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/t/TheWretched.java b/Mage.Sets/src/mage/cards/t/TheWretched.java
index 1adfa0bd43..e06617533f 100644
--- a/Mage.Sets/src/mage/cards/t/TheWretched.java
+++ b/Mage.Sets/src/mage/cards/t/TheWretched.java
@@ -29,22 +29,32 @@ import mage.watchers.common.LostControlWatcher;
  *
  * 5/1/2009 The ability grants you control of all creatures that are blocking it
  * as the ability resolves. This will include any creatures that were put onto
- * the battlefield blocking it. 5/1/2009 Any blocking creatures that regenerated
- * during combat will have been removed from combat. Since such creatures are no
- * longer in combat, they cannot be blocking The Wretched, which means you won't
- * be able to gain control of them. 5/1/2009 If The Wretched itself regenerated
- * during combat, then it will have been removed from combat. Since it is no
- * longer in combat, there cannot be any creatures blocking it, which means you
- * won't be able to gain control of any creatures. 10/1/2009 The Wretched's
- * ability triggers only if it's still on the battlefield when the end of combat
- * step begins (after the combat damage step). For example, if it's blocked by a
- * 7/7 creature and is destroyed, its ability won't trigger at all. 10/1/2009 If
- * The Wretched leaves the battlefield, you no longer control it, so the
- * duration of its control-change effect ends. 10/1/2009 If you lose control of
- * The Wretched before its ability resolves, you won't gain control of the
- * creatures blocking it at all. 10/1/2009 Once the ability resolves, it doesn't
- * care whether the permanents you gained control of remain creatures, only that
- * they remain on the battlefield.
+ * the battlefield blocking it.
+ *
+ * 5/1/2009 Any blocking creatures that regenerated during combat will have been
+ * removed from combat. Since such creatures are no longer in combat, they
+ * cannot be blocking The Wretched, which means you won't be able to gain
+ * control of them.
+ *
+ * 5/1/2009 If The Wretched itself regenerated during combat, then it will have
+ * been removed from combat. Since it is no longer in combat, there cannot be
+ * any creatures blocking it, which means you won't be able to gain control of
+ * any creatures.
+ *
+ * 10/1/2009 The Wretched's ability triggers only if it's still on the
+ * battlefield when the end of combat step begins (after the combat damage
+ * step). For example, if it's blocked by a 7/7 creature and is destroyed, its
+ * ability won't trigger at all.
+ *
+ * 10/1/2009 If The Wretched leaves the battlefield, you no longer control it,
+ * so the duration of its control-change effect ends.
+ *
+ * 10/1/2009 If you lose control of The Wretched before its ability resolves,
+ * you won't gain control of the creatures blocking it at all.
+ *
+ * 10/1/2009 Once the ability resolves, it doesn't care whether the permanents
+ * you gained control of remain creatures, only that they remain on the
+ * battlefield.
  */
 public final class TheWretched extends CardImpl {
 
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/LazavDimirMastermindTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/LazavDimirMastermindTest.java
index c2bf7afa6e..f84140e942 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/LazavDimirMastermindTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/LazavDimirMastermindTest.java
@@ -10,17 +10,14 @@ import org.junit.Assert;
 import org.junit.Test;
 import org.mage.test.serverside.base.CardTestPlayerBase;
 
-/**
- *
- * Lazav, Dimir Mastermind
- *
- * Legendary Creature — Shapeshifter 3/3, UUBB Hexproof Whenever a creature card
- * is put into an opponent's graveyard from anywhere, you may have Lazav, Dimir
- * Mastermind become a copy of that card except its name is still Lazav, Dimir
- * Mastermind, it's legendary in addition to its other types, and it gains
- * hexproof and this ability.
- *
- * @author LevelX2
+/*
+    Lazav, Dimir Mastermind
+    Legendary Creature — Shapeshifter 3/3, UUBB
+    Hexproof Whenever a creature card is put into an opponent's graveyard from anywhere, you may have
+    Lazav, Dimir Mastermind become a copy of that card except its name is still Lazav, Dimir Mastermind,
+    it's legendary in addition to its other types, and it gains hexproof and this ability.
+
+    @author LevelX2
  */
 public class LazavDimirMastermindTest extends CardTestPlayerBase {
 
@@ -83,14 +80,13 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase {
 
     }
 
-    /**
+    /*
      * Tests copy Nightveil Specter
      *
-     * Nightveil Specter Creature — Specter 2/3, {U/B}{U/B}{U/B} Flying Whenever
-     * Nightveil Specter deals combat damage to a player, that player exiles the
-     * top card of their library. You may play cards exiled with Nightveil
-     * Specter.
-     *
+     * Nightveil Specter Creature — Specter 2/3, {U/B}{U/B}{U/B}
+     * Flying
+     * Whenever Nightveil Specter deals combat damage to a player, that player exiles the
+     * top card of their library. You may play cards exiled with Nightveil Specter.
      */
     @Test
     public void testCopyNightveilSpecter() {
diff --git a/Mage/src/main/java/mage/abilities/condition/common/SourceOnBattlefieldControlUnchangedCondition.java b/Mage/src/main/java/mage/abilities/condition/common/SourceOnBattlefieldControlUnchangedCondition.java
index d1e9235db0..64f600e444 100644
--- a/Mage/src/main/java/mage/abilities/condition/common/SourceOnBattlefieldControlUnchangedCondition.java
+++ b/Mage/src/main/java/mage/abilities/condition/common/SourceOnBattlefieldControlUnchangedCondition.java
@@ -12,10 +12,10 @@ import mage.watchers.common.LostControlWatcher;
  * Monitoring the LOST_CONTROL event has the advantage that also all layered
  * effects can correctly check for controller change because comparing old and
  * new controller during their apply time does not take into account layered
- * cahnge control effects that will be applied later.
+ * change control effects that will be applied later.
  *
  * This condition needs the LostControlWatcher, so be sure to add it to the card
- * that uses the condition
+ * that uses the condition.
  *
  * @author LevelX2
  */

From d151c07241bb2732f1fe68c67a81e4a3cef6360d Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Wed, 10 Jun 2020 19:34:11 -0400
Subject: [PATCH 198/586] updated M21 spoiler and reprints

---
 Mage.Sets/src/mage/sets/CoreSet2021.java |  3 +++
 Utils/mtg-cards-data.txt                 | 10 ++++++++++
 2 files changed, 13 insertions(+)

diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 789bf68f63..9943cb1fbc 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -59,9 +59,11 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Faith's Fetters", 17, Rarity.UNCOMMON, mage.cards.f.FaithsFetters.class));
         cards.add(new SetCardInfo("Fierce Empath", 181, Rarity.UNCOMMON, mage.cards.f.FierceEmpath.class));
         cards.add(new SetCardInfo("Fungal Rebirth", 182, Rarity.UNCOMMON, mage.cards.f.FungalRebirth.class));
+        cards.add(new SetCardInfo("Furious Rise", 144, Rarity.UNCOMMON, mage.cards.f.FuriousRise.class));
         cards.add(new SetCardInfo("Gadrak, the Crown-Scourge", 146, Rarity.RARE, mage.cards.g.GadrakTheCrownScourge.class));
         cards.add(new SetCardInfo("Garruk's Warsteed", 337, Rarity.RARE, mage.cards.g.GarruksWarsteed.class));
         cards.add(new SetCardInfo("Garruk, Savage Herald", 336, Rarity.MYTHIC, mage.cards.g.GarrukSavageHerald.class));
+        cards.add(new SetCardInfo("Goblin Arsonist", 147, Rarity.COMMON, mage.cards.g.GoblinArsonist.class));
         cards.add(new SetCardInfo("Grasp of Darkness", 102, Rarity.COMMON, mage.cards.g.GraspOfDarkness.class));
         cards.add(new SetCardInfo("Grim Tutor", 103, Rarity.MYTHIC, mage.cards.g.GrimTutor.class));
         cards.add(new SetCardInfo("Havoc Jester", 149, Rarity.UNCOMMON, mage.cards.h.HavocJester.class));
@@ -97,6 +99,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Rain of Revelation", 61, Rarity.UNCOMMON, mage.cards.r.RainOfRevelation.class));
         cards.add(new SetCardInfo("Revitalize", 31, Rarity.COMMON, mage.cards.r.Revitalize.class));
         cards.add(new SetCardInfo("Rewind", 63, Rarity.UNCOMMON, mage.cards.r.Rewind.class));
+        cards.add(new SetCardInfo("Riddleform", 64, Rarity.UNCOMMON, mage.cards.r.Riddleform.class));
         cards.add(new SetCardInfo("Rin and Seri, Inseparable", 278, Rarity.MYTHIC, mage.cards.r.RinAndSeriInseparable.class));
         cards.add(new SetCardInfo("Runed Halo", 32, Rarity.RARE, mage.cards.r.RunedHalo.class));
         cards.add(new SetCardInfo("Seasoned Hallowblade", 34, Rarity.UNCOMMON, mage.cards.s.SeasonedHallowblade.class));
diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt
index 5b279971e4..444e501e73 100644
--- a/Utils/mtg-cards-data.txt
+++ b/Utils/mtg-cards-data.txt
@@ -37455,10 +37455,13 @@ Runed Halo|Core Set 2021|32|R|{W}{W}|Enchantment|||As Runed Halo enters the batt
 Seasoned Hallowblade|Core Set 2021|34|U|{1}{W}|Creature - Human Warrior|3|1|Discard a card: Tap Seasoned Hallowblade. It gains indestructible until end of turn.|
 Selfless Savior|Core Set 2021|36|U|{W}|Creature - Dog|1|1|Sacrifice Selfless Savior: Another target creature gains indestructible until end of turn.|
 Siege Striker|Core Set 2021|37|U|{2}{W}|Creature - Human Soldier|1|1|Double strike$Whenever Siege Striker attacks, you may tap any number of untapped creatures you control. Siege Striker gets +1/+1 until end of turn for each creature tapped this way.|
+Valorous Steed|Core Set 2021|42|C|{4}{W}|Creature - Unicorn|3|3|Vigilance$When Valorous Steed enters the battlefield, create a 2/2 white Knight creature token with vigilance.|
+Frantic Inventory|Core Set 2021|50|C|{1}{U}|Instant|||Draw a card, then draw cards equal to the number of cards named Frantic Inventory in your graveyard.|
 Jeskai Elder|Core Set 2021|53|U|{1}{U}|Creature - Human Monk|1|2|Prowess$Whenever Jeskai Elder deals combat damage to a player, you may draw a card. If you do, discard a card.|
 Pursued Whale|Core Set 2021|60|R|{5}{U}{U}|Creature - Whale|8|8|When Pursued Whale enters the battlefield, each opponent creates a 1/1 red Pirate creature token with "This creature can't block" and "Creatures you control attack each combat if able."$Spells your opponents cast that target Pursued Whale cost {3} more to cast.|
 Rain of Revelation|Core Set 2021|61|U|{3}{U}|Instant|||Draw three cards, then discard a card.|
 Rewind|Core Set 2021|63|U|{2}{U}{U}|Instant|||Counter target spell. Untap up to four lands.|
+Riddleform|Core Set 2021|64|U|{1}{U}|Enchantment|||Whenever you cast a noncreature spell, you may have Riddleform become a 3/3 Sphinx creature with flying in addition to its other types until end of turn.${2}{U}: Scry 1.|
 See the Truth|Core Set 2021|69|R|{1}{U}|Sorcery|||Look at the top three cards of your library. Put one of those cards into your hand and the rest on the bottom of your library in any order. If this spell was cast from anywhere other than your hand, put each of those cards into your hand instead.|
 Shipwreck Dowser|Core Set 2021|71|U|{3}{U}{U}|Creature - Merfolk Wizard|3|3|Prowess$When Shipwreck Dowser enters the battlefield, return target instant or sorcery card from your graveyard to your hand.|
 Spined Megalodon|Core Set 2021|72|C|{5}{U}{U}|Creature - Shark|5|7|Hexproof$Whenever Spined Megalodon attacks, scry 1.|
@@ -37487,11 +37490,18 @@ Chandra, Heart of Fire|Core Set 2021|135|M|{3}{R}{R}|Legendary Planeswalker - Ch
 Chandra's Incinerator|Core Set 2021|136|R|{5}{R}|Creature - Elemental|6|6|This spell costs {X} less to cast, where X is the total amount of noncombat damage dealt to your opponents this turn.$Trample$Whenever a source you control deals noncombat damage to an opponent, Chandra's Incinerator deals that much damage to target creature or planeswalker that player controls.|
 Chandra's Magmutt|Core Set 2021|137|C|{1}{R}|Creature - Elemental Dog|2|2|{T}: Chandra's Magmutt deals 1 damage to target player or planeswalker.|
 Chandra's Pyreling|Core Set 2021|138|U|{1}{R}|Creature - Elemental Lizard|1|3|Whenever a source you control deals noncombat damage to an opponent, Chandra's Pyreling gets +1/+0 and gains double strike until end of turn.|
+Conspicuous Snoop|Core Set 2021|139|R|{R}{R}|Creature - Goblin Rogue|2|2|Play with the top card of your library revealed.$You may cast Goblin spells from the top of your library.$As long as the top card of your library is a Goblin card, Conspicuous Snoop has all activated abilities of that card.|
 Double Vision|Core Set 2021|142|R|{3}{R}{R}|Enchantment|||Whenever you cast your first instant or sorcery spell each turn, copy that spell. You may choose new targets for the copy.|
+Fiery Emancipation|Core Set 2021|143|M|{3}{R}{R}{R}|Enchantment|||If a source you control would deal damage to a permanent or player, it deals triple that damage to that permanent or player instead.|
+Furious Rise|Core Set 2021|144|U|{2}{R}|Enchantment|||At the beginning of your end step, if you control a creature with power 4 or greater, exile the top card of your library. You may play that card until you exile another card with Furious Rise.|
 Gadrak, the Crown-Scourge|Core Set 2021|146|R|{2}{R}|Legendary Creature - Dragon|5|4|Flying$Gadrak, the Crown-Scourge can't attack unless you control four or more artifacts.$At the beginning of your end step, create a Treasure token for each nontoken creature that died this turn.|
+Goblin Arsonist|Core Set 2021|147|C|{R}|Creature - Goblin Shaman|1|1|When Goblin Arsonist dies, you may have it deal 1 damage to any target.|
 Havoc Jester|Core Set 2021|149|U|{4}{R}|Creature - Devil|5|5|Whenever you sacrifice a permanent, Havoc Jester deals 1 damage to any target.|
+Heartfire Immolator|Core Set 2021|150|U|{1}{R}|Creature - Human Wizard|2|2|Prowess${R}, Sacrifice Heartfire Immolator: It deals damage equal to its power to target creature or planeswalker.|
+Hellkite Punisher|Core Set 2021|151|U|{5}{R}{R}|Creature - Dragon|6|6|Flying${R}: Hellkite Punisher gets +1/+0 until end of turn.|
 Kinetic Augur|Core Set 2021|154|U|{3}{R}|Creature - Human Shaman|*|4|Trample$Kinetic Augur's power is equal to the number of instant and sorcery cards in your graveyard.$When Kinetic Augur enters the battlefield, discard up to two cards, then draw that many cards.|
 Soul Sear|Core Set 2021|160|U|{2}{R}|Instant|||Soul Sear deals 5 damage to target creature or planeswalker. That permanent loses indestructible until end of turn.|
+Transmogrify|Core Set 2021|167|R|{3}{R}|Sorcery|||Exile target creature. That creature's controller reveals cards from the top of their library until they reveal a creature card. That player puts that card onto the battlefield, then shuffles the rest into their library.|
 Volcanic Salvo|Core Set 2021|172|R|{10}{R}{R}|Sorcery|||This spell costs {X} less to cast, where X is the total power of creatures you control.$Volcanic Salvo deals 6 damage to each of up to two target creatures and/or planeswalkers.|
 Azusa, Lost but Seeking|Core Set 2021|173|R|{2}{G}|Legendary Creature - Human Monk|1|2|You may play two additional lands on each of your turns.|
 Cultivate|Core Set 2021|177|U|{2}{G}|Sorcery|||Search your library for up to two basic land cards, reveal those cards, and put one onto the battlefield tapped and the other into your hand. Then shuffle your library.|

From ab2d9d5b1afaeec05e5071f90ea382cb25a8c8ca Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Wed, 10 Jun 2020 19:37:20 -0400
Subject: [PATCH 199/586] Implemented Hellkite Punisher

---
 .../src/mage/cards/h/HellkitePunisher.java    | 45 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 46 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/h/HellkitePunisher.java

diff --git a/Mage.Sets/src/mage/cards/h/HellkitePunisher.java b/Mage.Sets/src/mage/cards/h/HellkitePunisher.java
new file mode 100644
index 0000000000..f6509448db
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/h/HellkitePunisher.java
@@ -0,0 +1,45 @@
+package mage.cards.h;
+
+import mage.MageInt;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.effects.common.continuous.BoostSourceEffect;
+import mage.abilities.keyword.FlyingAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class HellkitePunisher extends CardImpl {
+
+    public HellkitePunisher(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{R}{R}");
+
+        this.subtype.add(SubType.DRAGON);
+        this.power = new MageInt(6);
+        this.toughness = new MageInt(6);
+
+        // Flying
+        this.addAbility(FlyingAbility.getInstance());
+
+        // {R}: Hellkite Punisher gets +1/+0 until end of turn.
+        this.addAbility(new SimpleActivatedAbility(
+                new BoostSourceEffect(1, 0, Duration.EndOfTurn), new ManaCostsImpl("{R}")
+        ));
+    }
+
+    private HellkitePunisher(final HellkitePunisher card) {
+        super(card);
+    }
+
+    @Override
+    public HellkitePunisher copy() {
+        return new HellkitePunisher(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 9943cb1fbc..55cd8b2159 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -67,6 +67,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Grasp of Darkness", 102, Rarity.COMMON, mage.cards.g.GraspOfDarkness.class));
         cards.add(new SetCardInfo("Grim Tutor", 103, Rarity.MYTHIC, mage.cards.g.GrimTutor.class));
         cards.add(new SetCardInfo("Havoc Jester", 149, Rarity.UNCOMMON, mage.cards.h.HavocJester.class));
+        cards.add(new SetCardInfo("Hellkite Punisher", 151, Rarity.UNCOMMON, mage.cards.h.HellkitePunisher.class));
         cards.add(new SetCardInfo("Heroic Intervention", 188, Rarity.RARE, mage.cards.h.HeroicIntervention.class));
         cards.add(new SetCardInfo("Historian of Zhalfir", 325, Rarity.UNCOMMON, mage.cards.h.HistorianOfZhalfir.class));
         cards.add(new SetCardInfo("Indulging Patrician", 219, Rarity.UNCOMMON, mage.cards.i.IndulgingPatrician.class));

From dbb860d14f3cc5189436c7ffaaf927cd0e556120 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Wed, 10 Jun 2020 19:45:07 -0400
Subject: [PATCH 200/586] Implemented Valorous Steed

---
 Mage.Sets/src/mage/cards/v/ValorousSteed.java | 42 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 43 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/v/ValorousSteed.java

diff --git a/Mage.Sets/src/mage/cards/v/ValorousSteed.java b/Mage.Sets/src/mage/cards/v/ValorousSteed.java
new file mode 100644
index 0000000000..e704aa72e9
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/v/ValorousSteed.java
@@ -0,0 +1,42 @@
+package mage.cards.v;
+
+import mage.MageInt;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.effects.common.CreateTokenEffect;
+import mage.abilities.keyword.VigilanceAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.game.permanent.token.KnightToken;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class ValorousSteed extends CardImpl {
+
+    public ValorousSteed(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}");
+
+        this.subtype.add(SubType.UNICORN);
+        this.power = new MageInt(3);
+        this.toughness = new MageInt(3);
+
+        // Vigilance
+        this.addAbility(VigilanceAbility.getInstance());
+
+        // When Valorous Steed enters the battlefield, create a 2/2 white Knight creature token with vigilance.
+        this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new KnightToken())));
+    }
+
+    private ValorousSteed(final ValorousSteed card) {
+        super(card);
+    }
+
+    @Override
+    public ValorousSteed copy() {
+        return new ValorousSteed(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 55cd8b2159..cf85fdb7ab 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -127,6 +127,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Tide Skimmer", 79, Rarity.UNCOMMON, mage.cards.t.TideSkimmer.class));
         cards.add(new SetCardInfo("Tormod's Crypt", 241, Rarity.UNCOMMON, mage.cards.t.TormodsCrypt.class));
         cards.add(new SetCardInfo("Ugin, the Spirit Dragon", 1, Rarity.MYTHIC, mage.cards.u.UginTheSpiritDragon.class));
+        cards.add(new SetCardInfo("Valorous Steed", 42, Rarity.COMMON, mage.cards.v.ValorousSteed.class));
         cards.add(new SetCardInfo("Village Rites", 126, Rarity.COMMON, mage.cards.v.VillageRites.class));
         cards.add(new SetCardInfo("Vito, Thorn of the Dusk Rose", 127, Rarity.RARE, mage.cards.v.VitoThornOfTheDuskRose.class));
         cards.add(new SetCardInfo("Wildwood Patrol", 339, Rarity.COMMON, mage.cards.w.WildwoodPatrol.class));

From 6414b02995a72d7ddc7da04dfd635890c7fb42df Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Wed, 10 Jun 2020 19:48:41 -0400
Subject: [PATCH 201/586] Implemented Alpine Watchdog

---
 .../src/mage/cards/a/AlpineWatchdog.java      | 36 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 Utils/mtg-cards-data.txt                      |  1 +
 3 files changed, 38 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/a/AlpineWatchdog.java

diff --git a/Mage.Sets/src/mage/cards/a/AlpineWatchdog.java b/Mage.Sets/src/mage/cards/a/AlpineWatchdog.java
new file mode 100644
index 0000000000..65c6d9bdb4
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/a/AlpineWatchdog.java
@@ -0,0 +1,36 @@
+package mage.cards.a;
+
+import mage.MageInt;
+import mage.abilities.keyword.VigilanceAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class AlpineWatchdog extends CardImpl {
+
+    public AlpineWatchdog(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}");
+
+        this.subtype.add(SubType.DOG);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(2);
+
+        // Vigilance
+        this.addAbility(VigilanceAbility.getInstance());
+    }
+
+    private AlpineWatchdog(final AlpineWatchdog card) {
+        super(card);
+    }
+
+    @Override
+    public AlpineWatchdog copy() {
+        return new AlpineWatchdog(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index cf85fdb7ab..1f7798f761 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -35,6 +35,7 @@ public final class CoreSet2021 extends ExpansionSet {
 
         cards.add(new SetCardInfo("Adherent of Hope", 321, Rarity.COMMON, mage.cards.a.AdherentOfHope.class));
         cards.add(new SetCardInfo("Alpine Houndmaster", 215, Rarity.UNCOMMON, mage.cards.a.AlpineHoundmaster.class));
+        cards.add(new SetCardInfo("Alpine Watchdog", 2, Rarity.COMMON, mage.cards.a.AlpineWatchdog.class));
         cards.add(new SetCardInfo("Angelic Ascension", 3, Rarity.UNCOMMON, mage.cards.a.AngelicAscension.class));
         cards.add(new SetCardInfo("Azusa, Lost but Seeking", 173, Rarity.RARE, mage.cards.a.AzusaLostButSeeking.class));
         cards.add(new SetCardInfo("Bad Deal", 89, Rarity.UNCOMMON, mage.cards.b.BadDeal.class));
diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt
index 444e501e73..2bfaf0016d 100644
--- a/Utils/mtg-cards-data.txt
+++ b/Utils/mtg-cards-data.txt
@@ -37439,6 +37439,7 @@ Mysterious Egg|Ikoria: Lair of Behemoths|385|C|{1}|Creature - Egg|0|2|Whenever t
 Dirge Bat|Ikoria: Lair of Behemoths|386|R|{2}{B}{B}|Creature - Bat|3|3|Mutate {4}{B}{B}$Flash$Flying$Whenever this creature mutates, destroy target creature or planeswalker an opponent controls.|
 Crystalline Giant|Ikoria: Lair of Behemoths|387|R|{3}|Artifact Creature - Giant|3|3|At the beginning of combat on your turn, choose a kind of counter at random that Crystalline Giant doesn't have on it from among flying, first strike, deathtouch, hexproof, lifelink, menace, reach, trample, vigilance, or +1/+1. Put a counter of that kind on Crystalline Giant.|
 Ugin, the Spirit Dragon|Core Set 2021|1|M|{8}|Legendary Planeswalker - Ugin|7|+2: Ugin, the Spirit Dragon deals 3 damage to any target.$−X: Exile each permanent with converted mana cost X or less that's one or more colors.$−10: You gain 7 life, draw seven cards, then put up to seven permanent cards from your hand onto the battlefield.|
+Alpine Watchdog|Core Set 2021|2|C|{1}{W}|Creature - Dog|2|2|Vigilance|
 Angelic Ascension|Core Set 2021|3|U|{1}{W}|Instant|||Exile target creature or planeswalker. Its controller creates a 4/4 white Angel creature token with flying.|
 Baneslayer Angel|Core Set 2021|6|M|{3}{W}{W}|Creature - Angel|5|5|Flying, first strike, lifelink, protection from Demons and from Dragons|
 Basri Ket|Core Set 2021|7|M|{1}{W}{W}|Legendary Planeswalker - Basri|3|+1: Put a +1/+1 counter on up to one target creature. It gains indestructible until end of turn.$−2: Whenever one or more nontoken creatures attack this turn, create that many 1/1 white Soldier creature tokens that are tapped and attacking.$−6: You get an emblem with "At the beginning of combat on your turn, create a 1/1 white Soldier creature token, then put a +1/+1 counter on each creature you control."|

From 686d6e1777829b2690b08ab54d6d7ddfa715c417 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Wed, 10 Jun 2020 19:50:12 -0400
Subject: [PATCH 202/586] Implemented Igneous Cur

---
 Mage.Sets/src/mage/cards/i/IgneousCur.java | 42 ++++++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java   |  1 +
 Utils/mtg-cards-data.txt                   |  1 +
 3 files changed, 44 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/i/IgneousCur.java

diff --git a/Mage.Sets/src/mage/cards/i/IgneousCur.java b/Mage.Sets/src/mage/cards/i/IgneousCur.java
new file mode 100644
index 0000000000..9026e996ce
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/i/IgneousCur.java
@@ -0,0 +1,42 @@
+package mage.cards.i;
+
+import mage.MageInt;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.effects.common.continuous.BoostSourceEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class IgneousCur extends CardImpl {
+
+    public IgneousCur(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}");
+
+        this.subtype.add(SubType.ELEMENTAL);
+        this.subtype.add(SubType.DOG);
+        this.power = new MageInt(1);
+        this.toughness = new MageInt(2);
+
+        // {1}{R}: Igneous Cur gets +2/+0 until end of turn.
+        this.addAbility(new SimpleActivatedAbility(
+                new BoostSourceEffect(2, 0, Duration.EndOfTurn), new ManaCostsImpl("{2}{R}")
+        ));
+    }
+
+    private IgneousCur(final IgneousCur card) {
+        super(card);
+    }
+
+    @Override
+    public IgneousCur copy() {
+        return new IgneousCur(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 1f7798f761..071c212fdc 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -71,6 +71,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Hellkite Punisher", 151, Rarity.UNCOMMON, mage.cards.h.HellkitePunisher.class));
         cards.add(new SetCardInfo("Heroic Intervention", 188, Rarity.RARE, mage.cards.h.HeroicIntervention.class));
         cards.add(new SetCardInfo("Historian of Zhalfir", 325, Rarity.UNCOMMON, mage.cards.h.HistorianOfZhalfir.class));
+        cards.add(new SetCardInfo("Igneous Cur", 153, Rarity.COMMON, mage.cards.i.IgneousCur.class));
         cards.add(new SetCardInfo("Indulging Patrician", 219, Rarity.UNCOMMON, mage.cards.i.IndulgingPatrician.class));
         cards.add(new SetCardInfo("Island", 310, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Jeskai Elder", 53, Rarity.UNCOMMON, mage.cards.j.JeskaiElder.class));
diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt
index 2bfaf0016d..ce2c88ba88 100644
--- a/Utils/mtg-cards-data.txt
+++ b/Utils/mtg-cards-data.txt
@@ -37500,6 +37500,7 @@ Goblin Arsonist|Core Set 2021|147|C|{R}|Creature - Goblin Shaman|1|1|When Goblin
 Havoc Jester|Core Set 2021|149|U|{4}{R}|Creature - Devil|5|5|Whenever you sacrifice a permanent, Havoc Jester deals 1 damage to any target.|
 Heartfire Immolator|Core Set 2021|150|U|{1}{R}|Creature - Human Wizard|2|2|Prowess${R}, Sacrifice Heartfire Immolator: It deals damage equal to its power to target creature or planeswalker.|
 Hellkite Punisher|Core Set 2021|151|U|{5}{R}{R}|Creature - Dragon|6|6|Flying${R}: Hellkite Punisher gets +1/+0 until end of turn.|
+Igneous Cur|Core Set 2021|153|C|{1}{R}|Creature - Elemental Dog|1|2|{1}{R}: Igneous Cur gets +2/+0 until end of turn.|
 Kinetic Augur|Core Set 2021|154|U|{3}{R}|Creature - Human Shaman|*|4|Trample$Kinetic Augur's power is equal to the number of instant and sorcery cards in your graveyard.$When Kinetic Augur enters the battlefield, discard up to two cards, then draw that many cards.|
 Soul Sear|Core Set 2021|160|U|{2}{R}|Instant|||Soul Sear deals 5 damage to target creature or planeswalker. That permanent loses indestructible until end of turn.|
 Transmogrify|Core Set 2021|167|R|{3}{R}|Sorcery|||Exile target creature. That creature's controller reveals cards from the top of their library until they reveal a creature card. That player puts that card onto the battlefield, then shuffles the rest into their library.|

From 7cf52ce1369a5b6020b1ac220a042934191c3003 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Wed, 10 Jun 2020 20:21:03 -0400
Subject: [PATCH 203/586] Implemented Pursued Whale

---
 Mage.Sets/src/mage/cards/p/PursuedWhale.java  | 120 ++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |   1 +
 .../permanent/token/PursuedWhaleToken.java    |  47 +++++++
 3 files changed, 168 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/p/PursuedWhale.java
 create mode 100644 Mage/src/main/java/mage/game/permanent/token/PursuedWhaleToken.java

diff --git a/Mage.Sets/src/mage/cards/p/PursuedWhale.java b/Mage.Sets/src/mage/cards/p/PursuedWhale.java
new file mode 100644
index 0000000000..fd9ca15ab8
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/p/PursuedWhale.java
@@ -0,0 +1,120 @@
+package mage.cards.p;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.Mode;
+import mage.abilities.SpellAbility;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.cost.CostModificationEffectImpl;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.*;
+import mage.game.Game;
+import mage.game.permanent.token.PursuedWhaleToken;
+import mage.game.permanent.token.Token;
+import mage.target.Target;
+import mage.util.CardUtil;
+
+import java.util.Collection;
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class PursuedWhale extends CardImpl {
+
+    public PursuedWhale(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{U}{U}");
+
+        this.subtype.add(SubType.WHALE);
+        this.power = new MageInt(8);
+        this.toughness = new MageInt(8);
+
+        // When Pursued Whale enters the battlefield, each opponent creates a 1/1 red Pirate creature token with "This creature can't block" and "Creatures you control attack each combat if able."
+        this.addAbility(new EntersBattlefieldTriggeredAbility(new PursuedWhaleTokenEffect()));
+
+        // Spells your opponents cast that target Pursued Whale cost {3} more to cast.
+        this.addAbility(new SimpleStaticAbility(new PursuedWhaleCostIncreaseEffect()));
+    }
+
+    private PursuedWhale(final PursuedWhale card) {
+        super(card);
+    }
+
+    @Override
+    public PursuedWhale copy() {
+        return new PursuedWhale(this);
+    }
+}
+
+class PursuedWhaleTokenEffect extends OneShotEffect {
+
+    private static final Token token = new PursuedWhaleToken();
+
+    PursuedWhaleTokenEffect() {
+        super(Outcome.Benefit);
+        staticText = "each opponent creates a 1/1 red Pirate creature token with " +
+                "\"This creature can't block\" and \"Creatures you control attack each combat if able.\"";
+    }
+
+    private PursuedWhaleTokenEffect(final PursuedWhaleTokenEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public PursuedWhaleTokenEffect copy() {
+        return new PursuedWhaleTokenEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        for (UUID playerId : game.getOpponents(source.getControllerId())) {
+            token.putOntoBattlefield(1, game, source.getSourceId(), playerId);
+        }
+        return true;
+    }
+}
+
+class PursuedWhaleCostIncreaseEffect extends CostModificationEffectImpl {
+
+    PursuedWhaleCostIncreaseEffect() {
+        super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST);
+        staticText = "Spells your opponents cast that target {this} cost {3} more to cast";
+    }
+
+    private PursuedWhaleCostIncreaseEffect(PursuedWhaleCostIncreaseEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source, Ability abilityToModify) {
+        SpellAbility spellAbility = (SpellAbility) abilityToModify;
+        CardUtil.adjustCost(spellAbility, -3);
+        return true;
+    }
+
+    @Override
+    public boolean applies(Ability abilityToModify, Ability source, Game game) {
+        if (!(abilityToModify instanceof SpellAbility)
+                || !game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) {
+            return false;
+        }
+        return abilityToModify
+                .getModes()
+                .getSelectedModes()
+                .stream()
+                .map(uuid -> abilityToModify.getModes().get(uuid))
+                .map(Mode::getTargets)
+                .flatMap(Collection::stream)
+                .map(Target::getTargets)
+                .flatMap(Collection::stream)
+                .anyMatch(uuid -> uuid.equals(source.getSourceId()));
+    }
+
+    @Override
+    public PursuedWhaleCostIncreaseEffect copy() {
+        return new PursuedWhaleCostIncreaseEffect(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 071c212fdc..2d8073f853 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -97,6 +97,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Plains", 309, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Predatory Wurm", 338, Rarity.UNCOMMON, mage.cards.p.PredatoryWurm.class));
         cards.add(new SetCardInfo("Pridemalkin", 196, Rarity.COMMON, mage.cards.p.Pridemalkin.class));
+        cards.add(new SetCardInfo("Pursued Whale", 60, Rarity.RARE, mage.cards.p.PursuedWhale.class));
         cards.add(new SetCardInfo("Quirion Dryad", 198, Rarity.UNCOMMON, mage.cards.q.QuirionDryad.class));
         cards.add(new SetCardInfo("Radiant Fountain", 248, Rarity.COMMON, mage.cards.r.RadiantFountain.class));
         cards.add(new SetCardInfo("Rain of Revelation", 61, Rarity.UNCOMMON, mage.cards.r.RainOfRevelation.class));
diff --git a/Mage/src/main/java/mage/game/permanent/token/PursuedWhaleToken.java b/Mage/src/main/java/mage/game/permanent/token/PursuedWhaleToken.java
new file mode 100644
index 0000000000..3363fe182a
--- /dev/null
+++ b/Mage/src/main/java/mage/game/permanent/token/PursuedWhaleToken.java
@@ -0,0 +1,47 @@
+package mage.game.permanent.token;
+
+import mage.MageInt;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.effects.common.combat.AttacksIfAbleAllEffect;
+import mage.abilities.effects.common.combat.CantBlockSourceEffect;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+import mage.constants.TargetController;
+import mage.filter.common.FilterCreaturePermanent;
+
+/**
+ * @author TheElk801
+ */
+public final class PursuedWhaleToken extends TokenImpl {
+
+    private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures you control");
+
+    static {
+        filter.add(TargetController.YOU.getControllerPredicate());
+    }
+
+    public PursuedWhaleToken() {
+        super("Pirate", "1/1 red Pirate creature token with \"This creature can't block\" and \"Creatures you control attack each combat if able.\"");
+        cardType.add(CardType.CREATURE);
+        color.setRed(true);
+        subtype.add(SubType.PIRATE);
+        power = new MageInt(1);
+        toughness = new MageInt(1);
+
+        this.addAbility(new SimpleStaticAbility(new CantBlockSourceEffect(Duration.WhileOnBattlefield)
+                .setText("this creature can't block")));
+        this.addAbility(new SimpleStaticAbility(new AttacksIfAbleAllEffect(
+                filter, Duration.WhileOnBattlefield, true
+        )));
+    }
+
+    private PursuedWhaleToken(final PursuedWhaleToken token) {
+        super(token);
+    }
+
+    @Override
+    public PursuedWhaleToken copy() {
+        return new PursuedWhaleToken(this);
+    }
+}

From eab57783ed71732774059c0eabf10077f2c51eeb Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Wed, 10 Jun 2020 20:38:43 -0400
Subject: [PATCH 204/586] Implemented Warden of the Woods

---
 .../src/mage/cards/w/WardenOfTheWoods.java    | 48 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 .../common/BecomesTargetTriggeredAbility.java |  6 ++-
 3 files changed, 54 insertions(+), 1 deletion(-)
 create mode 100644 Mage.Sets/src/mage/cards/w/WardenOfTheWoods.java

diff --git a/Mage.Sets/src/mage/cards/w/WardenOfTheWoods.java b/Mage.Sets/src/mage/cards/w/WardenOfTheWoods.java
new file mode 100644
index 0000000000..4091621422
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/w/WardenOfTheWoods.java
@@ -0,0 +1,48 @@
+package mage.cards.w;
+
+import mage.MageInt;
+import mage.abilities.common.BecomesTargetTriggeredAbility;
+import mage.abilities.effects.common.DrawCardSourceControllerEffect;
+import mage.abilities.keyword.VigilanceAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SetTargetPointer;
+import mage.constants.SubType;
+import mage.filter.StaticFilters;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class WardenOfTheWoods extends CardImpl {
+
+    public WardenOfTheWoods(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}{G}");
+
+        this.subtype.add(SubType.TREEFOLK);
+        this.power = new MageInt(5);
+        this.toughness = new MageInt(7);
+
+        // Vigilance
+        this.addAbility(VigilanceAbility.getInstance());
+
+        // Whenever Warden of the Woods becomes the target of a spell or ability an opponent controls, you may draw two cards.
+        this.addAbility(new BecomesTargetTriggeredAbility(
+                new DrawCardSourceControllerEffect(2),
+                StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS,
+                SetTargetPointer.NONE, true
+        ));
+
+    }
+
+    private WardenOfTheWoods(final WardenOfTheWoods card) {
+        super(card);
+    }
+
+    @Override
+    public WardenOfTheWoods copy() {
+        return new WardenOfTheWoods(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 2d8073f853..929b1c4316 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -133,6 +133,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Valorous Steed", 42, Rarity.COMMON, mage.cards.v.ValorousSteed.class));
         cards.add(new SetCardInfo("Village Rites", 126, Rarity.COMMON, mage.cards.v.VillageRites.class));
         cards.add(new SetCardInfo("Vito, Thorn of the Dusk Rose", 127, Rarity.RARE, mage.cards.v.VitoThornOfTheDuskRose.class));
+        cards.add(new SetCardInfo("Warden of the Woods", 213, Rarity.UNCOMMON, mage.cards.w.WardenOfTheWoods.class));
         cards.add(new SetCardInfo("Wildwood Patrol", 339, Rarity.COMMON, mage.cards.w.WildwoodPatrol.class));
     }
 }
diff --git a/Mage/src/main/java/mage/abilities/common/BecomesTargetTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesTargetTriggeredAbility.java
index 1833985830..7c945d0a19 100644
--- a/Mage/src/main/java/mage/abilities/common/BecomesTargetTriggeredAbility.java
+++ b/Mage/src/main/java/mage/abilities/common/BecomesTargetTriggeredAbility.java
@@ -28,7 +28,11 @@ public class BecomesTargetTriggeredAbility extends TriggeredAbilityImpl {
     }
 
     public BecomesTargetTriggeredAbility(Effect effect, FilterStackObject filter, SetTargetPointer setTargetPointer) {
-        super(Zone.BATTLEFIELD, effect);
+        this(effect, filter, setTargetPointer, false);
+    }
+
+    public BecomesTargetTriggeredAbility(Effect effect, FilterStackObject filter, SetTargetPointer setTargetPointer, boolean optional) {
+        super(Zone.BATTLEFIELD, effect, optional);
         this.filter = filter.copy();
         this.setTargetPointer = setTargetPointer;
     }

From 0dddfe398982cd747421449b6c7546eb07272ec4 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Wed, 10 Jun 2020 20:43:57 -0400
Subject: [PATCH 205/586] Implemented Heartfire Immolator

---
 .../src/mage/cards/h/HeartfireImmolator.java  | 55 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 56 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/h/HeartfireImmolator.java

diff --git a/Mage.Sets/src/mage/cards/h/HeartfireImmolator.java b/Mage.Sets/src/mage/cards/h/HeartfireImmolator.java
new file mode 100644
index 0000000000..89ca31facb
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/h/HeartfireImmolator.java
@@ -0,0 +1,55 @@
+package mage.cards.h;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.common.SacrificeSourceCost;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.dynamicvalue.DynamicValue;
+import mage.abilities.dynamicvalue.common.SourcePermanentPowerCount;
+import mage.abilities.effects.common.DamageTargetEffect;
+import mage.abilities.keyword.ProwessAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.target.common.TargetCreatureOrPlaneswalker;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class HeartfireImmolator extends CardImpl {
+
+    private static final DynamicValue xValue = new SourcePermanentPowerCount(false);
+
+    public HeartfireImmolator(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}");
+
+        this.subtype.add(SubType.HUMAN);
+        this.subtype.add(SubType.WIZARD);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(2);
+
+        // Prowess
+        this.addAbility(new ProwessAbility());
+
+        // {R}, Sacrifice Heartfire Immolator: It deals damage equal to its power to target creature or planeswalker.
+        Ability ability = new SimpleActivatedAbility(
+                new DamageTargetEffect(xValue, "it"), new ManaCostsImpl("{R}")
+        );
+        ability.addCost(new SacrificeSourceCost());
+        ability.addTarget(new TargetCreatureOrPlaneswalker());
+        this.addAbility(ability);
+    }
+
+    private HeartfireImmolator(final HeartfireImmolator card) {
+        super(card);
+    }
+
+    @Override
+    public HeartfireImmolator copy() {
+        return new HeartfireImmolator(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 929b1c4316..951c9c1a31 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -68,6 +68,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Grasp of Darkness", 102, Rarity.COMMON, mage.cards.g.GraspOfDarkness.class));
         cards.add(new SetCardInfo("Grim Tutor", 103, Rarity.MYTHIC, mage.cards.g.GrimTutor.class));
         cards.add(new SetCardInfo("Havoc Jester", 149, Rarity.UNCOMMON, mage.cards.h.HavocJester.class));
+        cards.add(new SetCardInfo("Heartfire Immolator", 150, Rarity.UNCOMMON, mage.cards.h.HeartfireImmolator.class));
         cards.add(new SetCardInfo("Hellkite Punisher", 151, Rarity.UNCOMMON, mage.cards.h.HellkitePunisher.class));
         cards.add(new SetCardInfo("Heroic Intervention", 188, Rarity.RARE, mage.cards.h.HeroicIntervention.class));
         cards.add(new SetCardInfo("Historian of Zhalfir", 325, Rarity.UNCOMMON, mage.cards.h.HistorianOfZhalfir.class));

From 36b31d097ea0958796bf247716e70dc664b5ca5b Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Wed, 10 Jun 2020 22:38:59 -0400
Subject: [PATCH 206/586] reinstated new mill wording

---
 .../cards/copy/LazavDimirMastermindTest.java  | 22 +++++++++----------
 .../cards/copy/VolrathsShapshifterTest.java   |  7 +++---
 .../PutLibraryIntoGraveTargetEffect.java      |  8 +++----
 3 files changed, 18 insertions(+), 19 deletions(-)

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/LazavDimirMastermindTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/LazavDimirMastermindTest.java
index f84140e942..36bcc01fbe 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/LazavDimirMastermindTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/LazavDimirMastermindTest.java
@@ -28,7 +28,7 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase {
     public void testCopySimpleCreature() {
         addCard(Zone.BATTLEFIELD, playerA, "Lazav, Dimir Mastermind", 1);
         // Codex Shredder - Artifact
-        // {T}: Target player puts the top card of their library into their graveyard.
+        // {T}: Target player mills a card.
         // {5}, {T}, Sacrifice Codex Shredder: Return target card from your graveyard to your hand.
         addCard(Zone.BATTLEFIELD, playerA, "Codex Shredder", 1);
 
@@ -36,11 +36,10 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase {
         addCard(Zone.LIBRARY, playerB, "Assault Griffin", 5);
         skipInitShuffling();
 
-        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player puts the top card", playerB);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills a card.", playerB);
 
         setStopAt(1, PhaseStep.END_TURN);
         execute();
-        assertAllCommandsUsed();
 
         assertPermanentCount(playerA, "Lazav, Dimir Mastermind", 1);
         assertPowerToughness(playerA, "Lazav, Dimir Mastermind", 3, 2);
@@ -64,7 +63,7 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase {
         addCard(Zone.LIBRARY, playerB, "Ogre Slumlord", 5);
         skipInitShuffling();
 
-        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player puts the top card", playerB);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills a card.", playerB);
 
         setStopAt(1, PhaseStep.END_TURN);
         execute();
@@ -98,7 +97,7 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase {
         addCard(Zone.LIBRARY, playerB, "Nightveil Specter", 1);
         skipInitShuffling();
 
-        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player puts the top card", playerB);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills a card.", playerB);
 
         attack(3, playerA, "Lazav, Dimir Mastermind");
 
@@ -130,10 +129,10 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase {
         skipInitShuffling();
 
         // Lazav becomes a Nightveil Specter
-        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player puts the top card", playerB);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills a card.", playerB);
 
         // Lazav becomes a Silvercoat Lion
-        activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player puts the top card", playerB);
+        activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills a card.", playerB);
 
         setStopAt(3, PhaseStep.END_TURN);
         execute();
@@ -157,7 +156,7 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase {
         addCard(Zone.HAND, playerA, "Reanimate");
         addCard(Zone.BATTLEFIELD, playerA, "Lazav, Dimir Mastermind", 1);
         // Codex Shredder - Artifact
-        // {T}: Target player puts the top card of their library into their graveyard.
+        // {T}: Target player mills a card.
         // {5}, {T}, Sacrifice Codex Shredder: Return target card from your graveyard to your hand.
         addCard(Zone.BATTLEFIELD, playerA, "Codex Shredder", 1);
 
@@ -169,7 +168,7 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase {
 
         skipInitShuffling();
 
-        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player puts the top card", playerB);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills a card.", playerB);
 
         castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Tribute to Hunger");
 
@@ -190,6 +189,7 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase {
         Assert.assertFalse(lazav.getSubtype(currentGame).contains(SubType.GRIFFIN)); // no Griffin type
         Assert.assertFalse("Lazav, Dimir Mastermind must have flying", lazav.getAbilities().contains(FlyingAbility.getInstance()));
 
+
     }
 
     /**
@@ -199,7 +199,7 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase {
     public void testCopyCreatureExiled() {
         addCard(Zone.BATTLEFIELD, playerA, "Lazav, Dimir Mastermind", 1);
         // Codex Shredder - Artifact
-        // {T}: Target player puts the top card of their library into their graveyard.
+        // {T}: Target player mills a card.
         // {5}, {T}, Sacrifice Codex Shredder: Return target card from your graveyard to your hand.
         addCard(Zone.BATTLEFIELD, playerA, "Codex Shredder", 1);
 
@@ -211,7 +211,7 @@ public class LazavDimirMastermindTest extends CardTestPlayerBase {
         addCard(Zone.LIBRARY, playerB, "Assault Griffin", 5);
         skipInitShuffling();
 
-        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player puts the top card", playerB);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills a card.", playerB);
 
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Rest in Peace");
 
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/VolrathsShapshifterTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/VolrathsShapshifterTest.java
index 912088ef2f..f79cf477d3 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/VolrathsShapshifterTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/VolrathsShapshifterTest.java
@@ -15,12 +15,11 @@ import org.mage.test.serverside.base.CardTestPlayerBase;
 
 /**
  * Volrath's Shapeshifter
- *
+ * <p>
  * As long as the top card of your graveyard is a creature card, Volrath's
  * Shapeshifter has the full text of that card and has the text "2: Discard a
  * card." (Volrath's Shapeshifter has that card's name, mana cost, color, types,
  * abilities, power, and toughness.)
- *
  */
 public class VolrathsShapshifterTest extends CardTestPlayerBase {
 
@@ -58,7 +57,7 @@ public class VolrathsShapshifterTest extends CardTestPlayerBase {
     public void testLosingCopy() {
         addCard(Zone.BATTLEFIELD, playerA, "Volrath's Shapeshifter", 1);
         // Codex Shredder - Artifact
-        // {T}: {T}: Target player puts the top card of their library into their graveyard.
+        // {T}: Target player mills a card.
         // {5}, {T}, Sacrifice Codex Shredder: Return target card from your graveyard to your hand.
         addCard(Zone.BATTLEFIELD, playerA, "Codex Shredder", 1);
 
@@ -67,7 +66,7 @@ public class VolrathsShapshifterTest extends CardTestPlayerBase {
         addCard(Zone.LIBRARY, playerA, "Forest", 1);
         skipInitShuffling();
 
-        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player puts the top card of their library into their graveyard.", playerA);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player mills a card.", playerA);
 
         setStopAt(1, PhaseStep.END_TURN);
         setStrictChooseMode(true);
diff --git a/Mage/src/main/java/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java
index 0783542142..06c3743a18 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java
@@ -64,16 +64,16 @@ public class PutLibraryIntoGraveTargetEffect extends OneShotEffect {
         } else {
             sb.append("that target");
         }
-        sb.append(" puts the top ");
+
+        sb.append(" mills ");
         if (message.isEmpty()) {
             if (amount.toString().equals("1")) {
-                sb.append("card");
+                sb.append("a card");
             } else {
                 sb.append(CardUtil.numberToText(amount.toString())).append(" cards");
             }
-            sb.append(" of their library into their graveyard");
         } else {
-            sb.append(" X cards, where X is the number of ");
+            sb.append("X cards, where X is the number of ");
         }
 
         if (!message.isEmpty()) {

From c592542ff6e90934c1082be3ad53db39fda8ace1 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 11 Jun 2020 08:20:33 -0400
Subject: [PATCH 207/586] a few more mill changes

---
 Mage.Sets/src/mage/cards/b/BondOfInsight.java               | 2 +-
 Mage.Sets/src/mage/cards/b/BookBurning.java                 | 2 +-
 Mage.Sets/src/mage/cards/b/BrokenAmbitions.java             | 2 +-
 .../common/PutTopCardOfLibraryIntoGraveTargetEffect.java    | 6 ++----
 .../game/command/emblems/JaceTelepathUnboundEmblem.java     | 4 +---
 .../main/java/mage/game/command/planes/LetheLakePlane.java  | 2 +-
 6 files changed, 7 insertions(+), 11 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/b/BondOfInsight.java b/Mage.Sets/src/mage/cards/b/BondOfInsight.java
index fcd7cdda7e..9580a0a536 100644
--- a/Mage.Sets/src/mage/cards/b/BondOfInsight.java
+++ b/Mage.Sets/src/mage/cards/b/BondOfInsight.java
@@ -45,7 +45,7 @@ class BondOfInsightEffect extends OneShotEffect {
 
     BondOfInsightEffect() {
         super(Outcome.Benefit);
-        staticText = "Each player puts the top four cards of their library into their graveyard. " +
+        staticText = "Each player mills four cards. " +
                 "Return up to two instant and/or sorcery cards from your graveyard to your hand.";
     }
 
diff --git a/Mage.Sets/src/mage/cards/b/BookBurning.java b/Mage.Sets/src/mage/cards/b/BookBurning.java
index 6778bc4ee6..6678b6520f 100644
--- a/Mage.Sets/src/mage/cards/b/BookBurning.java
+++ b/Mage.Sets/src/mage/cards/b/BookBurning.java
@@ -41,7 +41,7 @@ class BookBurningMillEffect extends OneShotEffect {
 
     public BookBurningMillEffect() {
         super(Outcome.Detriment);
-        staticText = "Any player may have {source} deal 6 damage to them. If no one does, target player puts the top six cards of their library into their graveyard";
+        staticText = "Any player may have {source} deal 6 damage to them. If no one does, target player mills six cards";
     }
 
     public BookBurningMillEffect(final BookBurningMillEffect effect) {
diff --git a/Mage.Sets/src/mage/cards/b/BrokenAmbitions.java b/Mage.Sets/src/mage/cards/b/BrokenAmbitions.java
index 59228d09e5..cccf4aed4e 100644
--- a/Mage.Sets/src/mage/cards/b/BrokenAmbitions.java
+++ b/Mage.Sets/src/mage/cards/b/BrokenAmbitions.java
@@ -51,7 +51,7 @@ class BrokenAmbitionsEffect extends OneShotEffect {
     public BrokenAmbitionsEffect(Cost cost) {
         super(Outcome.Benefit);
         this.cost = cost;
-        this.staticText = "Counter target spell unless its controller pays {X}. Clash with an opponent. If you win, that spell's controller puts the top four cards of their library into their graveyard";
+        this.staticText = "Counter target spell unless its controller pays {X}. Clash with an opponent. If you win, that spell's controller mills four cards";
     }
 
     public BrokenAmbitionsEffect(DynamicValue genericMana) {
diff --git a/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveTargetEffect.java
index d45f62ad2c..9af46672ac 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveTargetEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveTargetEffect.java
@@ -1,4 +1,3 @@
-
 package mage.abilities.effects.common;
 
 import mage.abilities.Ability;
@@ -49,14 +48,13 @@ public class PutTopCardOfLibraryIntoGraveTargetEffect extends OneShotEffect {
     }
 
     private String setText() {
-        StringBuilder sb = new StringBuilder("target player puts the top ");
+        StringBuilder sb = new StringBuilder("target player mills ");
         if (numberCards.toString().equals("1")) {
-            sb.append(" card");
+            sb.append("a card");
         } else {
             sb.append(CardUtil.numberToText(numberCards.toString()));
             sb.append(" cards");
         }
-        sb.append(" of their library into their graveyard");
         return sb.toString();
     }
 }
diff --git a/Mage/src/main/java/mage/game/command/emblems/JaceTelepathUnboundEmblem.java b/Mage/src/main/java/mage/game/command/emblems/JaceTelepathUnboundEmblem.java
index 6891d99b3e..31b9302ca6 100644
--- a/Mage/src/main/java/mage/game/command/emblems/JaceTelepathUnboundEmblem.java
+++ b/Mage/src/main/java/mage/game/command/emblems/JaceTelepathUnboundEmblem.java
@@ -1,4 +1,3 @@
-
 package mage.game.command.emblems;
 
 import mage.abilities.Ability;
@@ -11,7 +10,6 @@ import mage.game.command.Emblem;
 import mage.target.common.TargetOpponent;
 
 /**
- *
  * @author spjspj
  */
 public final class JaceTelepathUnboundEmblem extends Emblem {
@@ -20,7 +18,7 @@ public final class JaceTelepathUnboundEmblem extends Emblem {
     public JaceTelepathUnboundEmblem() {
         this.setName("Emblem Jace");
         Effect effect = new PutTopCardOfLibraryIntoGraveTargetEffect(5);
-        effect.setText("target opponent puts the top five cards of their library into their graveyard");
+        effect.setText("target opponent mills five cards");
         Ability ability = new SpellCastControllerTriggeredAbility(Zone.COMMAND, effect, new FilterSpell("a spell"), false, false);
         ability.addTarget(new TargetOpponent());
         getAbilities().add(ability);
diff --git a/Mage/src/main/java/mage/game/command/planes/LetheLakePlane.java b/Mage/src/main/java/mage/game/command/planes/LetheLakePlane.java
index ace9cd43be..2806a2af63 100644
--- a/Mage/src/main/java/mage/game/command/planes/LetheLakePlane.java
+++ b/Mage/src/main/java/mage/game/command/planes/LetheLakePlane.java
@@ -31,7 +31,7 @@ public class LetheLakePlane extends Plane {
         this.setExpansionSetCodeForImage("PCA");
 
         // At the beginning of your upkeep, put the top ten cards of your libary into your graveyard
-        Ability ability = new BeginningOfUpkeepTriggeredAbility(Zone.COMMAND, new PutLibraryIntoGraveTargetEffect(10).setText("that player puts the top 10 cards of their library into their graveyard"), TargetController.ANY, false, true);
+        Ability ability = new BeginningOfUpkeepTriggeredAbility(Zone.COMMAND, new PutLibraryIntoGraveTargetEffect(10).setText("that player mills 10 cards"), TargetController.ANY, false, true);
         this.getAbilities().add(ability);
 
         // Active player can roll the planar die: Whenever you roll {CHAOS}, target player puts the top ten cards of their library into their graveyard

From 642cdea2f896f38ee79097670a97945583aff422 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Fri, 12 Jun 2020 00:18:40 +0400
Subject: [PATCH 208/586] Added single cards support in constructed format
 (like historic, see #6626);

---
 Mage/src/main/java/mage/cards/decks/Constructed.java | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/Mage/src/main/java/mage/cards/decks/Constructed.java b/Mage/src/main/java/mage/cards/decks/Constructed.java
index 8718c63540..7213b6b718 100644
--- a/Mage/src/main/java/mage/cards/decks/Constructed.java
+++ b/Mage/src/main/java/mage/cards/decks/Constructed.java
@@ -27,6 +27,7 @@ public class Constructed extends DeckValidator {
     protected List<String> restricted = new ArrayList<>();
     protected List<String> setCodes = new ArrayList<>();
     protected List<Rarity> rarities = new ArrayList<>();
+    protected Set<String> singleCards = new HashSet<>();
 
     public Constructed() {
         super("Constructed");
@@ -153,7 +154,7 @@ public class Constructed extends DeckValidator {
     }
 
     /**
-     * Checks if the given card is legal in any of the given sets
+     * Checks if the given card is legal in any of the given sets or as single card
      *
      * @param card - the card to check
      * @return Whether the card was printed in any of this format's sets.
@@ -168,6 +169,12 @@ public class Constructed extends DeckValidator {
                 break;
             }
         }
+
+        // check if single card allows
+        if (singleCards.contains(card.getName())) {
+            legal = true;
+        }
+
         if (!legal && !invalid.containsKey(card.getName())) {
             invalid.put(card.getName(), "Invalid set: " + card.getExpansionSetCode());
         }

From e68a20c5fb62df7a83dcd0ef454b6610a450055a Mon Sep 17 00:00:00 2001
From: 18ths <13023067+18ths@users.noreply.github.com>
Date: Thu, 11 Jun 2020 23:19:45 +0200
Subject: [PATCH 209/586] fixed and refactored balance effect (#6595)

* fixed and refactored balance effect

* inversed logic in balance effect - now players choose cards to keep instead to sacrifice/discard
---
 .../mage/test/cards/single/BalanceTest.java   |  41 +++++
 .../effects/common/BalanceEffect.java         | 166 ++++++++----------
 2 files changed, 116 insertions(+), 91 deletions(-)
 create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/single/BalanceTest.java

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/BalanceTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/BalanceTest.java
new file mode 100644
index 0000000000..b1af931ce3
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/BalanceTest.java
@@ -0,0 +1,41 @@
+package org.mage.test.cards.single;
+
+import mage.constants.PhaseStep;
+import mage.constants.Zone;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestPlayerBase;
+
+public class BalanceTest extends CardTestPlayerBase {
+
+    @Test
+    public void testBalance() {
+
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
+        addCard(Zone.BATTLEFIELD, playerA, "Runeclaw Bear", 3);
+        addCard(Zone.HAND, playerA, "Plains", 1);
+        addCard(Zone.HAND, playerA, "Balance");
+
+        addCard(Zone.BATTLEFIELD, playerB, "Swamp", 3);
+        addCard(Zone.BATTLEFIELD, playerB, "Runeclaw Bear", 2);
+        addCard(Zone.HAND, playerB, "Swamp", 2);
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balance");
+
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerA, "Plains", 2);
+        assertPermanentCount(playerA, "Runeclaw Bear", 2);
+        assertHandCount(playerA, "Plains", 1);
+        assertHandCount(playerA, "Balance", 0);
+        assertGraveyardCount(playerA, "Runeclaw Bear", 1);
+        assertGraveyardCount(playerA, "Balance", 1);
+
+        assertPermanentCount(playerB, "Swamp", 2);
+        assertPermanentCount(playerB, "Runeclaw Bear", 2);
+        assertHandCount(playerB, "Swamp", 1);
+        assertGraveyardCount(playerB, "Swamp", 2); //1 from hand, 1 from battlefield
+
+    }
+}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/BalanceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/BalanceEffect.java
index c81187c4ad..3f6a41bb02 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/BalanceEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/BalanceEffect.java
@@ -17,19 +17,14 @@ import mage.players.Player;
 import mage.target.common.TargetCardInHand;
 import mage.target.common.TargetControlledPermanent;
 
-import java.util.HashMap;
-import java.util.Map;
-import java.util.UUID;
+import java.util.*;
+import java.util.stream.Collectors;
 
 /**
  * @author emerald000
  */
 public class BalanceEffect extends OneShotEffect {
 
-    private static final FilterControlledPermanent filter = new FilterControlledLandPermanent("lands to keep");
-    private static final FilterControlledPermanent filter2 = new FilterControlledCreaturePermanent("creatures to keep");
-    private static final FilterCard filter3 = new FilterCard("cards to keep");
-
     public BalanceEffect() {
         super(Outcome.Sacrifice);
         staticText = "each player chooses a number of lands they control "
@@ -53,108 +48,45 @@ public class BalanceEffect extends OneShotEffect {
         if (controller == null) {
             return false;
         }
-        //Lands
-        int minLand = Integer.MAX_VALUE;
-        Cards landsToSacrifice = new CardsImpl();
-        for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
-            Player player = game.getPlayer(playerId);
-            if (player == null) {
-                continue;
-            }
-            int count = game.getBattlefield().countAll(StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND, player.getId(), game);
-            if (count < minLand) {
-                minLand = count;
-            }
-        }
 
-        for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
-            Player player = game.getPlayer(playerId);
-            if (player == null) {
-                continue;
-            }
-            TargetControlledPermanent target = new TargetControlledPermanent(minLand, minLand, filter, true);
-            if (!target.choose(Outcome.Sacrifice, player.getId(), source.getSourceId(), game)) {
-                continue;
-            }
-            for (Permanent permanent : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND, player.getId(), source.getSourceId(), game)) {
-                if (permanent != null && !target.getTargets().contains(permanent.getId())) {
-                    landsToSacrifice.add(permanent);
-                }
-            }
-        }
-
-        for (UUID cardId : landsToSacrifice) {
-            Permanent permanent = game.getPermanent(cardId);
-            if (permanent != null) {
-                permanent.sacrifice(source.getSourceId(), game);
-            }
-        }
-
-        //Creatures
-        int minCreature = Integer.MAX_VALUE;
-        Cards creaturesToSacrifice = new CardsImpl();
-        for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
-            Player player = game.getPlayer(playerId);
-            if (player == null) {
-            }
-            int count = game.getBattlefield().countAll(StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND, player.getId(), game);
-            if (count < minCreature) {
-                minCreature = count;
-            }
-        }
-
-        for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
-            Player player = game.getPlayer(playerId);
-            if (player == null) {
-                continue;
-            }
-            TargetControlledPermanent target = new TargetControlledPermanent(minCreature, minCreature, filter2, true);
-            if (!target.choose(Outcome.Sacrifice, player.getId(), source.getSourceId(), game)) {
-                continue;
-            }
-            for (Permanent permanent : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_CONTROLLED_CREATURE, player.getId(), source.getSourceId(), game)) {
-                if (permanent != null && !target.getTargets().contains(permanent.getId())) {
-                    creaturesToSacrifice.add(permanent);
-                }
-            }
-        }
-
-        for (UUID cardId : creaturesToSacrifice) {
-            Permanent permanent = game.getPermanent(cardId);
-            if (permanent != null) {
-                permanent.sacrifice(source.getSourceId(), game);
-            }
-        }
+        choosePermanentsToKeep(game, source, controller, StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND, new FilterControlledLandPermanent("lands to keep"));
+        choosePermanentsToKeep(game, source, controller, StaticFilters.FILTER_CONTROLLED_CREATURE, new FilterControlledCreaturePermanent("creatures to keep"));
 
         //Cards in hand
-        int minCard = Integer.MAX_VALUE;
-        Map<UUID, Cards> cardsToDiscard = new HashMap<>(2);
+        int lowestHandSize = Integer.MAX_VALUE;
         for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
             Player player = game.getPlayer(playerId);
             if (player == null) {
+                continue;
             }
-            int count = player.getHand().size();
-            if (count < minCard) {
-                minCard = count;
-            }
+
+            lowestHandSize = Math.min(lowestHandSize, player.getHand().size());
         }
 
+        Map<UUID, Cards> cardsToDiscard = new HashMap<>();
         for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
             Player player = game.getPlayer(playerId);
             if (player == null) {
                 continue;
             }
-            Cards cards = new CardsImpl();
-            TargetCardInHand target = new TargetCardInHand(minCard, filter3);
-            if (!target.choose(Outcome.Discard, player.getId(), source.getSourceId(), game)) {
+
+            TargetCardInHand target = new TargetCardInHand(lowestHandSize, new FilterCard("cards to keep"));
+            if (!target.choose(Outcome.Protect, player.getId(), source.getSourceId(), game)) {
                 continue;
             }
-            for (Card card : player.getHand().getCards(game)) {
-                if (card != null && !target.getTargets().contains(card.getId())) {
-                    cards.add(card);
+
+            Set<Card> allCardsInHand = player.getHand().getCards(game);
+            Set<Card> cardsToKeep = new LinkedHashSet<>();
+
+            for (Card card : allCardsInHand) {
+                if (card != null && target.getTargets().contains(card.getId())) {
+                    cardsToKeep.add(card);
                 }
             }
-            cardsToDiscard.put(playerId, cards);
+
+            cardsToDiscard.put(playerId, allCardsInHand.stream()
+                .filter(e -> !cardsToKeep.contains(e))
+                .collect(CardsImpl::new, CardsImpl::add, CardsImpl::addAll));
         }
 
         for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
@@ -163,6 +95,58 @@ public class BalanceEffect extends OneShotEffect {
                 player.discard(cardsToDiscard.get(playerId), source, game);
             }
         }
+
         return true;
     }
+
+    private void choosePermanentsToKeep(Game game, Ability source, Player controller,
+                                         FilterControlledPermanent filterPermanent, FilterControlledPermanent filterPermanentDialog) {
+        int lowestPermanentsCount = Integer.MAX_VALUE;
+        for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
+            Player player = game.getPlayer(playerId);
+            if (player == null) {
+                continue;
+            }
+
+            lowestPermanentsCount = Math.min(lowestPermanentsCount,
+                    game.getBattlefield().countAll(filterPermanent, player.getId(), game));
+        }
+
+        List<Permanent> permanentsToSacrifice = new ArrayList<>();
+
+        for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
+            Player player = game.getPlayer(playerId);
+            if (player == null) {
+                continue;
+            }
+
+            TargetControlledPermanent target = new TargetControlledPermanent(lowestPermanentsCount, lowestPermanentsCount, filterPermanentDialog, true);
+            if (!target.choose(Outcome.Protect, player.getId(), source.getSourceId(), game)) {
+                continue;
+            }
+
+            List<Permanent> allPermanentsOfType = game.getBattlefield().getActivePermanents(filterPermanent, player.getId(), source.getSourceId(), game);
+            List<Permanent> permanentsToKeep = new ArrayList<>();
+
+            for (Permanent permanent : allPermanentsOfType) {
+                if (permanent != null && target.getTargets().contains(permanent.getId())) {
+                    permanentsToKeep.add(permanent);
+                }
+            }
+
+            List<Permanent> playerPermanentsToSacrifice = allPermanentsOfType.stream().filter(e -> !permanentsToKeep.contains(e)).collect(Collectors.toList());
+            permanentsToSacrifice.addAll(playerPermanentsToSacrifice);
+
+            if (playerPermanentsToSacrifice.isEmpty()) {
+                game.informPlayers(player.getLogName() + " chose permanents to be sacrificed: "
+                        + playerPermanentsToSacrifice.stream().map(Permanent::getLogName).collect(Collectors.joining(", ")));
+            }
+        }
+
+        for (Permanent permanent : permanentsToSacrifice) {
+            if (permanent != null) {
+                permanent.sacrifice(source.getSourceId(), game);
+            }
+        }
+    }
 }

From 6fa21415fe5f3ca7f1250b9594278eb78a7a3b66 Mon Sep 17 00:00:00 2001
From: htrajan <htrajan2002@yahoo.com>
Date: Thu, 11 Jun 2020 15:36:52 -0700
Subject: [PATCH 210/586] Implement Basri cards (#6622)

* Implement Basri Ket

* Implement Basri's Lieutenant
---
 Mage.Sets/src/mage/cards/b/BasriKet.java      | 119 ++++++++++++++++++
 .../src/mage/cards/b/BasrisLieutenant.java    |  77 ++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |   2 +
 .../game/command/emblems/BasriKetEmblem.java  |  31 +++++
 .../emblems/LilianaWakerOfTheDeadEmblem.java  |   4 -
 5 files changed, 229 insertions(+), 4 deletions(-)
 create mode 100644 Mage.Sets/src/mage/cards/b/BasriKet.java
 create mode 100644 Mage.Sets/src/mage/cards/b/BasrisLieutenant.java
 create mode 100644 Mage/src/main/java/mage/game/command/emblems/BasriKetEmblem.java

diff --git a/Mage.Sets/src/mage/cards/b/BasriKet.java b/Mage.Sets/src/mage/cards/b/BasriKet.java
new file mode 100644
index 0000000000..ea8957545d
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/b/BasriKet.java
@@ -0,0 +1,119 @@
+package mage.cards.b;
+
+import mage.abilities.Ability;
+import mage.abilities.DelayedTriggeredAbility;
+import mage.abilities.LoyaltyAbility;
+import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
+import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
+import mage.abilities.effects.common.CreateTokenEffect;
+import mage.abilities.effects.common.GetEmblemEffect;
+import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
+import mage.abilities.effects.common.counter.AddCountersTargetEffect;
+import mage.abilities.keyword.IndestructibleAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+import mage.constants.SuperType;
+import mage.counters.CounterType;
+import mage.filter.common.FilterCreaturePermanent;
+import mage.filter.predicate.Predicates;
+import mage.filter.predicate.permanent.TokenPredicate;
+import mage.game.Game;
+import mage.game.command.emblems.BasriKetEmblem;
+import mage.game.events.GameEvent;
+import mage.game.permanent.Permanent;
+import mage.game.permanent.token.SoldierToken;
+import mage.target.common.TargetCreaturePermanent;
+
+import java.util.UUID;
+
+/**
+ *
+ * @author htrajan
+ */
+public final class BasriKet extends CardImpl {
+
+    public BasriKet(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{1}{W}{W}");
+        
+        this.addSuperType(SuperType.LEGENDARY);
+        this.subtype.add(SubType.BASRI);
+        this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3));
+
+        // +1: Put a +1/+1 counter on up to one target creature. It gains indestructible until end of turn.
+        Ability ability = new LoyaltyAbility(new AddCountersTargetEffect(
+                CounterType.P1P1.createInstance()
+        ).setText("Put a +1/+1 counter on up to one target creature"), 1);
+        ability.addEffect(new GainAbilityTargetEffect(
+                IndestructibleAbility.getInstance(), Duration.EndOfTurn
+        ).setText("It gains indestructible until end of turn"));
+        ability.addTarget(new TargetCreaturePermanent(0, 1));
+        this.addAbility(ability);
+
+        // −2: Whenever one or more nontoken creatures attack this turn, create that many 1/1 white Soldier creature tokens that are tapped and attacking.
+        this.addAbility(new LoyaltyAbility(new CreateDelayedTriggeredAbilityEffect(new BasriKetTriggeredAbility()), -2));
+
+        // −6: You get an emblem with "At the beginning of combat on your turn, create a 1/1 white Soldier creature token, then put a +1/+1 counter on each creature you control."
+        this.addAbility(new LoyaltyAbility(new GetEmblemEffect(new BasriKetEmblem()), -6));
+    }
+
+    private BasriKet(final BasriKet card) {
+        super(card);
+    }
+
+    @Override
+    public BasriKet copy() {
+        return new BasriKet(this);
+    }
+}
+
+class BasriKetTriggeredAbility extends DelayedTriggeredAbility {
+
+    private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nontoken creature");
+
+    static {
+        filter.add(Predicates.not(TokenPredicate.instance));
+    }
+
+    public BasriKetTriggeredAbility() {
+        super(null, Duration.EndOfTurn, false);
+    }
+
+    public BasriKetTriggeredAbility(BasriKetTriggeredAbility ability) {
+        super(ability);
+    }
+
+    @Override
+    public BasriKetTriggeredAbility copy() {
+        return new BasriKetTriggeredAbility(this);
+    }
+
+    @Override
+    public boolean checkEventType(GameEvent event, Game game) {
+        return event.getType() == GameEvent.EventType.DECLARED_ATTACKERS;
+    }
+
+    @Override
+    public boolean checkTrigger(GameEvent event, Game game) {
+        int attackingNonTokens = 0;
+        for (UUID attacker : game.getCombat().getAttackers()) {
+            Permanent permanent = game.getPermanent(attacker);
+            if (filter.match(permanent, game)) {
+                attackingNonTokens++;
+            }
+        }
+        if (attackingNonTokens > 0) {
+            this.getEffects().clear();
+            addEffect(new CreateTokenEffect(new SoldierToken(), attackingNonTokens, true, true));
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public String getRule() {
+        return "Whenever one or more nontoken creatures attack this turn, create that many 1/1 white Soldier creature tokens that are tapped and attacking.";
+    }
+}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/b/BasrisLieutenant.java b/Mage.Sets/src/mage/cards/b/BasrisLieutenant.java
new file mode 100644
index 0000000000..330eec1325
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/b/BasrisLieutenant.java
@@ -0,0 +1,77 @@
+package mage.cards.b;
+
+import mage.MageInt;
+import mage.MageObject;
+import mage.abilities.Ability;
+import mage.abilities.common.DiesThisOrAnotherCreatureTriggeredAbility;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.effects.common.CreateTokenEffect;
+import mage.abilities.effects.common.counter.AddCountersTargetEffect;
+import mage.abilities.keyword.ProtectionAbility;
+import mage.abilities.keyword.VigilanceAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.TargetController;
+import mage.counters.CounterType;
+import mage.filter.FilterObject;
+import mage.filter.common.FilterCreaturePermanent;
+import mage.filter.predicate.mageobject.MulticoloredPredicate;
+import mage.filter.predicate.permanent.CounterPredicate;
+import mage.game.permanent.token.KnightToken;
+import mage.target.common.TargetControlledCreaturePermanent;
+
+import java.util.UUID;
+
+/**
+ *
+ * @author htrajan
+ */
+public final class BasrisLieutenant extends CardImpl {
+
+    private static final FilterObject<MageObject> multicoloredFilter = new FilterObject<>("multicolored");
+    private static final FilterCreaturePermanent controlledCreatureWithP1P1CounterFilter = new FilterCreaturePermanent("creature you control");
+
+    static {
+        multicoloredFilter.add(MulticoloredPredicate.instance);
+        controlledCreatureWithP1P1CounterFilter.add(TargetController.YOU.getControllerPredicate());
+        controlledCreatureWithP1P1CounterFilter.add(new CounterPredicate(CounterType.P1P1));
+    }
+
+    public BasrisLieutenant(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}");
+        
+        this.subtype.add(SubType.HUMAN);
+        this.subtype.add(SubType.KNIGHT);
+        this.power = new MageInt(3);
+        this.toughness = new MageInt(4);
+
+        // Vigilance, protection from multicolored
+        this.addAbility(VigilanceAbility.getInstance());
+
+        // protection from multicolored
+        this.addAbility(new ProtectionAbility(multicoloredFilter));
+
+        // When Basri's Lieutenant enters the battlefield, put a +1/+1 counter on target creature you control.
+        Ability ability = new EntersBattlefieldTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance()));
+        ability.addTarget(new TargetControlledCreaturePermanent());
+        this.addAbility(ability);
+
+        // Whenever Basri's Lieutenant or another creature you control dies, if it had a +1/+1 counter on it, create a 2/2 white Knight creature token with vigilance.
+        this.addAbility(new DiesThisOrAnotherCreatureTriggeredAbility(
+                new CreateTokenEffect(new KnightToken()).setText("if it had a +1/+1 counter on it, create a 2/2 white Knight creature token with vigilance"),
+                false,
+                controlledCreatureWithP1P1CounterFilter
+        ));
+    }
+
+    private BasrisLieutenant(final BasrisLieutenant card) {
+        super(card);
+    }
+
+    @Override
+    public BasrisLieutenant copy() {
+        return new BasrisLieutenant(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 951c9c1a31..dde0bf23f9 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -40,8 +40,10 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Azusa, Lost but Seeking", 173, Rarity.RARE, mage.cards.a.AzusaLostButSeeking.class));
         cards.add(new SetCardInfo("Bad Deal", 89, Rarity.UNCOMMON, mage.cards.b.BadDeal.class));
         cards.add(new SetCardInfo("Baneslayer Angel", 6, Rarity.MYTHIC, mage.cards.b.BaneslayerAngel.class));
+        cards.add(new SetCardInfo("Basri Ket", 7, Rarity.MYTHIC, mage.cards.b.BasriKet.class));
         cards.add(new SetCardInfo("Basri's Acolyte", 8, Rarity.COMMON, mage.cards.b.BasrisAcolyte.class));
         cards.add(new SetCardInfo("Basri's Aegis", 322, Rarity.RARE, mage.cards.b.BasrisAegis.class));
+        cards.add(new SetCardInfo("Basri's Lieutenant", 9, Rarity.RARE, mage.cards.b.BasrisLieutenant.class));
         cards.add(new SetCardInfo("Basri's Solidarity", 10, Rarity.UNCOMMON, mage.cards.b.BasrisSolidarity.class));
         cards.add(new SetCardInfo("Basri, Devoted Paladin", 320, Rarity.MYTHIC, mage.cards.b.BasriDevotedPaladin.class));
         cards.add(new SetCardInfo("Bolt Hound", 131, Rarity.UNCOMMON, mage.cards.b.BoltHound.class));
diff --git a/Mage/src/main/java/mage/game/command/emblems/BasriKetEmblem.java b/Mage/src/main/java/mage/game/command/emblems/BasriKetEmblem.java
new file mode 100644
index 0000000000..e8fb405fc7
--- /dev/null
+++ b/Mage/src/main/java/mage/game/command/emblems/BasriKetEmblem.java
@@ -0,0 +1,31 @@
+package mage.game.command.emblems;
+
+import mage.abilities.Ability;
+import mage.abilities.common.BeginningOfCombatTriggeredAbility;
+import mage.abilities.effects.common.CreateTokenEffect;
+import mage.abilities.effects.common.counter.AddCountersAllEffect;
+import mage.constants.TargetController;
+import mage.constants.Zone;
+import mage.counters.CounterType;
+import mage.filter.StaticFilters;
+import mage.game.command.Emblem;
+import mage.game.permanent.token.SoldierToken;
+
+public final class BasriKetEmblem extends Emblem {
+    /**
+     * Emblem with "At the beginning of combat on your turn, create a 1/1 white Soldier creature token, then put a +1/+1 counter on each creature you control."
+     */
+
+    public BasriKetEmblem() {
+        setName("Emblem Basri");
+        Ability ability = new BeginningOfCombatTriggeredAbility(
+                Zone.COMMAND,
+                new CreateTokenEffect(new SoldierToken()),
+                TargetController.YOU, false, false);
+        ability.addEffect(
+                new AddCountersAllEffect(CounterType.P1P1.createInstance(), StaticFilters.FILTER_PERMANENT_CREATURE_CONTROLLED)
+                .setText(", then put a +1/+1 counter on each creature you control")
+        );
+        this.getAbilities().add(ability);
+    }
+}
diff --git a/Mage/src/main/java/mage/game/command/emblems/LilianaWakerOfTheDeadEmblem.java b/Mage/src/main/java/mage/game/command/emblems/LilianaWakerOfTheDeadEmblem.java
index 7ae6e94fe4..dead907e5b 100644
--- a/Mage/src/main/java/mage/game/command/emblems/LilianaWakerOfTheDeadEmblem.java
+++ b/Mage/src/main/java/mage/game/command/emblems/LilianaWakerOfTheDeadEmblem.java
@@ -5,9 +5,7 @@ import mage.abilities.common.BeginningOfCombatTriggeredAbility;
 import mage.abilities.effects.common.ReturnCreatureFromGraveyardToBattlefieldAndGainHasteEffect;
 import mage.constants.TargetController;
 import mage.constants.Zone;
-import mage.filter.FilterCard;
 import mage.filter.StaticFilters;
-import mage.filter.common.FilterCreatureCard;
 import mage.game.command.Emblem;
 import mage.target.common.TargetCardInGraveyard;
 
@@ -16,8 +14,6 @@ public final class LilianaWakerOfTheDeadEmblem extends Emblem {
      * Emblem with "At the beginning of combat on your turn, put target creature card from a graveyard onto the battlefield under your control. It gains haste."
      */
 
-    private static final FilterCard filter = new FilterCreatureCard("creature card from a graveyard");
-
     public LilianaWakerOfTheDeadEmblem() {
         setName("Emblem Liliana");
         Ability ability = new BeginningOfCombatTriggeredAbility(

From eaa5fed464982726ae3a50be83465da5807e64cf Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 11 Jun 2020 18:39:08 -0400
Subject: [PATCH 211/586] updated M21 spoiler and reprints

---
 .../src/mage/cards/s/SelflessSavior.java      |  5 ++--
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  2 ++
 Utils/mtg-cards-data.txt                      | 24 ++++++++++++++++++-
 3 files changed, 28 insertions(+), 3 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/s/SelflessSavior.java b/Mage.Sets/src/mage/cards/s/SelflessSavior.java
index a5af3c0118..fe9c603a64 100644
--- a/Mage.Sets/src/mage/cards/s/SelflessSavior.java
+++ b/Mage.Sets/src/mage/cards/s/SelflessSavior.java
@@ -12,7 +12,7 @@ import mage.constants.CardType;
 import mage.constants.Duration;
 import mage.constants.SubType;
 import mage.filter.FilterPermanent;
-import mage.filter.common.FilterCreaturePermanent;
+import mage.filter.common.FilterControlledCreaturePermanent;
 import mage.filter.predicate.permanent.AnotherPredicate;
 import mage.target.TargetPermanent;
 
@@ -23,7 +23,8 @@ import java.util.UUID;
  */
 public final class SelflessSavior extends CardImpl {
 
-    private static final FilterPermanent filter = new FilterCreaturePermanent("another target creature");
+    private static final FilterPermanent filter
+            = new FilterControlledCreaturePermanent("another target creature");
 
     static {
         filter.add(AnotherPredicate.instance);
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index dde0bf23f9..236e0b8643 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -120,6 +120,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Spined Megalodon", 72, Rarity.COMMON, mage.cards.s.SpinedMegalodon.class));
         cards.add(new SetCardInfo("Spirit of Malevolence", 331, Rarity.COMMON, mage.cards.s.SpiritOfMalevolence.class));
         cards.add(new SetCardInfo("Storm Caller", 335, Rarity.COMMON, mage.cards.s.StormCaller.class));
+        cards.add(new SetCardInfo("Swamp", 311, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Teferi's Ageless Insight", 76, Rarity.RARE, mage.cards.t.TeferisAgelessInsight.class));
         cards.add(new SetCardInfo("Teferi's Protege", 77, Rarity.COMMON, mage.cards.t.TeferisProtege.class));
         cards.add(new SetCardInfo("Teferi's Tutelage", 78, Rarity.UNCOMMON, mage.cards.t.TeferisTutelage.class));
@@ -136,6 +137,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Valorous Steed", 42, Rarity.COMMON, mage.cards.v.ValorousSteed.class));
         cards.add(new SetCardInfo("Village Rites", 126, Rarity.COMMON, mage.cards.v.VillageRites.class));
         cards.add(new SetCardInfo("Vito, Thorn of the Dusk Rose", 127, Rarity.RARE, mage.cards.v.VitoThornOfTheDuskRose.class));
+        cards.add(new SetCardInfo("Vryn Wingmare", 43, Rarity.UNCOMMON, mage.cards.v.VrynWingmare.class));
         cards.add(new SetCardInfo("Warden of the Woods", 213, Rarity.UNCOMMON, mage.cards.w.WardenOfTheWoods.class));
         cards.add(new SetCardInfo("Wildwood Patrol", 339, Rarity.COMMON, mage.cards.w.WildwoodPatrol.class));
     }
diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt
index ce2c88ba88..963dacdb3b 100644
--- a/Utils/mtg-cards-data.txt
+++ b/Utils/mtg-cards-data.txt
@@ -37451,14 +37451,20 @@ Idol of Endurance|Core Set 2021|23|R|{2}{W}|Artifact|||When Idol of Endurance en
 Light of Promise|Core Set 2021|25|U|{2}{W}|Enchantment - Aura|||Enchant creature$Enchanted creature has "Whenever you gain life, put that many +1/+1 counters on this creature."|
 Mangara, the Diplomat|Core Set 2021|27|M|{3}{W}|Legendary Creature - Human Cleric|2|4|Lifelink$Whenever an opponent attacks with creatures, if two or more of those creatures are attacking you and/or a planeswalker you control, draw a card.$Whenever an opponent casts their second spell each turn, draw a card.|
 Pack Leader|Core Set 2021|29|R|{1}{W}|Creature - Dog|2|2|Other Dogs you control get +1/+1.$Whenever Pack Leader attacks, prevent all combat damage that would be dealt this turn to Dogs you control.|
+Rambunctious Mutt|Core Set 2021|30|C|{3}{W}{W}|Creature - Dog|3|4|When Rambunctious Mutt enters the battlefield, destroy target artifact or enchantment an opponent controls.|
 Revitalize|Core Set 2021|31|C|{1}{W}|Instant|||You gain 3 life.$Draw a card.|
 Runed Halo|Core Set 2021|32|R|{W}{W}|Enchantment|||As Runed Halo enters the battlefield, choose a card name.$You have protection from the chosen name.|
 Seasoned Hallowblade|Core Set 2021|34|U|{1}{W}|Creature - Human Warrior|3|1|Discard a card: Tap Seasoned Hallowblade. It gains indestructible until end of turn.|
-Selfless Savior|Core Set 2021|36|U|{W}|Creature - Dog|1|1|Sacrifice Selfless Savior: Another target creature gains indestructible until end of turn.|
+Selfless Savior|Core Set 2021|36|U|{W}|Creature - Dog|1|1|Sacrifice Selfless Savior: Another target creature you control gains indestructible until end of turn.|
 Siege Striker|Core Set 2021|37|U|{2}{W}|Creature - Human Soldier|1|1|Double strike$Whenever Siege Striker attacks, you may tap any number of untapped creatures you control. Siege Striker gets +1/+1 until end of turn for each creature tapped this way.|
+Tempered Veteran|Core Set 2021|41|U|{1}{W}|Creature - Human Knight|1|2|{W}, {T}: Put a +1/+1 counter on target creature with a +1/+1 counter on it.${4}{W}{W}, {T}: Put a +1/+1 counter on target creature.|
 Valorous Steed|Core Set 2021|42|C|{4}{W}|Creature - Unicorn|3|3|Vigilance$When Valorous Steed enters the battlefield, create a 2/2 white Knight creature token with vigilance.|
+Vryn Wingmare|Core Set 2021|43|U|{2}{W}|Creature - Pegasus|2|1|Flying$Noncreature spells cost {1} more to cast.|
+Enthralling Hold|Core Set 2021|49|U|{3}{U}{U}|Enchantment - Aura|||Enchant creature$You can't choose an untapped creature as this spell's target as you cast it.$You control enchanted creature.|
 Frantic Inventory|Core Set 2021|50|C|{1}{U}|Instant|||Draw a card, then draw cards equal to the number of cards named Frantic Inventory in your graveyard.|
 Jeskai Elder|Core Set 2021|53|U|{1}{U}|Creature - Human Monk|1|2|Prowess$Whenever Jeskai Elder deals combat damage to a player, you may draw a card. If you do, discard a card.|
+Lofty Denial|Core Set 2021|56|C|{1}{U}|Instant|||Counter target spell unless its controller pays {1}. If you control a creature with flying, counter that spell unless its controller pays {4} instead.|
+Miscast|Core Set 2021|57|U|{U}|Instant|||Counter target instant or sorcery spell unless its controller pays {3}.|
 Pursued Whale|Core Set 2021|60|R|{5}{U}{U}|Creature - Whale|8|8|When Pursued Whale enters the battlefield, each opponent creates a 1/1 red Pirate creature token with "This creature can't block" and "Creatures you control attack each combat if able."$Spells your opponents cast that target Pursued Whale cost {3} more to cast.|
 Rain of Revelation|Core Set 2021|61|U|{3}{U}|Instant|||Draw three cards, then discard a card.|
 Rewind|Core Set 2021|63|U|{2}{U}{U}|Instant|||Counter target spell. Untap up to four lands.|
@@ -37466,6 +37472,7 @@ Riddleform|Core Set 2021|64|U|{1}{U}|Enchantment|||Whenever you cast a noncreatu
 See the Truth|Core Set 2021|69|R|{1}{U}|Sorcery|||Look at the top three cards of your library. Put one of those cards into your hand and the rest on the bottom of your library in any order. If this spell was cast from anywhere other than your hand, put each of those cards into your hand instead.|
 Shipwreck Dowser|Core Set 2021|71|U|{3}{U}{U}|Creature - Merfolk Wizard|3|3|Prowess$When Shipwreck Dowser enters the battlefield, return target instant or sorcery card from your graveyard to your hand.|
 Spined Megalodon|Core Set 2021|72|C|{5}{U}{U}|Creature - Shark|5|7|Hexproof$Whenever Spined Megalodon attacks, scry 1.|
+Stormwing Entity|Core Set 2021|73|R|{3}{U}{U}|Creature - Elemental|3|3|This spell costs {2}{U} less to cast if you've cast an instant or sorcery spell this turn.$Flying$Prowess$When Stormwing Entity enters the battlefield, scry 2.|
 Teferi, Master of Time|Core Set 2021|75|M|{2}{U}{U}|Legendary Planeswalker - Teferi|3|You may activate loyalty abilities of Teferi, Master of Time on any player's turn any time you could cast an instant.$+1: Draw a card, then discard a card.$−3: Target creature you don't control phases out.$−10: Take two extra turns after this one.|
 Teferi's Ageless Insight|Core Set 2021|76|R|{2}{U}{U}|Legendary Enchantment|||If you would draw a card except the first one you draw in each of your draw steps, draw two cards instead.|
 Teferi's Protege|Core Set 2021|77|C|{2}{U}|Creature - Human Wizard|2|3|{1}{U}, {T}: Draw a card, then discard a card.|
@@ -37479,11 +37486,15 @@ Eliminate|Core Set 2021|97|U|{1}{B}|Instant|||Destroy target creature or planesw
 Grasp of Darkness|Core Set 2021|102|C|{B}{B}|Instant|||Target creature gets -4/-4 until end of turn.|
 Grim Tutor|Core Set 2021|103|M|{1}{B}{B}|Sorcery|||Search your library for a card and put that card into your hand, then shuffle your library. You lose 3 life.|
 Liliana, Waker of the Dead|Core Set 2021|108|M|{2}{B}{B}|Legendary Planeswalker - Liliana|4|+1: Each player discards a card. Each opponent who can't loses 3 life.$−3: Target creature gets -X/-X until end of turn, where X is the number of cards in your graveyard.$−7: You get an emblem with "At the beginning of combat on your turn, put target creature card from a graveyard onto the battlefield under your control. It gains haste."|
+Liliana's Devotee|Core Set 2021|109|U|{2}{B}|Creature - Human Warlock|2|3|Zombies you control get +1/+0.$At the beginning of your end step, if a creature died this turn, you may pay {1}{B}. If you do, create a 2/2 black Zombie creature token.|
+Liliana's Standard Bearer|Core Set 2021|110|R|{2}{B}|Creature - Zombie Knight|3|1|Flash$When Liliana's Standard Bearer enters the battlefield, draw X cards, where X is the number of creatures that died under your control this turn.|
+Liliana's Steward|Core Set 2021|111|C|{B}|Creature - Zombie|1|2|{T}, Sacrifice Liliana's Steward: Target opponent discards a card. Activate this ability only any time you could cast a sorcery.|
 Malefic Scythe|Core Set 2021|112|U|{1}{B}|Artifact - Equipment|||Malefic Scythe enters the battlefield with a soul counter on it.$Equipped creature gets +1/+1 for each soul counter on Malefic Scythe.$Whenever equipped creature dies, put a soul counter on Malefic Scythe.$Equip {1}|
 Massacre Wurm|Core Set 2021|114|M|{3}{B}{B}{B}|Creature - Wurm|6|5|When Massacre Wurm enters the battlefield, creatures your opponents control get -2/-2 until end of turn.$Whenever a creature an opponent controls dies, that player loses 2 life.|
 Necromentia|Core Set 2021|116|R|{1}{B}{B}|Sorcery|||Choose a card name other than a basic land card name. Search target opponent's graveyard, hand, and library for any number of cards with that name and exile them. That player shuffles their library, then creates a 2/2 black Zombie creature token for each card exiled from their hand this way.|
 Peer into the Abyss|Core Set 2021|117|R|{4}{B}{B}{B}|Sorcery|||Target player draws cards equal to half the number of cards in their library and loses half their life. Round up each time.|
 Pestilent Haze|Core Set 2021|118|U|{1}{B}{B}|Sorcery|||Choose one —$• All creatures get -2/-2 until end of turn.$• Remove two loyalty counters from each planeswalker.|
+Thieves' Guild Enforcer|Core Set 2021|125|R|{B}|Creature - Human Rogue|1|1|Flash$Whenever Thieves' Guild Enforcer or another Rogue enters the battlefield under your control, each opponent mills two cards.$As long as an opponent has eight or more cards in their graveyard, Thieves' Guild Enforcer gets +2/+1 and has deathtouch.|
 Village Rites|Core Set 2021|126|C|{B}|Instant|||As an additional cost to cast this spell, sacrifice a creature.$Draw two cards.|
 Vito, Thorn of the Dusk Rose|Core Set 2021|127|R|{2}{B}|Legendary Creature - Vampire Cleric|1|3|Whenever you gain life, target opponent loses that much life.${3}{B}{B}: Creatures you control gain lifelink until end of turn.|
 Bolt Hound|Core Set 2021|131|U|{2}{R}|Creature - Elemental Dog|2|2|Haste$Whenever Bolt Hound attacks, other creatures you control get +1/+0 until end of turn.|
@@ -37508,17 +37519,27 @@ Volcanic Salvo|Core Set 2021|172|R|{10}{R}{R}|Sorcery|||This spell costs {X} les
 Azusa, Lost but Seeking|Core Set 2021|173|R|{2}{G}|Legendary Creature - Human Monk|1|2|You may play two additional lands on each of your turns.|
 Cultivate|Core Set 2021|177|U|{2}{G}|Sorcery|||Search your library for up to two basic land cards, reveal those cards, and put one onto the battlefield tapped and the other into your hand. Then shuffle your library.|
 Elder Gargaroth|Core Set 2021|179|M|{3}{G}{G}|Creature - Beast|6|6|Vigilance, reach, trample$Whenever Elder Gargaroth attacks or blocks, choose one —$• Create a 3/3 green Beast creature token.$• You gain 3 life.$• Draw a card.|
+Feline Sovereign|Core Set 2021|180|R|{2}{G}|Creature - Cat|2|3|Other Cats you control get +1/+1 and have protection from Dogs.$Whenever one or more Cats you control deal combat damage to a player, destroy up to one target artifact or enchantment that player controls.|
 Fierce Empath|Core Set 2021|181|U|{2}{G}|Creature - Elf|1|1|When Fierce Empath enters the battlefield, you may search your library for a creature card with converted mana cost 6 or greater, reveal it, put it into your hand, then shuffle your library.|
 Fungal Rebirth|Core Set 2021|182|U|{2}{G}|Instant|||Return target permanent card from your graveyard to your hand. If a creature died this turn, create two 1/1 green Saproling creature tokens.|
+Garruk, Unleashed|Core Set 2021|183|M|{2}{G}{G}|Legendary Planeswalker - Garruk|4|+1: Up to one target creature gets +3/+3 and gains trample until end of turn.$−2: Create a 3/3 green Beast creature token. Then, if an opponent controls more creatures than you, put a loyalty counter on Garruk, Unleashed.$−7: Create an emblem with "At the beginning of your end step, you may search your library for a creature card, put it onto the battlefield, then shuffle your library."|
+Garruk's Gorehorn|Core Set 2021|184|C|{4}{G}|Creature - Beast|7|3||
+Garruk's Harbinger|Core Set 2021|185|R|{1}{G}{G}|Creature - Beast|4|3|Hexproof from Black$Whenever Garruk's Harbinger deals combat damage to a player or planeswalker, look at that many cards from the top of your library. You may reveal a creature card or Garruk planeswalker card from among them and put it into your hand. Put the rest on the bottom of your library in a random order.|
+Garruk's Uprising|Core Set 2021|186|U|{2}{G}|Enchantment|||When Garruk's Uprising enters the battlefield, if you control a creature with power 4 or greater, draw a card.$Creatures you control have trample.$Whenever a creature with power 4 or greater enters the battlefield under your control, draw a card.|
 Heroic Intervention|Core Set 2021|188|R|{1}{G}|Instant|||Permanents you control gain hexproof and indestructible until end of turn.|
 Jolrael, Mwonvuli Recluse|Core Set 2021|191|R|{1}{G}|Legendary Creature - Human Druid|1|2|Whenever you draw your second card each turn, create a 2/2 green Cat creature token.${4}{G}{G}: Until end of turn, creatures you control have base power and toughness X/X, where X is the number of cards in your hand.|
 Llanowar Visionary|Core Set 2021|193|C|{2}{G}|Creature - Elf Druid|2|2|When Llanowar Visionary enters the battlefield, draw a card.${T}: Add {G}.|
 Pridemalkin|Core Set 2021|196|C|{2}{G}|Creature - Cat|2|1|When Pridemalkin enters the battlefield, put a +1/+1 counter on target creature you control.$Each creature you control with a +1/+1 counter on it has trample.|
 Primal Might|Core Set 2021|197|R|{X}{G}|Sorcery|||Target creature you control gets +X/+X until end of turn. Then it fights up to one target creature you don't control.|
 Quirion Dryad|Core Set 2021|198|U|{1}{G}|Creature - Dryad|1|1|Whenever you cast a white, blue, black, or red spell, put a +1/+1 counter on Quirion Dryad.|
+Trufflesnout|Core Set 2021|212|C|{2}{G}|Creature - Boar|2|2|When Trufflesnout enters the battlefield, choose one —$• Put a +1/+1 counter on Trufflesnout.$• You gain 4 life.|
 Warden of the Woods|Core Set 2021|213|U|{4}{G}{G}|Creature - Treefolk|5|7|Vigilance$Whenever Warden of the Woods becomes the target of a spell or ability an opponent controls, you may draw two cards.|
+Wildwood Scourge|Core Set 2021|214|U|{X}{G}|Creature - Hydra|0|0|Wildwood Scourge enters the battlefield with X +1/+1 counters on it.$Whenever one or more +1/+1 counters are put on another non-Hydra creature you control, put a +1/+1 counter on Wildwood Scourge.|
 Alpine Houndmaster|Core Set 2021|215|U|{R}{W}|Creature - Human Warrior|2|2|When Alpine Houndmaster enters the battlefield, you may search your library for a card named Alpine Watchdog and/or a card named Igneous Cur, reveal them, put them into your hand, then shuffle your library.$Whenever Alpine Houndmaster attacks, it gets +X/+0 until end of turn, where X is the number of other attacking creatures.|
+Conclave Mentor|Core Set 2021|216|U|{G}{W}|Creature - Centaur Cleric|2|2|If one or more +1/+1 counters would be put on a creature you control, that many plus one +1/+1 counters are put on that creature instead.$When Conclave Mentor dies, you gain life equal to its power.|
+Dire Fleet Warmonger|Core Set 2021|217|U|{1}{B}{R}|Creature - Orc Pirate|3|3|At the beginning of combat on your turn, you may sacrifice another creature. If you do, Dire Fleet Warmonger gets +2/+2 and gains trample until end of turn.|
 Indulging Patrician|Core Set 2021|219|U|{1}{W}{B}|Creature - Vampire Noble|1|4|Flying$Lifelink$At the beginning of your end step, if you gained 3 or more life this turn, each opponent loses 3 life.|
+Leafkin Avenger|Core Set 2021|220|U|{2}{R}{G}|Creature - Elemental Druid|4|3|{T}: Add {G} for each creature you control with power 4 or greater.${7}{R}: Leafkin Avenger deals damage equal to its power to target player or planeswalker.|
 Lorescale Coatl|Core Set 2021|221|U|{1}{G}{U}|Creature - Snake|2|2|Whenever you draw a card, you may put a +1/+1 counter on Lorescale Coatl.|
 Obsessive Stitcher|Core Set 2021|223|U|{1}{U}{B}|Creature - Human Wizard|0|3|{T}: Draw a card, then discard a card.${2}{U}{B}, {T}, Sacrifice Obsessive Stitcher: Return target creature card from your graveyard to the battlefield.|
 Chromatic Orrery|Core Set 2021|228|M|{7}|Legendary Artifact|||You may spend mana as though it were mana of any color.${T}: Add {C}{C}{C}{C}{C}.${5}, {T}: Draw a card for each color among permanents you control.|
@@ -37537,6 +37558,7 @@ Temple of Triumph|Core Set 2021|256|R||Land|||Temple of Triumph enters the battl
 Rin and Seri, Inseparable|Core Set 2021|278|M|{1}{R}{G}{W}|Legendary Creature - Dog Cat|4|4|Whenever you cast a Dog spell, create a 1/1 green Cat creature token.$Whenever you cast a Cat spell, create a 1/1 white Dog creature token.${R}{G}{W}, {T}: Rin and Seri, Inseparable deals damage to any target equal to the number of Dogs you control. You gain life equal to the number of Cats you control.|
 Plains|Core Set 2021|309|C||Basic Land - Plains|||({T}: Add {W}.)|
 Island|Core Set 2021|310|C||Basic Land - Island|||({T}: Add {U}.)|
+Swamp|Core Set 2021|311|C||Basic Land - Swamp|||({T}: Add {B}.)|
 Mountain|Core Set 2021|312|C||Basic Land - Mountain|||({T}: Add {R}.)|
 Containment Priest|Core Set 2021|314|R|{1}{W}|Creature - Human Cleric|2|2|Flash$If a nontoken creature would enter the battlefield and it wasn't cast, exile it instead.|
 Basri, Devoted Paladin|Core Set 2021|320|M|{4}{W}{W}|Legendary Planeswalker - Basri|4|+1: Put a +1/+1 counter on up to one target creature. It gains vigilance until end of turn.$−1: Whenever a creature attacks this turn, put a +1/+1 counter on it.$−6: Creatures you control get +2/+2 and gain flying until end of turn.|

From 717b3da6f95946ab3baa56ad5086f3e65357e1e2 Mon Sep 17 00:00:00 2001
From: John <hitch17@gmail.com>
Date: Thu, 11 Jun 2020 15:59:54 -0700
Subject: [PATCH 212/586] swap green and white in color order choices. (#6630)

---
 Mage/src/main/java/mage/choices/ChoiceColor.java | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Mage/src/main/java/mage/choices/ChoiceColor.java b/Mage/src/main/java/mage/choices/ChoiceColor.java
index aacbee2f13..e93a2b844a 100644
--- a/Mage/src/main/java/mage/choices/ChoiceColor.java
+++ b/Mage/src/main/java/mage/choices/ChoiceColor.java
@@ -16,11 +16,11 @@ public class ChoiceColor extends ChoiceImpl {
 
     public static List<String> getBaseColors() {
         List<String> arr = new ArrayList<>();
-        arr.add("Green");
+        arr.add("White");
         arr.add("Blue");
         arr.add("Black");
         arr.add("Red");
-        arr.add("White");
+        arr.add("Green");
         return arr;
     }
 

From 68d99aaf1d72533b097537a2f8f3ee66ae363f22 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 11 Jun 2020 18:47:58 -0400
Subject: [PATCH 213/586] Implemented Dire Fleet Warmonger

---
 .../src/mage/cards/d/DireFleetWarmonger.java  | 51 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 52 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/d/DireFleetWarmonger.java

diff --git a/Mage.Sets/src/mage/cards/d/DireFleetWarmonger.java b/Mage.Sets/src/mage/cards/d/DireFleetWarmonger.java
new file mode 100644
index 0000000000..d146792b7a
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/d/DireFleetWarmonger.java
@@ -0,0 +1,51 @@
+package mage.cards.d;
+
+import mage.MageInt;
+import mage.abilities.common.BeginningOfCombatTriggeredAbility;
+import mage.abilities.costs.common.SacrificeTargetCost;
+import mage.abilities.effects.common.DoIfCostPaid;
+import mage.abilities.effects.common.continuous.BoostSourceEffect;
+import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
+import mage.abilities.keyword.TrampleAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+import mage.constants.TargetController;
+import mage.filter.StaticFilters;
+import mage.target.common.TargetControlledPermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class DireFleetWarmonger extends CardImpl {
+
+    public DireFleetWarmonger(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{R}");
+
+        this.subtype.add(SubType.ORC);
+        this.subtype.add(SubType.PIRATE);
+        this.power = new MageInt(3);
+        this.toughness = new MageInt(3);
+
+        // At the beginning of combat on your turn, you may sacrifice another creature. If you do, Dire Fleet Warmonger gets +2/+2 and gains trample until end of turn.
+        this.addAbility(new BeginningOfCombatTriggeredAbility(new DoIfCostPaid(
+                new BoostSourceEffect(2, 2, Duration.EndOfTurn),
+                new SacrificeTargetCost(new TargetControlledPermanent(
+                        StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE
+                ))
+        ).addEffect(new GainAbilitySourceEffect(TrampleAbility.getInstance())), TargetController.YOU, false));
+    }
+
+    private DireFleetWarmonger(final DireFleetWarmonger card) {
+        super(card);
+    }
+
+    @Override
+    public DireFleetWarmonger copy() {
+        return new DireFleetWarmonger(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 236e0b8643..d1ace7036e 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -55,6 +55,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Chromatic Orrery", 228, Rarity.MYTHIC, mage.cards.c.ChromaticOrrery.class));
         cards.add(new SetCardInfo("Containment Priest", 314, Rarity.RARE, mage.cards.c.ContainmentPriest.class));
         cards.add(new SetCardInfo("Cultivate", 177, Rarity.UNCOMMON, mage.cards.c.Cultivate.class));
+        cards.add(new SetCardInfo("Dire Fleet Warmonger", 217, Rarity.UNCOMMON, mage.cards.d.DireFleetWarmonger.class));
         cards.add(new SetCardInfo("Double Vision", 142, Rarity.RARE, mage.cards.d.DoubleVision.class));
         cards.add(new SetCardInfo("Elder Gargaroth", 179, Rarity.MYTHIC, mage.cards.e.ElderGargaroth.class));
         cards.add(new SetCardInfo("Eliminate", 97, Rarity.UNCOMMON, mage.cards.e.Eliminate.class));

From 0e63be9231aaf86087e460c0d94c2200c03b906e Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 11 Jun 2020 18:50:00 -0400
Subject: [PATCH 214/586] Implemented Garruk's Gorehorn

---
 .../src/mage/cards/g/GarruksGorehorn.java     | 32 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 33 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/g/GarruksGorehorn.java

diff --git a/Mage.Sets/src/mage/cards/g/GarruksGorehorn.java b/Mage.Sets/src/mage/cards/g/GarruksGorehorn.java
new file mode 100644
index 0000000000..237734487f
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/g/GarruksGorehorn.java
@@ -0,0 +1,32 @@
+package mage.cards.g;
+
+import mage.MageInt;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class GarruksGorehorn extends CardImpl {
+
+    public GarruksGorehorn(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}");
+
+        this.subtype.add(SubType.BEAST);
+        this.power = new MageInt(7);
+        this.toughness = new MageInt(3);
+    }
+
+    private GarruksGorehorn(final GarruksGorehorn card) {
+        super(card);
+    }
+
+    @Override
+    public GarruksGorehorn copy() {
+        return new GarruksGorehorn(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index d1ace7036e..8ad9cc1b23 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -65,6 +65,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Fungal Rebirth", 182, Rarity.UNCOMMON, mage.cards.f.FungalRebirth.class));
         cards.add(new SetCardInfo("Furious Rise", 144, Rarity.UNCOMMON, mage.cards.f.FuriousRise.class));
         cards.add(new SetCardInfo("Gadrak, the Crown-Scourge", 146, Rarity.RARE, mage.cards.g.GadrakTheCrownScourge.class));
+        cards.add(new SetCardInfo("Garruk's Gorehorn", 184, Rarity.COMMON, mage.cards.g.GarruksGorehorn.class));
         cards.add(new SetCardInfo("Garruk's Warsteed", 337, Rarity.RARE, mage.cards.g.GarruksWarsteed.class));
         cards.add(new SetCardInfo("Garruk, Savage Herald", 336, Rarity.MYTHIC, mage.cards.g.GarrukSavageHerald.class));
         cards.add(new SetCardInfo("Goblin Arsonist", 147, Rarity.COMMON, mage.cards.g.GoblinArsonist.class));

From 578f481a36d0bb93a738ed980287ed805b3919bc Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 11 Jun 2020 18:59:18 -0400
Subject: [PATCH 215/586] Implemented Lofty Denial

---
 Mage.Sets/src/mage/cards/l/LoftyDenial.java | 53 +++++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java    |  1 +
 2 files changed, 54 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/l/LoftyDenial.java

diff --git a/Mage.Sets/src/mage/cards/l/LoftyDenial.java b/Mage.Sets/src/mage/cards/l/LoftyDenial.java
new file mode 100644
index 0000000000..30d834e78c
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/l/LoftyDenial.java
@@ -0,0 +1,53 @@
+package mage.cards.l;
+
+import mage.abilities.condition.Condition;
+import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
+import mage.abilities.costs.mana.GenericManaCost;
+import mage.abilities.decorator.ConditionalOneShotEffect;
+import mage.abilities.effects.common.CounterUnlessPaysEffect;
+import mage.abilities.keyword.FlyingAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.filter.FilterPermanent;
+import mage.filter.common.FilterControlledCreaturePermanent;
+import mage.filter.predicate.mageobject.AbilityPredicate;
+import mage.target.TargetSpell;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class LoftyDenial extends CardImpl {
+
+    private static final FilterPermanent filter = new FilterControlledCreaturePermanent();
+
+    static {
+        filter.add(new AbilityPredicate(FlyingAbility.class));
+    }
+
+    private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter);
+
+    public LoftyDenial(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}");
+
+        // Counter target spell unless its controller pays {1}. If you control a creature with flying, counter that spell unless its controller pays {4} instead.
+        this.getSpellAbility().addEffect(new ConditionalOneShotEffect(
+                new CounterUnlessPaysEffect(new GenericManaCost(4)),
+                new CounterUnlessPaysEffect(new GenericManaCost(1)),
+                condition, "counter target spell unless its controller pays {1}. " +
+                "If you control a creature with flying, counter that spell unless its controller pays {4} instead"
+        ));
+        this.getSpellAbility().addTarget(new TargetSpell());
+    }
+
+    private LoftyDenial(final LoftyDenial card) {
+        super(card);
+    }
+
+    @Override
+    public LoftyDenial copy() {
+        return new LoftyDenial(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 8ad9cc1b23..4ad9efea35 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -87,6 +87,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Liliana, Death Mage", 328, Rarity.MYTHIC, mage.cards.l.LilianaDeathMage.class));
         cards.add(new SetCardInfo("Liliana, Waker of the Dead", 108, Rarity.MYTHIC, mage.cards.l.LilianaWakerOfTheDead.class));
         cards.add(new SetCardInfo("Llanowar Visionary", 193, Rarity.COMMON, mage.cards.l.LlanowarVisionary.class));
+        cards.add(new SetCardInfo("Lofty Denial", 56, Rarity.COMMON, mage.cards.l.LoftyDenial.class));
         cards.add(new SetCardInfo("Lorescale Coatl", 221, Rarity.UNCOMMON, mage.cards.l.LorescaleCoatl.class));
         cards.add(new SetCardInfo("Malefic Scythe", 112, Rarity.UNCOMMON, mage.cards.m.MaleficScythe.class));
         cards.add(new SetCardInfo("Mangara, the Diplomat", 27, Rarity.MYTHIC, mage.cards.m.MangaraTheDiplomat.class));

From feefd0ae5f08f83f01eedff275ac8d6b63155462 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 11 Jun 2020 18:59:29 -0400
Subject: [PATCH 216/586] Implemented Miscast

---
 Mage.Sets/src/mage/cards/m/Miscast.java  | 34 ++++++++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java |  1 +
 2 files changed, 35 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/m/Miscast.java

diff --git a/Mage.Sets/src/mage/cards/m/Miscast.java b/Mage.Sets/src/mage/cards/m/Miscast.java
new file mode 100644
index 0000000000..1bfb7ff2ce
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/m/Miscast.java
@@ -0,0 +1,34 @@
+package mage.cards.m;
+
+import mage.abilities.costs.mana.GenericManaCost;
+import mage.abilities.effects.common.CounterUnlessPaysEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.filter.StaticFilters;
+import mage.target.TargetSpell;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class Miscast extends CardImpl {
+
+    public Miscast(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}");
+
+        // Counter target instant or sorcery spell unless its controller pays {3}.
+        this.getSpellAbility().addEffect(new CounterUnlessPaysEffect(new GenericManaCost(3)));
+        this.getSpellAbility().addTarget(new TargetSpell(StaticFilters.FILTER_SPELL_INSTANT_OR_SORCERY));
+    }
+
+    private Miscast(final Miscast card) {
+        super(card);
+    }
+
+    @Override
+    public Miscast copy() {
+        return new Miscast(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 4ad9efea35..511d79d9e9 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -94,6 +94,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Massacre Wurm", 114, Rarity.MYTHIC, mage.cards.m.MassacreWurm.class));
         cards.add(new SetCardInfo("Mazemind Tome", 232, Rarity.RARE, mage.cards.m.MazemindTome.class));
         cards.add(new SetCardInfo("Meteorite", 233, Rarity.UNCOMMON, mage.cards.m.Meteorite.class));
+        cards.add(new SetCardInfo("Miscast", 57, Rarity.UNCOMMON, mage.cards.m.Miscast.class));
         cards.add(new SetCardInfo("Mountain", 312, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Mystic Skyfish", 326, Rarity.COMMON, mage.cards.m.MysticSkyfish.class));
         cards.add(new SetCardInfo("Necromentia", 116, Rarity.RARE, mage.cards.n.Necromentia.class));

From 6b881bca03983f3e12bb0a25486ed18cabf49f46 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 11 Jun 2020 19:06:07 -0400
Subject: [PATCH 217/586] Implemented Rambunctious Mutt

---
 .../src/mage/cards/r/RambunctiousMutt.java    | 51 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 52 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/r/RambunctiousMutt.java

diff --git a/Mage.Sets/src/mage/cards/r/RambunctiousMutt.java b/Mage.Sets/src/mage/cards/r/RambunctiousMutt.java
new file mode 100644
index 0000000000..f751f3d529
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/r/RambunctiousMutt.java
@@ -0,0 +1,51 @@
+package mage.cards.r;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.effects.common.DestroyTargetEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.TargetController;
+import mage.filter.FilterPermanent;
+import mage.filter.common.FilterArtifactOrEnchantmentPermanent;
+import mage.target.TargetPermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class RambunctiousMutt extends CardImpl {
+
+    private static final FilterPermanent filter
+            = new FilterArtifactOrEnchantmentPermanent("artifact or enchantment an opponent controls");
+
+    static {
+        filter.add(TargetController.OPPONENT.getControllerPredicate());
+    }
+
+    public RambunctiousMutt(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}{W}");
+
+        this.subtype.add(SubType.DOG);
+        this.power = new MageInt(3);
+        this.toughness = new MageInt(4);
+
+        // When Rambunctious Mutt enters the battlefield, destroy target artifact or enchantment an opponent controls.
+        Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect());
+        ability.addTarget(new TargetPermanent(filter));
+        this.addAbility(ability);
+    }
+
+    private RambunctiousMutt(final RambunctiousMutt card) {
+        super(card);
+    }
+
+    @Override
+    public RambunctiousMutt copy() {
+        return new RambunctiousMutt(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 511d79d9e9..585709ac1f 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -108,6 +108,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Quirion Dryad", 198, Rarity.UNCOMMON, mage.cards.q.QuirionDryad.class));
         cards.add(new SetCardInfo("Radiant Fountain", 248, Rarity.COMMON, mage.cards.r.RadiantFountain.class));
         cards.add(new SetCardInfo("Rain of Revelation", 61, Rarity.UNCOMMON, mage.cards.r.RainOfRevelation.class));
+        cards.add(new SetCardInfo("Rambunctious Mutt", 30, Rarity.COMMON, mage.cards.r.RambunctiousMutt.class));
         cards.add(new SetCardInfo("Revitalize", 31, Rarity.COMMON, mage.cards.r.Revitalize.class));
         cards.add(new SetCardInfo("Rewind", 63, Rarity.UNCOMMON, mage.cards.r.Rewind.class));
         cards.add(new SetCardInfo("Riddleform", 64, Rarity.UNCOMMON, mage.cards.r.Riddleform.class));

From 1141a4685d74a4f4c8da5eaecfccb9eae91a333b Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 11 Jun 2020 19:19:49 -0400
Subject: [PATCH 218/586] Implemented Tempered Veteran

---
 .../src/mage/cards/t/TemperedVeteran.java     | 67 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 68 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/t/TemperedVeteran.java

diff --git a/Mage.Sets/src/mage/cards/t/TemperedVeteran.java b/Mage.Sets/src/mage/cards/t/TemperedVeteran.java
new file mode 100644
index 0000000000..d9470f439d
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/t/TemperedVeteran.java
@@ -0,0 +1,67 @@
+package mage.cards.t;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.common.TapSourceCost;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.effects.common.counter.AddCountersTargetEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.counters.CounterType;
+import mage.filter.FilterPermanent;
+import mage.filter.common.FilterCreaturePermanent;
+import mage.filter.predicate.permanent.CounterPredicate;
+import mage.target.TargetPermanent;
+import mage.target.common.TargetCreaturePermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class TemperedVeteran extends CardImpl {
+
+    private static final FilterPermanent filter
+            = new FilterCreaturePermanent("creature with a +1/+1 counter on it");
+
+    static {
+        filter.add(new CounterPredicate(CounterType.P1P1));
+    }
+
+    public TemperedVeteran(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}");
+
+        this.subtype.add(SubType.HUMAN);
+        this.subtype.add(SubType.KNIGHT);
+        this.power = new MageInt(1);
+        this.toughness = new MageInt(2);
+
+        // {W}, {T}: Put a +1/+1 counter on target creature with a +1/+1 counter on it.
+        Ability ability = new SimpleActivatedAbility(
+                new AddCountersTargetEffect(CounterType.P1P1.createInstance()), new ManaCostsImpl("{W}")
+        );
+        ability.addCost(new TapSourceCost());
+        ability.addTarget(new TargetPermanent(filter));
+        this.addAbility(ability);
+
+        // {4}{W}{W}, {T}: Put a +1/+1 counter on target creature.
+        ability = new SimpleActivatedAbility(
+                new AddCountersTargetEffect(CounterType.P1P1.createInstance()), new ManaCostsImpl("{4}{W}{W}")
+        );
+        ability.addCost(new TapSourceCost());
+        ability.addTarget(new TargetCreaturePermanent());
+        this.addAbility(ability);
+    }
+
+    private TemperedVeteran(final TemperedVeteran card) {
+        super(card);
+    }
+
+    @Override
+    public TemperedVeteran copy() {
+        return new TemperedVeteran(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 585709ac1f..821c0d4bc8 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -131,6 +131,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Teferi's Tutelage", 78, Rarity.UNCOMMON, mage.cards.t.TeferisTutelage.class));
         cards.add(new SetCardInfo("Teferi's Wavecaster", 327, Rarity.RARE, mage.cards.t.TeferisWavecaster.class));
         cards.add(new SetCardInfo("Teferi, Master of Time", 75, Rarity.MYTHIC, mage.cards.t.TeferiMasterOfTime.class));
+        cards.add(new SetCardInfo("Tempered Veteran", 41, Rarity.UNCOMMON, mage.cards.t.TemperedVeteran.class));
         cards.add(new SetCardInfo("Temple of Epiphany", 252, Rarity.RARE, mage.cards.t.TempleOfEpiphany.class));
         cards.add(new SetCardInfo("Temple of Malady", 253, Rarity.RARE, mage.cards.t.TempleOfMalady.class));
         cards.add(new SetCardInfo("Temple of Mystery", 254, Rarity.RARE, mage.cards.t.TempleOfMystery.class));

From a383bffc9b0d16b979dab59817975a9fe4460e96 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 11 Jun 2020 19:34:54 -0400
Subject: [PATCH 219/586] Implemented Thieves' Guild Enforcer

---
 .../mage/cards/t/ThievesGuildEnforcer.java    | 89 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 90 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/t/ThievesGuildEnforcer.java

diff --git a/Mage.Sets/src/mage/cards/t/ThievesGuildEnforcer.java b/Mage.Sets/src/mage/cards/t/ThievesGuildEnforcer.java
new file mode 100644
index 0000000000..2e87972f01
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/t/ThievesGuildEnforcer.java
@@ -0,0 +1,89 @@
+package mage.cards.t;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.condition.Condition;
+import mage.abilities.decorator.ConditionalContinuousEffect;
+import mage.abilities.effects.common.PutTopCardOfLibraryIntoGraveEachPlayerEffect;
+import mage.abilities.effects.common.continuous.BoostSourceEffect;
+import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
+import mage.abilities.keyword.DeathtouchAbility;
+import mage.abilities.keyword.FlashAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+import mage.constants.TargetController;
+import mage.filter.FilterPermanent;
+import mage.game.Game;
+import mage.players.Library;
+import mage.players.Player;
+
+import java.util.Objects;
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class ThievesGuildEnforcer extends CardImpl {
+
+    private static final FilterPermanent filter = new FilterPermanent(SubType.ROGUE, "{this} or another Rogue");
+
+    public ThievesGuildEnforcer(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}");
+
+        this.subtype.add(SubType.HUMAN);
+        this.subtype.add(SubType.ROGUE);
+        this.power = new MageInt(1);
+        this.toughness = new MageInt(1);
+
+        // Flash
+        this.addAbility(FlashAbility.getInstance());
+
+        // Whenever Thieves' Guild Enforcer or another Rogue enters the battlefield under your control, each opponent mills two cards.
+        this.addAbility(new EntersBattlefieldControlledTriggeredAbility(
+                new PutTopCardOfLibraryIntoGraveEachPlayerEffect(
+                        2, TargetController.OPPONENT
+                ), filter
+        ));
+
+        // As long as an opponent has eight or more cards in their graveyard, Thieves' Guild Enforcer gets +2/+1 and has deathtouch.
+        Ability ability = new SimpleStaticAbility(new ConditionalContinuousEffect(
+                new BoostSourceEffect(2, 1, Duration.WhileOnBattlefield),
+                ThievesGuildEnforcerCondition.instance, "as long as an opponent " +
+                "has eight or more cards in their graveyard, {this} gets +2/+1"
+        ));
+        ability.addEffect(new ConditionalContinuousEffect(new GainAbilityControlledEffect(
+                DeathtouchAbility.getInstance(), Duration.WhileOnBattlefield
+        ), ThievesGuildEnforcerCondition.instance, "and has deathtouch"));
+        this.addAbility(ability);
+    }
+
+    private ThievesGuildEnforcer(final ThievesGuildEnforcer card) {
+        super(card);
+    }
+
+    @Override
+    public ThievesGuildEnforcer copy() {
+        return new ThievesGuildEnforcer(this);
+    }
+}
+
+enum ThievesGuildEnforcerCondition implements Condition {
+    instance;
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        return game
+                .getOpponents(source.getControllerId())
+                .stream()
+                .map(game::getPlayer)
+                .filter(Objects::nonNull)
+                .map(Player::getLibrary)
+                .mapToInt(Library::size)
+                .anyMatch(i -> i >= 8);
+    }
+}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 821c0d4bc8..eb75b80dd8 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -137,6 +137,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Temple of Mystery", 254, Rarity.RARE, mage.cards.t.TempleOfMystery.class));
         cards.add(new SetCardInfo("Temple of Silence", 255, Rarity.RARE, mage.cards.t.TempleOfSilence.class));
         cards.add(new SetCardInfo("Temple of Triumph", 256, Rarity.RARE, mage.cards.t.TempleOfTriumph.class));
+        cards.add(new SetCardInfo("Thieves' Guild Enforcer", 125, Rarity.RARE, mage.cards.t.ThievesGuildEnforcer.class));
         cards.add(new SetCardInfo("Tide Skimmer", 79, Rarity.UNCOMMON, mage.cards.t.TideSkimmer.class));
         cards.add(new SetCardInfo("Tormod's Crypt", 241, Rarity.UNCOMMON, mage.cards.t.TormodsCrypt.class));
         cards.add(new SetCardInfo("Ugin, the Spirit Dragon", 1, Rarity.MYTHIC, mage.cards.u.UginTheSpiritDragon.class));

From f7173459b97cd61fac31d6be5e8e3103e9e3f04f Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 11 Jun 2020 19:45:29 -0400
Subject: [PATCH 220/586] Implemented Volcanic Salvo

---
 Mage.Sets/src/mage/cards/v/VolcanicSalvo.java | 83 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 84 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/v/VolcanicSalvo.java

diff --git a/Mage.Sets/src/mage/cards/v/VolcanicSalvo.java b/Mage.Sets/src/mage/cards/v/VolcanicSalvo.java
new file mode 100644
index 0000000000..7c17f54e1d
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/v/VolcanicSalvo.java
@@ -0,0 +1,83 @@
+package mage.cards.v;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.SpellAbility;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.effects.common.DamageTargetEffect;
+import mage.abilities.effects.common.cost.CostModificationEffectImpl;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.*;
+import mage.filter.StaticFilters;
+import mage.game.Game;
+import mage.game.permanent.Permanent;
+import mage.target.common.TargetCreatureOrPlaneswalker;
+import mage.util.CardUtil;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class VolcanicSalvo extends CardImpl {
+
+    public VolcanicSalvo(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{10}{R}{R}");
+
+        // This spell costs {X} less to cast, where X is the total power of creatures you control.
+        this.addAbility(new SimpleStaticAbility(Zone.ALL, new VolcanicSalvoCostReductionEffect()));
+
+        // Volcanic Salvo deals 6 damage to each of up to two target creatures and/or planeswalkers.
+        this.getSpellAbility().addEffect(new DamageTargetEffect(6)
+                .setText("{this} deals 6 damage to each of up to two target creatures and/or planeswalkers")
+        );
+        this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker(0, 2));
+    }
+
+    private VolcanicSalvo(final VolcanicSalvo card) {
+        super(card);
+    }
+
+    @Override
+    public VolcanicSalvo copy() {
+        return new VolcanicSalvo(this);
+    }
+}
+
+class VolcanicSalvoCostReductionEffect extends CostModificationEffectImpl {
+
+    VolcanicSalvoCostReductionEffect() {
+        super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.REDUCE_COST);
+        staticText = "This spell costs {X} less to cast, where X is the total power of creatures you control";
+    }
+
+    private VolcanicSalvoCostReductionEffect(final VolcanicSalvoCostReductionEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source, Ability abilityToModify) {
+        int reductionAmount = game.getBattlefield()
+                .getAllActivePermanents(
+                        StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), game
+                ).stream()
+                .map(Permanent::getPower)
+                .mapToInt(MageInt::getValue)
+                .sum();
+        CardUtil.reduceCost(abilityToModify, Math.max(0, reductionAmount));
+        return true;
+    }
+
+    @Override
+    public boolean applies(Ability abilityToModify, Ability source, Game game) {
+        return abilityToModify instanceof SpellAbility
+                && abilityToModify.getSourceId().equals(source.getSourceId())
+                && game.getCard(abilityToModify.getSourceId()) != null;
+    }
+
+    @Override
+    public VolcanicSalvoCostReductionEffect copy() {
+        return new VolcanicSalvoCostReductionEffect(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index eb75b80dd8..18036aff0f 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -144,6 +144,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Valorous Steed", 42, Rarity.COMMON, mage.cards.v.ValorousSteed.class));
         cards.add(new SetCardInfo("Village Rites", 126, Rarity.COMMON, mage.cards.v.VillageRites.class));
         cards.add(new SetCardInfo("Vito, Thorn of the Dusk Rose", 127, Rarity.RARE, mage.cards.v.VitoThornOfTheDuskRose.class));
+        cards.add(new SetCardInfo("Volcanic Salvo", 172, Rarity.RARE, mage.cards.v.VolcanicSalvo.class));
         cards.add(new SetCardInfo("Vryn Wingmare", 43, Rarity.UNCOMMON, mage.cards.v.VrynWingmare.class));
         cards.add(new SetCardInfo("Warden of the Woods", 213, Rarity.UNCOMMON, mage.cards.w.WardenOfTheWoods.class));
         cards.add(new SetCardInfo("Wildwood Patrol", 339, Rarity.COMMON, mage.cards.w.WildwoodPatrol.class));

From 023b0032160d378124e7b93ab34dc36474b3bf20 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 11 Jun 2020 19:48:27 -0400
Subject: [PATCH 221/586] Implemented Trufflesnout

---
 Mage.Sets/src/mage/cards/t/Trufflesnout.java | 48 ++++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java     |  1 +
 2 files changed, 49 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/t/Trufflesnout.java

diff --git a/Mage.Sets/src/mage/cards/t/Trufflesnout.java b/Mage.Sets/src/mage/cards/t/Trufflesnout.java
new file mode 100644
index 0000000000..4a60fa0da0
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/t/Trufflesnout.java
@@ -0,0 +1,48 @@
+package mage.cards.t;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.Mode;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.effects.common.GainLifeEffect;
+import mage.abilities.effects.common.counter.AddCountersSourceEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.counters.CounterType;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class Trufflesnout extends CardImpl {
+
+    public Trufflesnout(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}");
+
+        this.subtype.add(SubType.BOAR);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(2);
+
+        // When Trufflesnout enters the battlefield, choose one —
+        // • Put a +1/+1 counter on Trufflesnout.
+        Ability ability = new EntersBattlefieldTriggeredAbility(
+                new AddCountersSourceEffect(CounterType.P1P1.createInstance())
+        );
+
+        // • You gain 4 life.
+        ability.addMode(new Mode(new GainLifeEffect(4)));
+        this.addAbility(ability);
+    }
+
+    private Trufflesnout(final Trufflesnout card) {
+        super(card);
+    }
+
+    @Override
+    public Trufflesnout copy() {
+        return new Trufflesnout(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 18036aff0f..19e46b01da 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -140,6 +140,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Thieves' Guild Enforcer", 125, Rarity.RARE, mage.cards.t.ThievesGuildEnforcer.class));
         cards.add(new SetCardInfo("Tide Skimmer", 79, Rarity.UNCOMMON, mage.cards.t.TideSkimmer.class));
         cards.add(new SetCardInfo("Tormod's Crypt", 241, Rarity.UNCOMMON, mage.cards.t.TormodsCrypt.class));
+        cards.add(new SetCardInfo("Trufflesnout", 212, Rarity.COMMON, mage.cards.t.Trufflesnout.class));
         cards.add(new SetCardInfo("Ugin, the Spirit Dragon", 1, Rarity.MYTHIC, mage.cards.u.UginTheSpiritDragon.class));
         cards.add(new SetCardInfo("Valorous Steed", 42, Rarity.COMMON, mage.cards.v.ValorousSteed.class));
         cards.add(new SetCardInfo("Village Rites", 126, Rarity.COMMON, mage.cards.v.VillageRites.class));

From 7fb4e638b73696ca1e3b496d595ea2c4e2a9d49b Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 11 Jun 2020 19:52:45 -0400
Subject: [PATCH 222/586] Implemented Wildwood Scourge

---
 .../src/mage/cards/w/WildwoodScourge.java     | 88 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 89 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/w/WildwoodScourge.java

diff --git a/Mage.Sets/src/mage/cards/w/WildwoodScourge.java b/Mage.Sets/src/mage/cards/w/WildwoodScourge.java
new file mode 100644
index 0000000000..71ed9a279f
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/w/WildwoodScourge.java
@@ -0,0 +1,88 @@
+package mage.cards.w;
+
+import mage.MageInt;
+import mage.abilities.TriggeredAbilityImpl;
+import mage.abilities.common.EntersBattlefieldAbility;
+import mage.abilities.effects.common.EntersBattlefieldWithXCountersEffect;
+import mage.abilities.effects.common.counter.AddCountersSourceEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.Zone;
+import mage.counters.CounterType;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+import mage.game.permanent.Permanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class WildwoodScourge extends CardImpl {
+
+    public WildwoodScourge(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{X}{G}");
+
+        this.subtype.add(SubType.HYDRA);
+        this.power = new MageInt(0);
+        this.toughness = new MageInt(0);
+
+        // Wildwood Scourge enters the battlefield with X +1/+1 counters on it.
+        this.addAbility(new EntersBattlefieldAbility(new EntersBattlefieldWithXCountersEffect(CounterType.P1P1.createInstance())));
+
+        // Whenever one or more +1/+1 counters are put on another non-Hydra creature you control, put a +1/+1 counter on Wildwood Scourge.
+        this.addAbility(new EnduringScalelordTriggeredAbility());
+    }
+
+    private WildwoodScourge(final WildwoodScourge card) {
+        super(card);
+    }
+
+    @Override
+    public WildwoodScourge copy() {
+        return new WildwoodScourge(this);
+    }
+}
+
+class EnduringScalelordTriggeredAbility extends TriggeredAbilityImpl {
+
+    EnduringScalelordTriggeredAbility() {
+        super(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false);
+    }
+
+    private EnduringScalelordTriggeredAbility(final EnduringScalelordTriggeredAbility ability) {
+        super(ability);
+    }
+
+    @Override
+    public EnduringScalelordTriggeredAbility copy() {
+        return new EnduringScalelordTriggeredAbility(this);
+    }
+
+    @Override
+    public boolean checkEventType(GameEvent event, Game game) {
+        return event.getType() == GameEvent.EventType.COUNTERS_ADDED;
+    }
+
+    @Override
+    public boolean checkTrigger(GameEvent event, Game game) {
+        if (event.getData().equals(CounterType.P1P1.getName())) {
+            Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId());
+            if (permanent == null) {
+                permanent = game.getPermanentEntering(event.getTargetId());
+            }
+            return (permanent != null
+                    && !event.getTargetId().equals(this.getSourceId())
+                    && permanent.isCreature()
+                    && permanent.isControlledBy(this.getControllerId()));
+        }
+        return false;
+    }
+
+    @Override
+    public String getRule() {
+        return "Whenever one or more +1/+1 counters are put on another creature you control, put a +1/+1 counter on {this}.";
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 19e46b01da..2235bd0109 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -149,5 +149,6 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Vryn Wingmare", 43, Rarity.UNCOMMON, mage.cards.v.VrynWingmare.class));
         cards.add(new SetCardInfo("Warden of the Woods", 213, Rarity.UNCOMMON, mage.cards.w.WardenOfTheWoods.class));
         cards.add(new SetCardInfo("Wildwood Patrol", 339, Rarity.COMMON, mage.cards.w.WildwoodPatrol.class));
+        cards.add(new SetCardInfo("Wildwood Scourge", 214, Rarity.UNCOMMON, mage.cards.w.WildwoodScourge.class));
     }
 }

From 2fd2d170b88eeb528706f080d710b597f49d8989 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 11 Jun 2020 19:57:24 -0400
Subject: [PATCH 223/586] Implemented Liliana's Steward

---
 .../src/mage/cards/l/LilianasSteward.java     | 47 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 48 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/l/LilianasSteward.java

diff --git a/Mage.Sets/src/mage/cards/l/LilianasSteward.java b/Mage.Sets/src/mage/cards/l/LilianasSteward.java
new file mode 100644
index 0000000000..9e7c7a5e4d
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/l/LilianasSteward.java
@@ -0,0 +1,47 @@
+package mage.cards.l;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.ActivateAsSorceryActivatedAbility;
+import mage.abilities.costs.common.SacrificeSourceCost;
+import mage.abilities.costs.common.TapSourceCost;
+import mage.abilities.effects.common.discard.DiscardTargetEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.Zone;
+import mage.target.common.TargetOpponent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class LilianasSteward extends CardImpl {
+
+    public LilianasSteward(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}");
+
+        this.subtype.add(SubType.ZOMBIE);
+        this.power = new MageInt(1);
+        this.toughness = new MageInt(2);
+
+        // {T}, Sacrifice Liliana's Steward: Target opponent discards a card. Activate this ability only any time you could cast a sorcery.
+        Ability ability = new ActivateAsSorceryActivatedAbility(
+                Zone.BATTLEFIELD, new DiscardTargetEffect(1), new TapSourceCost()
+        );
+        ability.addCost(new SacrificeSourceCost());
+        ability.addTarget(new TargetOpponent());
+        this.addAbility(ability);
+    }
+
+    private LilianasSteward(final LilianasSteward card) {
+        super(card);
+    }
+
+    @Override
+    public LilianasSteward copy() {
+        return new LilianasSteward(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 2235bd0109..272257b659 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -84,6 +84,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Keral Keep Disciples", 334, Rarity.UNCOMMON, mage.cards.k.KeralKeepDisciples.class));
         cards.add(new SetCardInfo("Liliana's Scorn", 329, Rarity.RARE, mage.cards.l.LilianasScorn.class));
         cards.add(new SetCardInfo("Liliana's Scrounger", 330, Rarity.UNCOMMON, mage.cards.l.LilianasScrounger.class));
+        cards.add(new SetCardInfo("Liliana's Steward", 111, Rarity.COMMON, mage.cards.l.LilianasSteward.class));
         cards.add(new SetCardInfo("Liliana, Death Mage", 328, Rarity.MYTHIC, mage.cards.l.LilianaDeathMage.class));
         cards.add(new SetCardInfo("Liliana, Waker of the Dead", 108, Rarity.MYTHIC, mage.cards.l.LilianaWakerOfTheDead.class));
         cards.add(new SetCardInfo("Llanowar Visionary", 193, Rarity.COMMON, mage.cards.l.LlanowarVisionary.class));

From 0eaaee076427c01ea98a1a827b2e3d5bfcc20990 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 11 Jun 2020 20:09:47 -0400
Subject: [PATCH 224/586] Implemented Liliana's Devotee

---
 .../src/mage/cards/l/LilianasDevotee.java     | 62 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 63 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/l/LilianasDevotee.java

diff --git a/Mage.Sets/src/mage/cards/l/LilianasDevotee.java b/Mage.Sets/src/mage/cards/l/LilianasDevotee.java
new file mode 100644
index 0000000000..32a0236aeb
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/l/LilianasDevotee.java
@@ -0,0 +1,62 @@
+package mage.cards.l;
+
+import mage.MageInt;
+import mage.abilities.common.BeginningOfEndStepTriggeredAbility;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.condition.common.MorbidCondition;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
+import mage.abilities.effects.common.CreateTokenEffect;
+import mage.abilities.effects.common.DoIfCostPaid;
+import mage.abilities.effects.common.continuous.BoostControlledEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+import mage.constants.TargetController;
+import mage.filter.common.FilterCreaturePermanent;
+import mage.game.permanent.token.ZombieToken;
+import mage.watchers.common.MorbidWatcher;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class LilianasDevotee extends CardImpl {
+
+    private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(SubType.ZOMBIE, "Zombies");
+
+    public LilianasDevotee(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}");
+
+        this.subtype.add(SubType.HUMAN);
+        this.subtype.add(SubType.WARLOCK);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(3);
+
+        // Zombies you control get +1/+0.
+        this.addAbility(new SimpleStaticAbility(new BoostControlledEffect(
+                1, 0, Duration.WhileOnBattlefield, filter
+        )));
+
+        // At the beginning of your end step, if a creature died this turn, you may pay {1}{B}. If you do, create a 2/2 black Zombie creature token.
+        this.addAbility(new ConditionalInterveningIfTriggeredAbility(
+                new BeginningOfEndStepTriggeredAbility(new DoIfCostPaid(
+                        new CreateTokenEffect(new ZombieToken()), new ManaCostsImpl("{1}{B}")
+                ), TargetController.YOU, false), MorbidCondition.instance,
+                "At the beginning of your end step, if a creature died this turn, " +
+                        "you may pay {1}{B}. If you do, create a 2/2 black Zombie creature token."
+        ), new MorbidWatcher());
+    }
+
+    private LilianasDevotee(final LilianasDevotee card) {
+        super(card);
+    }
+
+    @Override
+    public LilianasDevotee copy() {
+        return new LilianasDevotee(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 272257b659..8e2f1cf734 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -82,6 +82,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Jeskai Elder", 53, Rarity.UNCOMMON, mage.cards.j.JeskaiElder.class));
         cards.add(new SetCardInfo("Jolrael, Mwonvuli Recluse", 191, Rarity.RARE, mage.cards.j.JolraelMwonvuliRecluse.class));
         cards.add(new SetCardInfo("Keral Keep Disciples", 334, Rarity.UNCOMMON, mage.cards.k.KeralKeepDisciples.class));
+        cards.add(new SetCardInfo("Liliana's Devotee", 109, Rarity.UNCOMMON, mage.cards.l.LilianasDevotee.class));
         cards.add(new SetCardInfo("Liliana's Scorn", 329, Rarity.RARE, mage.cards.l.LilianasScorn.class));
         cards.add(new SetCardInfo("Liliana's Scrounger", 330, Rarity.UNCOMMON, mage.cards.l.LilianasScrounger.class));
         cards.add(new SetCardInfo("Liliana's Steward", 111, Rarity.COMMON, mage.cards.l.LilianasSteward.class));

From ca6c6dc8faba95ec6ca4f43279d46b5455899eb3 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 11 Jun 2020 21:01:52 -0400
Subject: [PATCH 225/586] Implementred Fiery Emancipation

---
 .../src/mage/cards/f/FieryEmancipation.java   | 77 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 78 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/f/FieryEmancipation.java

diff --git a/Mage.Sets/src/mage/cards/f/FieryEmancipation.java b/Mage.Sets/src/mage/cards/f/FieryEmancipation.java
new file mode 100644
index 0000000000..0c6009ef96
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/f/FieryEmancipation.java
@@ -0,0 +1,77 @@
+package mage.cards.f;
+
+import mage.abilities.Ability;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.effects.ReplacementEffectImpl;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.Outcome;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+import mage.util.CardUtil;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class FieryEmancipation extends CardImpl {
+
+    public FieryEmancipation(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{R}{R}{R}");
+
+        // If a source you control would deal damage to a permanent or player, it deals triple that damage to that permanent or player instead.
+        this.addAbility(new SimpleStaticAbility(new FieryEmancipationEffect()));
+    }
+
+    private FieryEmancipation(final FieryEmancipation card) {
+        super(card);
+    }
+
+    @Override
+    public FieryEmancipation copy() {
+        return new FieryEmancipation(this);
+    }
+}
+
+class FieryEmancipationEffect extends ReplacementEffectImpl {
+
+    FieryEmancipationEffect() {
+        super(Duration.WhileOnBattlefield, Outcome.Damage);
+        staticText = "If a source you control would deal damage to a permanent or player, " +
+                "it deals triple that damage to that permanent or player instead";
+    }
+
+    private FieryEmancipationEffect(final FieryEmancipationEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public FieryEmancipationEffect copy() {
+        return new FieryEmancipationEffect(this);
+    }
+
+    @Override
+    public boolean checksEventType(GameEvent event, Game game) {
+        return event.getType().equals(GameEvent.EventType.DAMAGE_PLAYER)
+                || event.getType().equals(GameEvent.EventType.DAMAGE_CREATURE)
+                || event.getType().equals(GameEvent.EventType.DAMAGE_PLANESWALKER);
+    }
+
+    @Override
+    public boolean applies(GameEvent event, Ability source, Game game) {
+        return game.getControllerId(event.getSourceId()).equals(source.getControllerId());
+    }
+
+    @Override
+    public boolean replaceEvent(GameEvent event, Ability source, Game game) {
+        event.setAmount(CardUtil.addWithOverflowCheck(
+                CardUtil.addWithOverflowCheck(
+                        event.getAmount(), event.getAmount()
+                ), event.getAmount()
+        ));
+        return false;
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 8e2f1cf734..36c03c355f 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -62,6 +62,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Fabled Passage", 246, Rarity.RARE, mage.cards.f.FabledPassage.class));
         cards.add(new SetCardInfo("Faith's Fetters", 17, Rarity.UNCOMMON, mage.cards.f.FaithsFetters.class));
         cards.add(new SetCardInfo("Fierce Empath", 181, Rarity.UNCOMMON, mage.cards.f.FierceEmpath.class));
+        cards.add(new SetCardInfo("Fiery Emancipation", 143, Rarity.MYTHIC, mage.cards.f.FieryEmancipation.class));
         cards.add(new SetCardInfo("Fungal Rebirth", 182, Rarity.UNCOMMON, mage.cards.f.FungalRebirth.class));
         cards.add(new SetCardInfo("Furious Rise", 144, Rarity.UNCOMMON, mage.cards.f.FuriousRise.class));
         cards.add(new SetCardInfo("Gadrak, the Crown-Scourge", 146, Rarity.RARE, mage.cards.g.GadrakTheCrownScourge.class));

From d05851a6f4f056abb31b99be745ab12f43a443df Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 11 Jun 2020 21:19:15 -0400
Subject: [PATCH 226/586] Implemented Stormwing Entity

---
 .../src/mage/cards/s/StormwingEntity.java     | 109 ++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |   1 +
 2 files changed, 110 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/s/StormwingEntity.java

diff --git a/Mage.Sets/src/mage/cards/s/StormwingEntity.java b/Mage.Sets/src/mage/cards/s/StormwingEntity.java
new file mode 100644
index 0000000000..d910fffb42
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/StormwingEntity.java
@@ -0,0 +1,109 @@
+package mage.cards.s;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.condition.Condition;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect;
+import mage.abilities.effects.keyword.ScryEffect;
+import mage.abilities.hint.ConditionHint;
+import mage.abilities.keyword.FlyingAbility;
+import mage.abilities.keyword.ProwessAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.WatcherScope;
+import mage.constants.Zone;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+import mage.game.stack.Spell;
+import mage.watchers.Watcher;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class StormwingEntity extends CardImpl {
+
+    private static final ConditionHint hint = new ConditionHint(
+            StormwingEntityCondition.instance, "You cast an instant or sorcery spell this turn"
+    );
+
+    public StormwingEntity(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{U}");
+
+        this.subtype.add(SubType.ELEMENTAL);
+        this.power = new MageInt(3);
+        this.toughness = new MageInt(3);
+
+        // This spell costs {2}{U} less to cast if you've cast an instant or sorcery spell this turn.
+        this.addAbility(new SimpleStaticAbility(Zone.STACK, new SpellCostReductionSourceEffect(
+                new ManaCostsImpl("{2}{U}"), StormwingEntityCondition.instance
+        )).setRuleAtTheTop(true).addHint(hint), new StormwingEntityWatcher());
+
+        // Flying
+        this.addAbility(FlyingAbility.getInstance());
+
+        // Prowess
+        this.addAbility(new ProwessAbility());
+
+        // When Stormwing Entity enters the battlefield, scry 2.
+        this.addAbility(new EntersBattlefieldTriggeredAbility(new ScryEffect(2)));
+    }
+
+    private StormwingEntity(final StormwingEntity card) {
+        super(card);
+    }
+
+    @Override
+    public StormwingEntity copy() {
+        return new StormwingEntity(this);
+    }
+}
+
+enum StormwingEntityCondition implements Condition {
+    instance;
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        StormwingEntityWatcher watcher = game.getState().getWatcher(StormwingEntityWatcher.class);
+        return watcher != null && watcher.checkPlayer(source.getControllerId());
+    }
+}
+
+class StormwingEntityWatcher extends Watcher {
+
+    private final Set<UUID> playerMap = new HashSet<>();
+
+    StormwingEntityWatcher() {
+        super(WatcherScope.GAME);
+    }
+
+    @Override
+    public void watch(GameEvent event, Game game) {
+        if (event.getType() != GameEvent.EventType.SPELL_CAST) {
+            return;
+        }
+        Spell spell = game.getSpell(event.getTargetId());
+        if (spell == null || !spell.isInstantOrSorcery()) {
+            return;
+        }
+        playerMap.add(event.getPlayerId());
+    }
+
+    @Override
+    public void reset() {
+        playerMap.clear();
+        super.reset();
+    }
+
+    boolean checkPlayer(UUID playerId) {
+        return playerMap.contains(playerId);
+    }
+}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 36c03c355f..06cde24ff9 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -128,6 +128,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Spined Megalodon", 72, Rarity.COMMON, mage.cards.s.SpinedMegalodon.class));
         cards.add(new SetCardInfo("Spirit of Malevolence", 331, Rarity.COMMON, mage.cards.s.SpiritOfMalevolence.class));
         cards.add(new SetCardInfo("Storm Caller", 335, Rarity.COMMON, mage.cards.s.StormCaller.class));
+        cards.add(new SetCardInfo("Stormwing Entity", 73, Rarity.RARE, mage.cards.s.StormwingEntity.class));
         cards.add(new SetCardInfo("Swamp", 311, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Teferi's Ageless Insight", 76, Rarity.RARE, mage.cards.t.TeferisAgelessInsight.class));
         cards.add(new SetCardInfo("Teferi's Protege", 77, Rarity.COMMON, mage.cards.t.TeferisProtege.class));

From 9ebdad04abf5c2870b5e7ab2b1f6665440724fe2 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 11 Jun 2020 21:24:30 -0400
Subject: [PATCH 227/586] Implemented Conclave Mentor

---
 .../src/mage/cards/c/ConclaveMentor.java      | 101 ++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |   1 +
 2 files changed, 102 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/c/ConclaveMentor.java

diff --git a/Mage.Sets/src/mage/cards/c/ConclaveMentor.java b/Mage.Sets/src/mage/cards/c/ConclaveMentor.java
new file mode 100644
index 0000000000..d7ffc91922
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/c/ConclaveMentor.java
@@ -0,0 +1,101 @@
+package mage.cards.c;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.dynamicvalue.DynamicValue;
+import mage.abilities.dynamicvalue.common.SourcePermanentPowerCount;
+import mage.abilities.effects.ReplacementEffectImpl;
+import mage.abilities.effects.common.GainLifeEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.Outcome;
+import mage.constants.SubType;
+import mage.counters.CounterType;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+import mage.game.permanent.Permanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class ConclaveMentor extends CardImpl {
+
+    private static final DynamicValue xValue = new SourcePermanentPowerCount(false);
+
+    public ConclaveMentor(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}{W}");
+
+        this.subtype.add(SubType.CENTAUR);
+        this.subtype.add(SubType.CLERIC);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(2);
+
+        // If one or more +1/+1 counters would be put on a creature you control, that many plus one +1/+1 counters are put on that creature instead.
+        this.addAbility(new SimpleStaticAbility(new ConclaveMentorEffect()));
+
+        // When Conclave Mentor dies, you gain life equal to its power.
+        this.addAbility(new DiesTriggeredAbility(new GainLifeEffect(xValue, "you gain life equal to its power")));
+    }
+
+    private ConclaveMentor(final ConclaveMentor card) {
+        super(card);
+    }
+
+    @Override
+    public ConclaveMentor copy() {
+        return new ConclaveMentor(this);
+    }
+}
+
+class ConclaveMentorEffect extends ReplacementEffectImpl {
+
+    ConclaveMentorEffect() {
+        super(Duration.WhileOnBattlefield, Outcome.BoostCreature, false);
+        staticText = "If one or more +1/+1 counters would be put on a creature you control, " +
+                "that many plus one +1/+1 counters are put on that creature instead";
+    }
+
+    private ConclaveMentorEffect(final ConclaveMentorEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public boolean replaceEvent(GameEvent event, Ability source, Game game) {
+        event.setAmountForCounters(event.getAmount() + 1, true);
+        return false;
+    }
+
+    @Override
+    public boolean checksEventType(GameEvent event, Game game) {
+        return event.getType() == GameEvent.EventType.ADD_COUNTERS;
+    }
+
+    @Override
+    public boolean applies(GameEvent event, Ability source, Game game) {
+        if (event.getData().equals(CounterType.P1P1.getName()) && event.getAmount() > 0) {
+            Permanent permanent = game.getPermanent(event.getTargetId());
+            if (permanent == null) {
+                permanent = game.getPermanentEntering(event.getTargetId());
+            }
+            return permanent != null && permanent.isControlledBy(source.getControllerId())
+                    && permanent.isCreature();
+        }
+        return false;
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        return true;
+    }
+
+    @Override
+    public ConclaveMentorEffect copy() {
+        return new ConclaveMentorEffect(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 06cde24ff9..a5ea785d8b 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -53,6 +53,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Chandra's Magmutt", 137, Rarity.COMMON, mage.cards.c.ChandrasMagmutt.class));
         cards.add(new SetCardInfo("Chandra, Flame's Catalyst", 332, Rarity.MYTHIC, mage.cards.c.ChandraFlamesCatalyst.class));
         cards.add(new SetCardInfo("Chromatic Orrery", 228, Rarity.MYTHIC, mage.cards.c.ChromaticOrrery.class));
+        cards.add(new SetCardInfo("Conclave Mentor", 216, Rarity.UNCOMMON, mage.cards.c.ConclaveMentor.class));
         cards.add(new SetCardInfo("Containment Priest", 314, Rarity.RARE, mage.cards.c.ContainmentPriest.class));
         cards.add(new SetCardInfo("Cultivate", 177, Rarity.UNCOMMON, mage.cards.c.Cultivate.class));
         cards.add(new SetCardInfo("Dire Fleet Warmonger", 217, Rarity.UNCOMMON, mage.cards.d.DireFleetWarmonger.class));

From 8902fb10022d45fe6e8db44813d652f4a72dc350 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Fri, 12 Jun 2020 10:15:55 +0400
Subject: [PATCH 228/586] Added and fixed tests for Meddling Mage from #6570

---
 .../abilities/activated/ChaosWandTest.java    |   1 -
 .../cards/abilities/keywords/SuspendTest.java |   2 -
 .../cards/restriction/MeddlingMageTest.java   | 195 ++++++++++++++++++
 .../java/org/mage/test/player/TestPlayer.java |  94 +++++----
 4 files changed, 244 insertions(+), 48 deletions(-)
 create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/restriction/MeddlingMageTest.java

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/activated/ChaosWandTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/activated/ChaosWandTest.java
index d570d87c7f..4b98e27289 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/activated/ChaosWandTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/activated/ChaosWandTest.java
@@ -25,7 +25,6 @@ public class ChaosWandTest extends CardTestPlayerBase {
         activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{4}, {T}: ");
         addTarget(playerA, playerB);
         setChoice(playerA, "Yes"); // cast for free
-        setChoice(playerA, "Cast Blood Tithe");
 
         setStrictChooseMode(true);
         setStopAt(1, PhaseStep.BEGIN_COMBAT);
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SuspendTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SuspendTest.java
index 5f699c9382..69db45b6dc 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SuspendTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SuspendTest.java
@@ -223,7 +223,6 @@ public class SuspendTest extends CardTestPlayerBase {
         checkExileCount("after counter", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 1);
 
         // 3 time counters removes on upkeep (3, 5, 7) and cast again
-        setChoice(playerA, "Cast");
         addTarget(playerA, playerB);
         checkLife("after suspend", 7, PhaseStep.PRECOMBAT_MAIN, playerB, 20 - 3);
         checkGraveyardCount("after suspend", 7, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 1);
@@ -258,7 +257,6 @@ public class SuspendTest extends CardTestPlayerBase {
         checkExileCount("after counter", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wear // Tear", 1);
 
         // 3 time counters removes on upkeep (3, 5, 7) and cast again
-        setChoice(playerA, "Cast Wear");
         addTarget(playerA, "Bident of Thassa");
         checkPermanentCount("after suspend", 7, PhaseStep.PRECOMBAT_MAIN, playerB, "Bident of Thassa", 0);
         checkPermanentCount("after suspend", 7, PhaseStep.PRECOMBAT_MAIN, playerB, "Bow of Nylea", 1);
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/restriction/MeddlingMageTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/restriction/MeddlingMageTest.java
new file mode 100644
index 0000000000..3830a9c40e
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/restriction/MeddlingMageTest.java
@@ -0,0 +1,195 @@
+package org.mage.test.cards.restriction;
+
+import mage.constants.EmptyNames;
+import mage.constants.PhaseStep;
+import mage.constants.Zone;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestPlayerBase;
+
+public class MeddlingMageTest extends CardTestPlayerBase {
+
+    //As Meddling Mage enters the battlefield, choose a nonland card name. Spells with the chosen name can't be cast.
+
+    @Test
+    public void testMeddlingMageDefaultScenario() {
+        addCard(Zone.HAND, playerA, "Meddling Mage");
+        addCard(Zone.HAND, playerA, "Savannah Lions");
+
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
+
+        checkPlayableAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Savannah Lions", true);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Meddling Mage");
+        setChoice(playerA, "Savannah Lions"); // name a spell that can't be cast
+
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+        checkPlayableAbility("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Savannah Lions", false);
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerA, "Meddling Mage", 1);
+        assertPermanentCount(playerA, "Savannah Lions", 0);
+        assertHandCount(playerA, "Meddling Mage", 0);
+        assertHandCount(playerA, "Savannah Lions", 1);
+    }
+
+    @Test
+    public void testMeddlingMageIsochronScepterScenario() {
+        addCard(Zone.HAND, playerA, "Meddling Mage");
+        addCard(Zone.HAND, playerA, "Isochron Scepter");
+        addCard(Zone.HAND, playerA, "Lightning Bolt");
+
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Meddling Mage");
+        setChoice(playerA, "Lightning Bolt"); // name a spell that can't be cast
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Isochron Scepter");
+        setChoice(playerA, "Yes"); // use imprint
+        setChoice(playerA, "Lightning Bolt"); // target for imprint
+
+        // copy and cast imprinted card
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+        activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{2}, {T}:");
+        setChoice(playerA, "Yes"); // create copy
+        setChoice(playerA, "Yes"); // cast copy
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerA, "Meddling Mage", 1);
+        assertPermanentCount(playerA, "Isochron Scepter", 1);
+        assertExileCount("Lightning Bolt", 1);
+        assertLife(playerA, 20);
+        assertLife(playerB, 20);
+
+    }
+
+    @Test
+    public void testMeddlingMageFaceDownCreature() {
+        addCard(Zone.HAND, playerA, "Meddling Mage");
+        addCard(Zone.HAND, playerA, "Ainok Tracker"); // red morph creature to prevent it casting from Islands and Plains
+
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 4);
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Meddling Mage");
+        setChoice(playerA, "Ainok Tracker"); // name a spell that can't be cast
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ainok Tracker");
+        setChoice(playerA, "Yes"); // cast it face down as 2/2 creature
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerA, "Meddling Mage", 1);
+        assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
+        assertHandCount(playerA, "Meddling Mage", 0);
+        assertHandCount(playerA, "Ainok Tracker", 0);
+    }
+
+    @Test
+    public void testMeddlingMageFuseCardStopAndCastWell() {
+        addCard(Zone.HAND, playerA, "Meddling Mage");
+
+        // Create a 3/3 green Centaur creature token.
+        // You gain 2 life for each creature you control.
+        addCard(Zone.HAND, playerA, "Alive // Well"); // {3}{G} // {W}
+
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 4);
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Meddling Mage");
+        setChoice(playerA, "Well"); // name a spell that can't be cast
+
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+        checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Alive", true);
+        checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Well", false);
+        checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "fused Alive // Well", false);
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+
+        assertPermanentCount(playerA, "Meddling Mage", 1);
+        assertHandCount(playerA, "Meddling Mage", 0);
+        assertHandCount(playerA, "Alive // Well", 1);
+        assertLife(playerA, 20);
+
+    }
+
+    @Test
+    public void testMeddlingMageFuseCardStopAliveAndCastWell() {
+        addCard(Zone.HAND, playerA, "Meddling Mage");
+
+        // Create a 3/3 green Centaur creature token.
+        // You gain 2 life for each creature you control.
+        addCard(Zone.HAND, playerA, "Alive // Well"); // {3}{G} // {W}
+
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 4);
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Meddling Mage");
+        setChoice(playerA, "Alive"); // name a spell that can't be cast
+
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+        checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Alive", false);
+        checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Well", true);
+        checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "fused Alive // Well", false);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Well");
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerA, "Meddling Mage", 1);
+        assertHandCount(playerA, "Meddling Mage", 0);
+        assertHandCount(playerA, "Alive // Well", 0);
+        assertLife(playerA, 22);
+    }
+
+    @Test
+    public void testMeddlingMageFuseCardStopAliveAndCastFused() {
+        addCard(Zone.HAND, playerA, "Meddling Mage");
+
+        // Create a 3/3 green Centaur creature token.
+        // You gain 2 life for each creature you control.
+        addCard(Zone.HAND, playerA, "Alive // Well"); // {3}{G} // {W}
+
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 4);
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Meddling Mage");
+        setChoice(playerA, "Alive"); // name a spell that can't be cast
+
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+        checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Alive", false);
+        checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Well", true);
+        checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "fused Alive // Well", false);
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerA, "Meddling Mage", 1);
+        assertHandCount(playerA, "Meddling Mage", 0);
+
+        assertHandCount(playerA, "Alive // Well", 1);
+        assertLife(playerA, 20);
+    }
+}
diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
index f15f7c1363..a93b43236a 100644
--- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
+++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
@@ -1,10 +1,5 @@
 package org.mage.test.player;
 
-import java.io.Serializable;
-import java.util.*;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.stream.Collectors;
 import mage.MageItem;
 import mage.MageObject;
 import mage.MageObjectReference;
@@ -62,6 +57,13 @@ import mage.util.CardUtil;
 import org.apache.log4j.Logger;
 import org.junit.Assert;
 import org.junit.Ignore;
+
+import java.io.Serializable;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
 import static org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl.*;
 
 /**
@@ -191,7 +193,7 @@ public class TestPlayer implements Player {
 
     /**
      * @param maxCallsWithoutAction max number of priority passes a player may
-     * have for this test (default = 100)
+     *                              have for this test (default = 100)
      */
     public void setMaxCallsWithoutAction(int maxCallsWithoutAction) {
         this.maxCallsWithoutAction = maxCallsWithoutAction;
@@ -1044,13 +1046,13 @@ public class TestPlayer implements Player {
 
         List<String> data = cards.stream()
                 .map(c -> (((c instanceof PermanentToken) ? "[T] " : "[C] ")
-                + c.getIdName()
-                + (c.isCopy() ? " [copy of " + c.getCopyFrom().getId().toString().substring(0, 3) + "]" : "")
-                + " - " + c.getPower().getValue() + "/" + c.getToughness().getValue()
-                + (c.isPlaneswalker() ? " - L" + c.getCounters(game).getCount(CounterType.LOYALTY) : "")
-                + ", " + (c.isTapped() ? "Tapped" : "Untapped")
-                + getPrintableAliases(", [", c.getId(), "]")
-                + (c.getAttachedTo() == null ? "" : ", attached to " + game.getPermanent(c.getAttachedTo()).getIdName())))
+                        + c.getIdName()
+                        + (c.isCopy() ? " [copy of " + c.getCopyFrom().getId().toString().substring(0, 3) + "]" : "")
+                        + " - " + c.getPower().getValue() + "/" + c.getToughness().getValue()
+                        + (c.isPlaneswalker() ? " - L" + c.getCounters(game).getCount(CounterType.LOYALTY) : "")
+                        + ", " + (c.isTapped() ? "Tapped" : "Untapped")
+                        + getPrintableAliases(", [", c.getId(), "]")
+                        + (c.getAttachedTo() == null ? "" : ", attached to " + game.getPermanent(c.getAttachedTo()).getIdName())))
                 .sorted()
                 .collect(Collectors.toList());
 
@@ -1074,12 +1076,12 @@ public class TestPlayer implements Player {
 
         List<String> data = abilities.stream()
                 .map(a -> (a.getZone() + " -> "
-                + a.getSourceObject(game).getIdName() + " -> "
-                + (a.toString().startsWith("Cast ") ? "[" + a.getManaCostsToPay().getText() + "] -> " : "") // printed cost, not modified
-                + (a.toString().length() > 0
-                ? a.toString().substring(0, Math.min(20, a.toString().length()))
-                : a.getClass().getSimpleName())
-                + "..."))
+                        + a.getSourceObject(game).getIdName() + " -> "
+                        + (a.toString().startsWith("Cast ") ? "[" + a.getManaCostsToPay().getText() + "] -> " : "") // printed cost, not modified
+                        + (a.toString().length() > 0
+                        ? a.toString().substring(0, Math.min(20, a.toString().length()))
+                        : a.getClass().getSimpleName())
+                        + "..."))
                 .sorted()
                 .collect(Collectors.toList());
 
@@ -1444,7 +1446,7 @@ public class TestPlayer implements Player {
         UUID defenderId = null;
         boolean mustAttackByAction = false;
         boolean madeAttackByAction = false;
-        for (Iterator<org.mage.test.player.PlayerAction> it = actions.iterator(); it.hasNext();) {
+        for (Iterator<org.mage.test.player.PlayerAction> it = actions.iterator(); it.hasNext(); ) {
             PlayerAction action = it.next();
 
             // aiXXX commands
@@ -2019,7 +2021,7 @@ public class TestPlayer implements Player {
             // skip targets
             if (targets.get(0).equals(TARGET_SKIP)) {
                 Assert.assertTrue("found skip target, but it require more targets, needs "
-                        + (target.getMinNumberOfTargets() - target.getTargets().size()) + " more",
+                                + (target.getMinNumberOfTargets() - target.getTargets().size()) + " more",
                         target.getTargets().size() >= target.getMinNumberOfTargets());
                 targets.remove(0);
                 return true;
@@ -2324,7 +2326,7 @@ public class TestPlayer implements Player {
 
         this.chooseStrictModeFailed("choice", game,
                 "Triggered list (total " + abilities.size() + "):\n"
-                + abilities.stream().map(a -> getInfo(a, game)).collect(Collectors.joining("\n")));
+                        + abilities.stream().map(a -> getInfo(a, game)).collect(Collectors.joining("\n")));
         return computerPlayer.chooseTriggeredAbility(abilities, game);
     }
 
@@ -3494,7 +3496,7 @@ public class TestPlayer implements Player {
 
     @Override
     public boolean choose(Outcome outcome, Target target,
-            UUID sourceId, Game game
+                          UUID sourceId, Game game
     ) {
         // needed to call here the TestPlayer because it's overwitten
         return choose(outcome, target, sourceId, game, null);
@@ -3502,7 +3504,7 @@ public class TestPlayer implements Player {
 
     @Override
     public boolean choose(Outcome outcome, Cards cards,
-            TargetCard target, Game game
+                          TargetCard target, Game game
     ) {
         assertAliasSupportInChoices(false);
         if (!choices.isEmpty()) {
@@ -3539,7 +3541,7 @@ public class TestPlayer implements Player {
 
     @Override
     public boolean chooseTargetAmount(Outcome outcome, TargetAmount target,
-            Ability source, Game game
+                                      Ability source, Game game
     ) {
         // chooseTargetAmount calls for EACH target cycle (e.g. one target per click, see TargetAmount)
         // if use want to stop choosing then chooseTargetAmount must return false (example: up to xxx)
@@ -3552,7 +3554,7 @@ public class TestPlayer implements Player {
             // skip targets
             if (targets.get(0).equals(TARGET_SKIP)) {
                 Assert.assertTrue("found skip target, but it require more targets, needs "
-                        + (target.getMinNumberOfTargets() - target.getTargets().size()) + " more",
+                                + (target.getMinNumberOfTargets() - target.getTargets().size()) + " more",
                         target.getTargets().size() >= target.getMinNumberOfTargets());
                 targets.remove(0);
                 return false; // false in chooseTargetAmount = stop to choose
@@ -3605,15 +3607,15 @@ public class TestPlayer implements Player {
 
     @Override
     public boolean choosePile(Outcome outcome, String message,
-            List<? extends Card> pile1, List<? extends Card> pile2,
-            Game game
+                              List<? extends Card> pile1, List<? extends Card> pile2,
+                              Game game
     ) {
         return computerPlayer.choosePile(outcome, message, pile1, pile2, game);
     }
 
     @Override
     public boolean playMana(Ability ability, ManaCost unpaid,
-            String promptText, Game game
+                            String promptText, Game game
     ) {
         groupsForTargetHandling = null;
 
@@ -3663,15 +3665,15 @@ public class TestPlayer implements Player {
 
     @Override
     public UUID chooseBlockerOrder(List<Permanent> blockers, CombatGroup combatGroup,
-            List<UUID> blockerOrder, Game game
+                                   List<UUID> blockerOrder, Game game
     ) {
         return computerPlayer.chooseBlockerOrder(blockers, combatGroup, blockerOrder, game);
     }
 
     @Override
     public void assignDamage(int damage, List<UUID> targets,
-            String singleTargetName, UUID sourceId,
-            Game game
+                             String singleTargetName, UUID sourceId,
+                             Game game
     ) {
         computerPlayer.assignDamage(damage, targets, singleTargetName, sourceId, game);
     }
@@ -3690,14 +3692,14 @@ public class TestPlayer implements Player {
 
     @Override
     public void pickCard(List<Card> cards, Deck deck,
-            Draft draft
+                         Draft draft
     ) {
         computerPlayer.pickCard(cards, deck, draft);
     }
 
     @Override
     public boolean scry(int value, Ability source,
-            Game game
+                        Game game
     ) {
         // Don't scry at the start of the game.
         if (game.getTurnNum() == 1 && game.getStep() == null) {
@@ -3708,44 +3710,44 @@ public class TestPlayer implements Player {
 
     @Override
     public boolean surveil(int value, Ability source,
-            Game game
+                           Game game
     ) {
         return computerPlayer.surveil(value, source, game);
     }
 
     @Override
     public boolean moveCards(Card card, Zone toZone,
-            Ability source, Game game
+                             Ability source, Game game
     ) {
         return computerPlayer.moveCards(card, toZone, source, game);
     }
 
     @Override
     public boolean moveCards(Card card, Zone toZone,
-            Ability source, Game game,
-            boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
+                             Ability source, Game game,
+                             boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
     ) {
         return computerPlayer.moveCards(card, toZone, source, game, tapped, faceDown, byOwner, appliedEffects);
     }
 
     @Override
     public boolean moveCards(Cards cards, Zone toZone,
-            Ability source, Game game
+                             Ability source, Game game
     ) {
         return computerPlayer.moveCards(cards, toZone, source, game);
     }
 
     @Override
     public boolean moveCards(Set<Card> cards, Zone toZone,
-            Ability source, Game game
+                             Ability source, Game game
     ) {
         return computerPlayer.moveCards(cards, toZone, source, game);
     }
 
     @Override
     public boolean moveCards(Set<Card> cards, Zone toZone,
-            Ability source, Game game,
-            boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
+                             Ability source, Game game,
+                             boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
     ) {
         return computerPlayer.moveCards(cards, toZone, source, game, tapped, faceDown, byOwner, appliedEffects);
     }
@@ -3833,12 +3835,14 @@ public class TestPlayer implements Player {
 
     @Override
     public SpellAbility chooseAbilityForCast(Card card, Game game, boolean noMana) {
-        String allInfo = "";
+        assertAliasSupportInChoices(false);
 
         Map<UUID, ActivatedAbility> useable = PlayerImpl.getSpellAbilities(this.getId(), card, game.getState().getZone(card.getId()), game);
-        allInfo = useable.values().stream().map(Object::toString).collect(Collectors.joining("\n"));
+        String allInfo = useable.values().stream().map(Object::toString).collect(Collectors.joining("\n"));
+        if (useable.size() == 1) {
+            return (SpellAbility) useable.values().iterator().next();
+        }
 
-        assertAliasSupportInChoices(false);
         if (!choices.isEmpty()) {
             for (ActivatedAbility ability : useable.values()) {
                 if (ability.toString().startsWith(choices.get(0))) {

From 63851b73a1608b4410563e0970c530bcaaf7c975 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Fri, 12 Jun 2020 07:42:36 -0400
Subject: [PATCH 229/586] Updated commander zone change rules (ready for
 review) (#6620)

* updated commander zone change rules

* moved commander tracking into game state

* fixed a zone change error

* fixed some more tests for new commander rule

* updated variable names

* updated a test name
---
 .../commander/duel/CastBRGCommanderTest.java  |  2 +-
 .../duel/CommanderReplaceEffectTest.java      |  4 +--
 .../test/commander/duel/MythUnboundTest.java  |  1 -
 .../test/commander/duel/NorinTheWaryTest.java |  5 +--
 .../CommanderReplacementEffect.java           |  2 --
 Mage/src/main/java/mage/game/GameImpl.java    | 35 +++++++++++++++++++
 Mage/src/main/java/mage/game/GameState.java   | 10 ++++++
 .../main/java/mage/players/PlayerImpl.java    |  2 +-
 8 files changed, 52 insertions(+), 9 deletions(-)

diff --git a/Mage.Tests/src/test/java/org/mage/test/commander/duel/CastBRGCommanderTest.java b/Mage.Tests/src/test/java/org/mage/test/commander/duel/CastBRGCommanderTest.java
index 9abe4909fa..924781998f 100644
--- a/Mage.Tests/src/test/java/org/mage/test/commander/duel/CastBRGCommanderTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/commander/duel/CastBRGCommanderTest.java
@@ -160,7 +160,7 @@ public class CastBRGCommanderTest extends CardTestCommanderDuelBase {
         assertGraveyardCount(playerA, "Mogg Infestation", 1);
         assertCommandZoneCount(playerB, "Daxos of Meletis", 1);
 
-        assertPermanentCount(playerB, "Goblin", 0);
+        assertPermanentCount(playerB, "Goblin", 2);
 
     }
 
diff --git a/Mage.Tests/src/test/java/org/mage/test/commander/duel/CommanderReplaceEffectTest.java b/Mage.Tests/src/test/java/org/mage/test/commander/duel/CommanderReplaceEffectTest.java
index 4e7dcb3b2d..1461804f89 100644
--- a/Mage.Tests/src/test/java/org/mage/test/commander/duel/CommanderReplaceEffectTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/commander/duel/CommanderReplaceEffectTest.java
@@ -110,8 +110,8 @@ public class CommanderReplaceEffectTest extends CardTestCommanderDuelBase {
         execute();
 
         assertPermanentCount(playerA, "Soulherder", 1);
-        assertPermanentCount(playerA, "Daxos of Meletis", 0);
-        assertCommandZoneCount(playerA, "Daxos of Meletis", 1);
+        assertPermanentCount(playerA, "Daxos of Meletis", 1);
+        assertCommandZoneCount(playerA, "Daxos of Meletis", 0);
         assertPowerToughness(playerA, "Soulherder", 2, 2);
 
     }
diff --git a/Mage.Tests/src/test/java/org/mage/test/commander/duel/MythUnboundTest.java b/Mage.Tests/src/test/java/org/mage/test/commander/duel/MythUnboundTest.java
index b08652f2bb..3375f7a23c 100644
--- a/Mage.Tests/src/test/java/org/mage/test/commander/duel/MythUnboundTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/commander/duel/MythUnboundTest.java
@@ -52,7 +52,6 @@ public class MythUnboundTest extends CardTestCommanderDuelBase {
         assertPermanentCount(playerA, "Myth Unbound", 1);
         assertGraveyardCount(playerB, "Lightning Bolt", 1);
         assertPermanentCount(playerA, "Oviya Pashiri, Sage Lifecrafter", 1);
-        assertHandCount(playerA, 1);
         assertTappedCount("Forest", false, 4 - 3); // 1 for first, 2 for second cast
     }
 }
diff --git a/Mage.Tests/src/test/java/org/mage/test/commander/duel/NorinTheWaryTest.java b/Mage.Tests/src/test/java/org/mage/test/commander/duel/NorinTheWaryTest.java
index 8db47a88fb..36a44ea0a9 100644
--- a/Mage.Tests/src/test/java/org/mage/test/commander/duel/NorinTheWaryTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/commander/duel/NorinTheWaryTest.java
@@ -72,7 +72,7 @@ public class NorinTheWaryTest extends CardTestCommanderDuelBase {
     }
 
     @Test
-    public void castNorinTheWaryToCommandAndReturn() {
+    public void castNorinTheWaryToCommandAndNotReturn() {
         addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
         addCard(Zone.HAND, playerA, "Lightning Bolt", 1);
 
@@ -83,8 +83,9 @@ public class NorinTheWaryTest extends CardTestCommanderDuelBase {
         execute();
 
         assertGraveyardCount(playerA, "Lightning Bolt", 1);
-        assertPermanentCount(playerA, "Norin the Wary", 1);
+        assertPermanentCount(playerA, "Norin the Wary", 0);
         assertExileCount("Norin the Wary", 0);
+        assertCommandZoneCount(playerA, "Norin the Wary", 1);
 
         assertLife(playerB, 37);
 
diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/CommanderReplacementEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/CommanderReplacementEffect.java
index e60ccca358..5ccacd8711 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/continuous/CommanderReplacementEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/CommanderReplacementEffect.java
@@ -106,8 +106,6 @@ public class CommanderReplacementEffect extends ReplacementEffectImpl {
         switch (zEvent.getToZone()) {
             case LIBRARY:
             case HAND:
-            case GRAVEYARD:
-            case EXILED:
                 if (commanderId.equals(zEvent.getTargetId())) {
                     return true;
                 }
diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java
index a1f0d9e9cf..f32b53881b 100644
--- a/Mage/src/main/java/mage/game/GameImpl.java
+++ b/Mage/src/main/java/mage/game/GameImpl.java
@@ -1853,6 +1853,41 @@ public abstract class GameImpl implements Game, Serializable {
             }
         }
 
+        // If a commander is in a graveyard or in exile and that card was put into that zone
+        // since the last time state-based actions were checked, its owner may put it into the command zone.
+        for (Player player : state.getPlayers().values()) {
+            Set<UUID> commanderIds = getCommandersIds(player, CommanderCardType.COMMANDER_OR_OATHBREAKER);
+            if (commanderIds.isEmpty()) {
+                continue;
+            }
+            Set<Card> commanders = new HashSet<>();
+            Cards toMove = new CardsImpl();
+            player.getGraveyard()
+                    .stream()
+                    .filter(commanderIds::contains)
+                    .map(this::getCard)
+                    .filter(Objects::nonNull)
+                    .forEach(commanders::add);
+            commanderIds
+                    .stream()
+                    .map(uuid -> getExile().getCard(uuid, this))
+                    .filter(Objects::nonNull)
+                    .forEach(commanders::add);
+            commanders.removeIf(card -> state.checkCommanderShouldStay(card, this));
+            for (Card card : commanders) {
+                if (player.chooseUse(Outcome.Benefit, "Move " + card.getIdName() + " to the command zone or leave it in its current zone?", "You can only make this choice once", "Move to command", "Leave in current zone", null, this)) {
+                    toMove.add(card);
+                } else {
+                    state.setCommanderShouldStay(card, this);
+                }
+            }
+            if (toMove.isEmpty()) {
+                continue;
+            }
+            player.moveCards(toMove, Zone.COMMAND, null, this);
+            somethingHappened = true;
+        }
+
         // 704.5e If a copy of a spell is in a zone other than the stack, it ceases to exist. If a copy of a card is in any zone other than the stack or the battlefield, it ceases to exist.
         // (Isochron Scepter) 12/1/2004: If you don't want to cast the copy, you can choose not to; the copy ceases to exist the next time state-based actions are checked.
         Iterator<Card> copiedCards = this.getState().getCopiedCards().iterator();
diff --git a/Mage/src/main/java/mage/game/GameState.java b/Mage/src/main/java/mage/game/GameState.java
index dabfb43700..f156e307f5 100644
--- a/Mage/src/main/java/mage/game/GameState.java
+++ b/Mage/src/main/java/mage/game/GameState.java
@@ -5,6 +5,7 @@ import java.util.*;
 import static java.util.Collections.emptyList;
 import java.util.stream.Collectors;
 import mage.MageObject;
+import mage.MageObjectReference;
 import mage.abilities.*;
 import mage.abilities.effects.ContinuousEffect;
 import mage.abilities.effects.ContinuousEffects;
@@ -98,6 +99,7 @@ public class GameState implements Serializable, Copyable<GameState> {
     private Map<UUID, Card> copiedCards = new HashMap<>();
     private int permanentOrderNumber;
     private Map<UUID, FilterCreaturePermanent> usePowerInsteadOfToughnessForDamageLethalityFilters = new HashMap<>();
+    private final Set<MageObjectReference> commandersToStay = new HashSet<>();
 
     private int applyEffectsCounter; // Upcounting number of each applyEffects execution
 
@@ -1225,4 +1227,12 @@ public class GameState implements Serializable, Copyable<GameState> {
                 .map(usePowerInsteadOfToughnessForDamageLethalityFilters::get)
                 .collect(Collectors.toList());
     }
+
+    boolean checkCommanderShouldStay(Card card, Game game) {
+        return commandersToStay.stream().anyMatch(mor -> mor.refersTo(card, game));
+    }
+
+    void setCommanderShouldStay(Card card, Game game) {
+        commandersToStay.add(new MageObjectReference(card, game));
+    }
 }
diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java
index a816624f03..db4f20f661 100644
--- a/Mage/src/main/java/mage/players/PlayerImpl.java
+++ b/Mage/src/main/java/mage/players/PlayerImpl.java
@@ -3879,7 +3879,7 @@ public abstract class PlayerImpl implements Player, Serializable {
                 break;
             case COMMAND:
                 for (Card card : cards) {
-                    if (moveCardToCommandWithInfo(card, source.getSourceId(), game, fromZone)) {
+                    if (moveCardToCommandWithInfo(card, source == null ? null : source.getSourceId(), game, fromZone)) {
                         successfulMovedCards.add(card);
                     }
                 }

From bef6cb2421c42d1fae1a45834dab41c0c4fd44a0 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Fri, 12 Jun 2020 07:58:18 -0400
Subject: [PATCH 230/586] updated M21 spoiler and reprints

---
 Mage.Sets/src/mage/sets/CoreSet2021.java |  3 +++
 Utils/mtg-cards-data.txt                 | 13 ++++++++++++-
 2 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index a5ea785d8b..c36c6895b3 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -56,8 +56,10 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Conclave Mentor", 216, Rarity.UNCOMMON, mage.cards.c.ConclaveMentor.class));
         cards.add(new SetCardInfo("Containment Priest", 314, Rarity.RARE, mage.cards.c.ContainmentPriest.class));
         cards.add(new SetCardInfo("Cultivate", 177, Rarity.UNCOMMON, mage.cards.c.Cultivate.class));
+        cards.add(new SetCardInfo("Defiant Strike", 15, Rarity.COMMON, mage.cards.d.DefiantStrike.class));
         cards.add(new SetCardInfo("Dire Fleet Warmonger", 217, Rarity.UNCOMMON, mage.cards.d.DireFleetWarmonger.class));
         cards.add(new SetCardInfo("Double Vision", 142, Rarity.RARE, mage.cards.d.DoubleVision.class));
+        cards.add(new SetCardInfo("Duress", 96, Rarity.COMMON, mage.cards.d.Duress.class));
         cards.add(new SetCardInfo("Elder Gargaroth", 179, Rarity.MYTHIC, mage.cards.e.ElderGargaroth.class));
         cards.add(new SetCardInfo("Eliminate", 97, Rarity.UNCOMMON, mage.cards.e.Eliminate.class));
         cards.add(new SetCardInfo("Fabled Passage", 246, Rarity.RARE, mage.cards.f.FabledPassage.class));
@@ -113,6 +115,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Radiant Fountain", 248, Rarity.COMMON, mage.cards.r.RadiantFountain.class));
         cards.add(new SetCardInfo("Rain of Revelation", 61, Rarity.UNCOMMON, mage.cards.r.RainOfRevelation.class));
         cards.add(new SetCardInfo("Rambunctious Mutt", 30, Rarity.COMMON, mage.cards.r.RambunctiousMutt.class));
+        cards.add(new SetCardInfo("Ranger's Guile", 199, Rarity.COMMON, mage.cards.r.RangersGuile.class));
         cards.add(new SetCardInfo("Revitalize", 31, Rarity.COMMON, mage.cards.r.Revitalize.class));
         cards.add(new SetCardInfo("Rewind", 63, Rarity.UNCOMMON, mage.cards.r.Rewind.class));
         cards.add(new SetCardInfo("Riddleform", 64, Rarity.UNCOMMON, mage.cards.r.Riddleform.class));
diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt
index 963dacdb3b..b3c1dc34fa 100644
--- a/Utils/mtg-cards-data.txt
+++ b/Utils/mtg-cards-data.txt
@@ -37446,7 +37446,9 @@ Basri Ket|Core Set 2021|7|M|{1}{W}{W}|Legendary Planeswalker - Basri|3|+1: Put a
 Basri's Acolyte|Core Set 2021|8|C|{2}{W}{W}|Creature - Cat Cleric|2|3|Lifelink$When Basri's Acolyte enters the battlefield, put a +1/+1 counter on each of up to two other target creatures you control.|
 Basri's Lieutenant|Core Set 2021|9|R|{3}{W}|Creature - Human Knight|3|4|Vigilance, protection from multicolored$When Basri's Lieutenant enters the battlefield, put a +1/+1 counter on target creature you control.$Whenever Basri's Lieutenant or another creature you control dies, if it had a +1/+1 counter on it, create a 2/2 white Knight creature token with vigilance.|
 Basri's Solidarity|Core Set 2021|10|U|{1}{W}|Sorcery|||Put a +1/+1 counter on each creature you control.|
+Defiant Strike|Core Set 2021|15|C|{W}|Instant|||Target creature gets +1/+0 until end of turn.$Draw a card.|
 Faith's Fetters|Core Set 2021|17|U|{3}{W}|Enchantment - Aura|||Enchant permanent$When Faith's Fetters enters the battlefield, you gain 4 life.$Enchanted permanent can't attack or block, and its activated abilities can't be activated unless they're mana abilities.|
+Falconer Adept|Core Set 2021|18|U|{3}{W}|Creature - Human Soldier|2|3|Whenever Falconer Adept attacks, create a 1/1 white Bird creature token with flying that's tapped and attacking.|
 Idol of Endurance|Core Set 2021|23|R|{2}{W}|Artifact|||When Idol of Endurance enters the battlefield, exile all creature cards with converted mana cost 3 or less from your graveyard until Idol of Endurance leaves the battlefield.${1}{W}, {T}: Until end of turn, you may cast a creature spell from among the cards exiled with Idol of Endurance without paying its mana cost.|
 Light of Promise|Core Set 2021|25|U|{2}{W}|Enchantment - Aura|||Enchant creature$Enchanted creature has "Whenever you gain life, put that many +1/+1 counters on this creature."|
 Mangara, the Diplomat|Core Set 2021|27|M|{3}{W}|Legendary Creature - Human Cleric|2|4|Lifelink$Whenever an opponent attacks with creatures, if two or more of those creatures are attacking you and/or a planeswalker you control, draw a card.$Whenever an opponent casts their second spell each turn, draw a card.|
@@ -37478,10 +37480,12 @@ Teferi's Ageless Insight|Core Set 2021|76|R|{2}{U}{U}|Legendary Enchantment|||If
 Teferi's Protege|Core Set 2021|77|C|{2}{U}|Creature - Human Wizard|2|3|{1}{U}, {T}: Draw a card, then discard a card.|
 Teferi's Tutelage|Core Set 2021|78|U|{2}{U}|Enchantment|||When Teferi's Tutelage enters the battlefield, draw a card, then discard a card.$Whenever you draw a card, target opponent mills two cards.|
 Tide Skimmer|Core Set 2021|79|U|{3}{U}|Creature - Drake|2|3|Flying$Whenever you attack with two or more creatures with flying, draw a card.|
+Tolarian Kraken|Core Set 2021|80|U|{4}{U}{U}|Creature - Kraken|4|6|Whenever you draw a card, you may pay {1}. When you do, you may tap or untap target creature.|
 Waker of Waves|Core Set 2021|84|U|{5}{U}{U}|Creature - Whale|7|7|Creatures your opponents control get -1/-0.${1}{U}, Discard Waker of Waves: Look at the top two cards of your library. Put one of them into your hand and the other into your graveyard.|
 Archfiend's Vessel|Core Set 2021|88|U|{B}|Creature - Human Cleric|1|1|Lifelink$When Archfiend's Vessel enters the battlefield, if it entered from your graveyard or you cast it from your graveyard, exile it. If you do, create a 5/5 black Demon creature token with flying.|
 Bad Deal|Core Set 2021|89|U|{4}{B}{B}|Sorcery|||You draw two cards and each opponent discards two cards. Each player loses 2 life.|
 Carrion Grub|Core Set 2021|92|U|{3}{B}|Creature - Insect|0|5|Carrion Grub gets +X/+0, where X is the greatest power among creature cards in your graveyard.$When Carrion Grub enters the battlefield, mill four cards.|
+Duress|Core Set 2021|96|C|{B}|Sorcery|||Target opponent reveals their hand. You choose a noncreature, nonland card from it. That player discards that card.|
 Eliminate|Core Set 2021|97|U|{1}{B}|Instant|||Destroy target creature or planeswalker with converted mana cost 3 or less.|
 Grasp of Darkness|Core Set 2021|102|C|{B}{B}|Instant|||Target creature gets -4/-4 until end of turn.|
 Grim Tutor|Core Set 2021|103|M|{1}{B}{B}|Sorcery|||Search your library for a card and put that card into your hand, then shuffle your library. You lose 3 life.|
@@ -37498,6 +37502,7 @@ Thieves' Guild Enforcer|Core Set 2021|125|R|{B}|Creature - Human Rogue|1|1|Flash
 Village Rites|Core Set 2021|126|C|{B}|Instant|||As an additional cost to cast this spell, sacrifice a creature.$Draw two cards.|
 Vito, Thorn of the Dusk Rose|Core Set 2021|127|R|{2}{B}|Legendary Creature - Vampire Cleric|1|3|Whenever you gain life, target opponent loses that much life.${3}{B}{B}: Creatures you control gain lifelink until end of turn.|
 Bolt Hound|Core Set 2021|131|U|{2}{R}|Creature - Elemental Dog|2|2|Haste$Whenever Bolt Hound attacks, other creatures you control get +1/+0 until end of turn.|
+Brash Taunter|Core Set 2021|133|R|{4}{R}|Creature - Goblin|1|1|Indestructible$Whenever Brash Taunter is dealt damage, it deals that much damage to target opponent.${2}{R}, {T}: Brash Taunter fights another target creature.|
 Chandra, Heart of Fire|Core Set 2021|135|M|{3}{R}{R}|Legendary Planeswalker - Chandra|5|+1: Discard your hand, then exile the top three cards of your library. Until end of turn, you may play cards exiled this way.$+1: Chandra, Heart of Fire deals 2 damage to any target.$−9: Search your graveyard and library for any number of red instant and/or sorcery spells, exile them, then shuffle your library. You may cast them this turn. Add six {R}.|
 Chandra's Incinerator|Core Set 2021|136|R|{5}{R}|Creature - Elemental|6|6|This spell costs {X} less to cast, where X is the total amount of noncombat damage dealt to your opponents this turn.$Trample$Whenever a source you control deals noncombat damage to an opponent, Chandra's Incinerator deals that much damage to target creature or planeswalker that player controls.|
 Chandra's Magmutt|Core Set 2021|137|C|{1}{R}|Creature - Elemental Dog|2|2|{T}: Chandra's Magmutt deals 1 damage to target player or planeswalker.|
@@ -37514,24 +37519,29 @@ Hellkite Punisher|Core Set 2021|151|U|{5}{R}{R}|Creature - Dragon|6|6|Flying${R}
 Igneous Cur|Core Set 2021|153|C|{1}{R}|Creature - Elemental Dog|1|2|{1}{R}: Igneous Cur gets +2/+0 until end of turn.|
 Kinetic Augur|Core Set 2021|154|U|{3}{R}|Creature - Human Shaman|*|4|Trample$Kinetic Augur's power is equal to the number of instant and sorcery cards in your graveyard.$When Kinetic Augur enters the battlefield, discard up to two cards, then draw that many cards.|
 Soul Sear|Core Set 2021|160|U|{2}{R}|Instant|||Soul Sear deals 5 damage to target creature or planeswalker. That permanent loses indestructible until end of turn.|
+Traitorous Greed|Core Set 2021|166|U|{3}{R}|Sorcery|||Gain control of target creature until end of turn. Untap that creature. It gains haste until end of turn. Add two mana of any one color.|
 Transmogrify|Core Set 2021|167|R|{3}{R}|Sorcery|||Exile target creature. That creature's controller reveals cards from the top of their library until they reveal a creature card. That player puts that card onto the battlefield, then shuffles the rest into their library.|
 Volcanic Salvo|Core Set 2021|172|R|{10}{R}{R}|Sorcery|||This spell costs {X} less to cast, where X is the total power of creatures you control.$Volcanic Salvo deals 6 damage to each of up to two target creatures and/or planeswalkers.|
 Azusa, Lost but Seeking|Core Set 2021|173|R|{2}{G}|Legendary Creature - Human Monk|1|2|You may play two additional lands on each of your turns.|
+Burlfist Oak|Core Set 2021|174|U|{2}{G}{G}|Creature - Treefolk|2|3|Whenever you draw a card, Burlfist Oak gets +2/+2 until end of turn.|
 Cultivate|Core Set 2021|177|U|{2}{G}|Sorcery|||Search your library for up to two basic land cards, reveal those cards, and put one onto the battlefield tapped and the other into your hand. Then shuffle your library.|
+Drowsing Tyrannodon|Core Set 2021|178|C|{1}{G}|Creature - Dinosaur|3|3|Defender$As long as you control a creature with power 4 or greater, Drowsing Tyrannodon can attack as though it didn't have defender.|
 Elder Gargaroth|Core Set 2021|179|M|{3}{G}{G}|Creature - Beast|6|6|Vigilance, reach, trample$Whenever Elder Gargaroth attacks or blocks, choose one —$• Create a 3/3 green Beast creature token.$• You gain 3 life.$• Draw a card.|
 Feline Sovereign|Core Set 2021|180|R|{2}{G}|Creature - Cat|2|3|Other Cats you control get +1/+1 and have protection from Dogs.$Whenever one or more Cats you control deal combat damage to a player, destroy up to one target artifact or enchantment that player controls.|
 Fierce Empath|Core Set 2021|181|U|{2}{G}|Creature - Elf|1|1|When Fierce Empath enters the battlefield, you may search your library for a creature card with converted mana cost 6 or greater, reveal it, put it into your hand, then shuffle your library.|
 Fungal Rebirth|Core Set 2021|182|U|{2}{G}|Instant|||Return target permanent card from your graveyard to your hand. If a creature died this turn, create two 1/1 green Saproling creature tokens.|
-Garruk, Unleashed|Core Set 2021|183|M|{2}{G}{G}|Legendary Planeswalker - Garruk|4|+1: Up to one target creature gets +3/+3 and gains trample until end of turn.$−2: Create a 3/3 green Beast creature token. Then, if an opponent controls more creatures than you, put a loyalty counter on Garruk, Unleashed.$−7: Create an emblem with "At the beginning of your end step, you may search your library for a creature card, put it onto the battlefield, then shuffle your library."|
+Garruk, Unleashed|Core Set 2021|183|M|{2}{G}{G}|Legendary Planeswalker - Garruk|4|+1: Up to one target creature gets +3/+3 and gains trample until end of turn.$−2: Create a 3/3 green Beast creature token. Then if an opponent controls more creatures than you, put a loyalty counter on Garruk, Unleashed.$−7: Create an emblem with "At the beginning of your end step, you may search your library for a creature card, put it onto the battlefield, then shuffle your library."|
 Garruk's Gorehorn|Core Set 2021|184|C|{4}{G}|Creature - Beast|7|3||
 Garruk's Harbinger|Core Set 2021|185|R|{1}{G}{G}|Creature - Beast|4|3|Hexproof from Black$Whenever Garruk's Harbinger deals combat damage to a player or planeswalker, look at that many cards from the top of your library. You may reveal a creature card or Garruk planeswalker card from among them and put it into your hand. Put the rest on the bottom of your library in a random order.|
 Garruk's Uprising|Core Set 2021|186|U|{2}{G}|Enchantment|||When Garruk's Uprising enters the battlefield, if you control a creature with power 4 or greater, draw a card.$Creatures you control have trample.$Whenever a creature with power 4 or greater enters the battlefield under your control, draw a card.|
 Heroic Intervention|Core Set 2021|188|R|{1}{G}|Instant|||Permanents you control gain hexproof and indestructible until end of turn.|
+Invigorating Surge|Core Set 2021|190|U|{2}{G}|Instant|||Put a +1/+1 counter on target creature you control, then double the number of +1/+1 counters on that creature.|
 Jolrael, Mwonvuli Recluse|Core Set 2021|191|R|{1}{G}|Legendary Creature - Human Druid|1|2|Whenever you draw your second card each turn, create a 2/2 green Cat creature token.${4}{G}{G}: Until end of turn, creatures you control have base power and toughness X/X, where X is the number of cards in your hand.|
 Llanowar Visionary|Core Set 2021|193|C|{2}{G}|Creature - Elf Druid|2|2|When Llanowar Visionary enters the battlefield, draw a card.${T}: Add {G}.|
 Pridemalkin|Core Set 2021|196|C|{2}{G}|Creature - Cat|2|1|When Pridemalkin enters the battlefield, put a +1/+1 counter on target creature you control.$Each creature you control with a +1/+1 counter on it has trample.|
 Primal Might|Core Set 2021|197|R|{X}{G}|Sorcery|||Target creature you control gets +X/+X until end of turn. Then it fights up to one target creature you don't control.|
 Quirion Dryad|Core Set 2021|198|U|{1}{G}|Creature - Dryad|1|1|Whenever you cast a white, blue, black, or red spell, put a +1/+1 counter on Quirion Dryad.|
+Ranger's Guile|Core Set 2021|199|C|{G}|Instant|||Target creature you control gets +1/+1 and gains hexproof until end of turn.|
 Trufflesnout|Core Set 2021|212|C|{2}{G}|Creature - Boar|2|2|When Trufflesnout enters the battlefield, choose one —$• Put a +1/+1 counter on Trufflesnout.$• You gain 4 life.|
 Warden of the Woods|Core Set 2021|213|U|{4}{G}{G}|Creature - Treefolk|5|7|Vigilance$Whenever Warden of the Woods becomes the target of a spell or ability an opponent controls, you may draw two cards.|
 Wildwood Scourge|Core Set 2021|214|U|{X}{G}|Creature - Hydra|0|0|Wildwood Scourge enters the battlefield with X +1/+1 counters on it.$Whenever one or more +1/+1 counters are put on another non-Hydra creature you control, put a +1/+1 counter on Wildwood Scourge.|
@@ -37542,6 +37552,7 @@ Indulging Patrician|Core Set 2021|219|U|{1}{W}{B}|Creature - Vampire Noble|1|4|F
 Leafkin Avenger|Core Set 2021|220|U|{2}{R}{G}|Creature - Elemental Druid|4|3|{T}: Add {G} for each creature you control with power 4 or greater.${7}{R}: Leafkin Avenger deals damage equal to its power to target player or planeswalker.|
 Lorescale Coatl|Core Set 2021|221|U|{1}{G}{U}|Creature - Snake|2|2|Whenever you draw a card, you may put a +1/+1 counter on Lorescale Coatl.|
 Obsessive Stitcher|Core Set 2021|223|U|{1}{U}{B}|Creature - Human Wizard|0|3|{T}: Draw a card, then discard a card.${2}{U}{B}, {T}, Sacrifice Obsessive Stitcher: Return target creature card from your graveyard to the battlefield.|
+Watcher of the Spheres|Core Set 2021|227|U|{W}{U}|Creature - Bird Wizard|2|2|Flying$Creature spells with flying you cast cost {1} less to cast.$Whenever another creature with flying enters the battlefield under your control, Watcher of the Spheres gets +1/+1 until end of turn.|
 Chromatic Orrery|Core Set 2021|228|M|{7}|Legendary Artifact|||You may spend mana as though it were mana of any color.${T}: Add {C}{C}{C}{C}{C}.${5}, {T}: Draw a card for each color among permanents you control.|
 Mazemind Tome|Core Set 2021|232|R|{2}|Artifact|||{T}, Put a page counter on Mazemind Tome: Scry 1.${2}, {T}, Put a page counter on Mazemind Tome: Draw a card.$When there are four or more page counters on Mazemind Tome, exile it. If you do, you gain 4 life.|
 Meteorite|Core Set 2021|233|U|{5}|Artifact|||When Meteorite enters the battlefield, it deals 2 damage to any target.${T}: Add one mana of any color.|

From 4dc5104a945406c263042d1ad0506d81b0017d98 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Fri, 12 Jun 2020 08:02:13 -0400
Subject: [PATCH 231/586] Implemented Burlfist Oak

---
 Mage.Sets/src/mage/cards/b/BurlfistOak.java | 40 +++++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java    |  1 +
 2 files changed, 41 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/b/BurlfistOak.java

diff --git a/Mage.Sets/src/mage/cards/b/BurlfistOak.java b/Mage.Sets/src/mage/cards/b/BurlfistOak.java
new file mode 100644
index 0000000000..66bd617a80
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/b/BurlfistOak.java
@@ -0,0 +1,40 @@
+package mage.cards.b;
+
+import mage.MageInt;
+import mage.abilities.common.DrawCardControllerTriggeredAbility;
+import mage.abilities.effects.common.continuous.BoostSourceEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class BurlfistOak extends CardImpl {
+
+    public BurlfistOak(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{G}");
+
+        this.subtype.add(SubType.TREEFOLK);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(3);
+
+        // Whenever you draw a card, Burlfist Oak gets +2/+2 until end of turn.
+        this.addAbility(new DrawCardControllerTriggeredAbility(
+                new BoostSourceEffect(2, 2, Duration.EndOfTurn), false
+        ));
+    }
+
+    private BurlfistOak(final BurlfistOak card) {
+        super(card);
+    }
+
+    @Override
+    public BurlfistOak copy() {
+        return new BurlfistOak(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index c36c6895b3..d9ea4f6044 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -47,6 +47,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Basri's Solidarity", 10, Rarity.UNCOMMON, mage.cards.b.BasrisSolidarity.class));
         cards.add(new SetCardInfo("Basri, Devoted Paladin", 320, Rarity.MYTHIC, mage.cards.b.BasriDevotedPaladin.class));
         cards.add(new SetCardInfo("Bolt Hound", 131, Rarity.UNCOMMON, mage.cards.b.BoltHound.class));
+        cards.add(new SetCardInfo("Burlfist Oak", 174, Rarity.UNCOMMON, mage.cards.b.BurlfistOak.class));
         cards.add(new SetCardInfo("Carrion Grub", 92, Rarity.UNCOMMON, mage.cards.c.CarrionGrub.class));
         cards.add(new SetCardInfo("Chandra's Firemaw", 333, Rarity.RARE, mage.cards.c.ChandrasFiremaw.class));
         cards.add(new SetCardInfo("Chandra's Incinerator", 136, Rarity.RARE, mage.cards.c.ChandrasIncinerator.class));

From 8e114ce230b004449dd10660bbb551f86f22bb70 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Fri, 12 Jun 2020 08:09:58 -0400
Subject: [PATCH 232/586] Implemented Drowsing Tyrannodon

---
 .../src/mage/cards/d/DrowsingTyrannodon.java  | 49 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 50 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/d/DrowsingTyrannodon.java

diff --git a/Mage.Sets/src/mage/cards/d/DrowsingTyrannodon.java b/Mage.Sets/src/mage/cards/d/DrowsingTyrannodon.java
new file mode 100644
index 0000000000..0635f947dd
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/d/DrowsingTyrannodon.java
@@ -0,0 +1,49 @@
+package mage.cards.d;
+
+import mage.MageInt;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.condition.common.FerociousCondition;
+import mage.abilities.decorator.ConditionalAsThoughEffect;
+import mage.abilities.effects.common.combat.CanAttackAsThoughItDidntHaveDefenderSourceEffect;
+import mage.abilities.keyword.DefenderAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class DrowsingTyrannodon extends CardImpl {
+
+    public DrowsingTyrannodon(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}");
+
+        this.subtype.add(SubType.DINOSAUR);
+        this.power = new MageInt(3);
+        this.toughness = new MageInt(3);
+
+        // Defender
+        this.addAbility(DefenderAbility.getInstance());
+
+        // As long as you control a creature with power 4 or greater, Drowsing Tyrannodon can attack as though it didn't have defender.
+        this.addAbility(new SimpleStaticAbility(new ConditionalAsThoughEffect(
+                new CanAttackAsThoughItDidntHaveDefenderSourceEffect(
+                        Duration.WhileOnBattlefield
+                ), FerociousCondition.instance
+        ).setText("as long as you control a creature with power 4 or greater, " +
+                "{this} can attack as though it didn't have defender")));
+    }
+
+    private DrowsingTyrannodon(final DrowsingTyrannodon card) {
+        super(card);
+    }
+
+    @Override
+    public DrowsingTyrannodon copy() {
+        return new DrowsingTyrannodon(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index d9ea4f6044..9823216759 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -60,6 +60,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Defiant Strike", 15, Rarity.COMMON, mage.cards.d.DefiantStrike.class));
         cards.add(new SetCardInfo("Dire Fleet Warmonger", 217, Rarity.UNCOMMON, mage.cards.d.DireFleetWarmonger.class));
         cards.add(new SetCardInfo("Double Vision", 142, Rarity.RARE, mage.cards.d.DoubleVision.class));
+        cards.add(new SetCardInfo("Drowsing Tyrannodon", 178, Rarity.COMMON, mage.cards.d.DrowsingTyrannodon.class));
         cards.add(new SetCardInfo("Duress", 96, Rarity.COMMON, mage.cards.d.Duress.class));
         cards.add(new SetCardInfo("Elder Gargaroth", 179, Rarity.MYTHIC, mage.cards.e.ElderGargaroth.class));
         cards.add(new SetCardInfo("Eliminate", 97, Rarity.UNCOMMON, mage.cards.e.Eliminate.class));

From 3ff978840b9e6a0f65da93b4a0e96824ecabc028 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Fri, 12 Jun 2020 08:11:26 -0400
Subject: [PATCH 233/586] Implemented Falconer Adept

---
 Mage.Sets/src/mage/cards/f/FalconerAdept.java | 41 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 42 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/f/FalconerAdept.java

diff --git a/Mage.Sets/src/mage/cards/f/FalconerAdept.java b/Mage.Sets/src/mage/cards/f/FalconerAdept.java
new file mode 100644
index 0000000000..c9f342a026
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/f/FalconerAdept.java
@@ -0,0 +1,41 @@
+package mage.cards.f;
+
+import mage.MageInt;
+import mage.abilities.common.AttacksTriggeredAbility;
+import mage.abilities.effects.common.CreateTokenEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.game.permanent.token.BirdToken;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class FalconerAdept extends CardImpl {
+
+    public FalconerAdept(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}");
+
+        this.subtype.add(SubType.HUMAN);
+        this.subtype.add(SubType.SOLDIER);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(3);
+
+        // Whenever Falconer Adept attacks, create a 1/1 white Bird creature token with flying that's tapped and attacking.
+        this.addAbility(new AttacksTriggeredAbility(
+                new CreateTokenEffect(new BirdToken(), 1, true, true), false
+        ));
+    }
+
+    private FalconerAdept(final FalconerAdept card) {
+        super(card);
+    }
+
+    @Override
+    public FalconerAdept copy() {
+        return new FalconerAdept(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 9823216759..2b84857607 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -66,6 +66,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Eliminate", 97, Rarity.UNCOMMON, mage.cards.e.Eliminate.class));
         cards.add(new SetCardInfo("Fabled Passage", 246, Rarity.RARE, mage.cards.f.FabledPassage.class));
         cards.add(new SetCardInfo("Faith's Fetters", 17, Rarity.UNCOMMON, mage.cards.f.FaithsFetters.class));
+        cards.add(new SetCardInfo("Falconer Adept", 18, Rarity.UNCOMMON, mage.cards.f.FalconerAdept.class));
         cards.add(new SetCardInfo("Fierce Empath", 181, Rarity.UNCOMMON, mage.cards.f.FierceEmpath.class));
         cards.add(new SetCardInfo("Fiery Emancipation", 143, Rarity.MYTHIC, mage.cards.f.FieryEmancipation.class));
         cards.add(new SetCardInfo("Fungal Rebirth", 182, Rarity.UNCOMMON, mage.cards.f.FungalRebirth.class));

From e35afdae0e2bfbe293e1754415a6c01fed1e79aa Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Fri, 12 Jun 2020 08:19:44 -0400
Subject: [PATCH 234/586] Implemented Invigorating Surge

---
 .../src/mage/cards/i/InvigoratingSurge.java   | 66 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 67 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/i/InvigoratingSurge.java

diff --git a/Mage.Sets/src/mage/cards/i/InvigoratingSurge.java b/Mage.Sets/src/mage/cards/i/InvigoratingSurge.java
new file mode 100644
index 0000000000..dfaae8a0e3
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/i/InvigoratingSurge.java
@@ -0,0 +1,66 @@
+package mage.cards.i;
+
+import mage.abilities.Ability;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.counter.AddCountersTargetEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Outcome;
+import mage.counters.CounterType;
+import mage.game.Game;
+import mage.game.permanent.Permanent;
+import mage.target.common.TargetControlledCreaturePermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class InvigoratingSurge extends CardImpl {
+
+    public InvigoratingSurge(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{G}");
+
+        // Put a +1/+1 counter on target creature you control, then double the number of +1/+1 counters on that creature.
+        this.getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.P1P1.createInstance()));
+        this.getSpellAbility().addEffect(new InvigoratingSurgeEffect());
+        this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent());
+    }
+
+    private InvigoratingSurge(final InvigoratingSurge card) {
+        super(card);
+    }
+
+    @Override
+    public InvigoratingSurge copy() {
+        return new InvigoratingSurge(this);
+    }
+}
+
+class InvigoratingSurgeEffect extends OneShotEffect {
+
+    InvigoratingSurgeEffect() {
+        super(Outcome.Benefit);
+        staticText = ", then double the number of +1/+1 counters on that creature";
+    }
+
+    private InvigoratingSurgeEffect(final InvigoratingSurgeEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public InvigoratingSurgeEffect copy() {
+        return new InvigoratingSurgeEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Permanent permanent = game.getPermanent(source.getFirstTarget());
+        if (permanent == null) {
+            return false;
+        }
+        int counterCount = permanent.getCounters(game).getCount(CounterType.P1P1);
+        return counterCount > 0 && permanent.addCounters(CounterType.P1P1.createInstance(counterCount), source, game);
+    }
+}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 2b84857607..f6ce3f540f 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -85,6 +85,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Historian of Zhalfir", 325, Rarity.UNCOMMON, mage.cards.h.HistorianOfZhalfir.class));
         cards.add(new SetCardInfo("Igneous Cur", 153, Rarity.COMMON, mage.cards.i.IgneousCur.class));
         cards.add(new SetCardInfo("Indulging Patrician", 219, Rarity.UNCOMMON, mage.cards.i.IndulgingPatrician.class));
+        cards.add(new SetCardInfo("Invigorating Surge", 190, Rarity.UNCOMMON, mage.cards.i.InvigoratingSurge.class));
         cards.add(new SetCardInfo("Island", 310, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Jeskai Elder", 53, Rarity.UNCOMMON, mage.cards.j.JeskaiElder.class));
         cards.add(new SetCardInfo("Jolrael, Mwonvuli Recluse", 191, Rarity.RARE, mage.cards.j.JolraelMwonvuliRecluse.class));

From f9b16e53fb7195f0699cd273f2801022d4bb74b4 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Fri, 12 Jun 2020 08:25:26 -0400
Subject: [PATCH 235/586] Implemented Tolarian Kraken

---
 .../src/mage/cards/t/TolarianKraken.java      | 49 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 50 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/t/TolarianKraken.java

diff --git a/Mage.Sets/src/mage/cards/t/TolarianKraken.java b/Mage.Sets/src/mage/cards/t/TolarianKraken.java
new file mode 100644
index 0000000000..8fc46dbd91
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/t/TolarianKraken.java
@@ -0,0 +1,49 @@
+package mage.cards.t;
+
+import mage.MageInt;
+import mage.abilities.common.DrawCardControllerTriggeredAbility;
+import mage.abilities.common.delayed.ReflexiveTriggeredAbility;
+import mage.abilities.costs.mana.GenericManaCost;
+import mage.abilities.effects.common.DoWhenCostPaid;
+import mage.abilities.effects.common.MayTapOrUntapTargetEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.target.common.TargetCreaturePermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class TolarianKraken extends CardImpl {
+
+    public TolarianKraken(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}{U}");
+
+        this.subtype.add(SubType.KRAKEN);
+        this.power = new MageInt(4);
+        this.toughness = new MageInt(6);
+
+        // Whenever you draw a card, you may pay {1}. When you do, you may tap or untap target creature.
+        ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility(
+                new MayTapOrUntapTargetEffect(), false,
+                "you may tap or untap target creature"
+        );
+        ability.addTarget(new TargetCreaturePermanent());
+        this.addAbility(new DrawCardControllerTriggeredAbility(new DoWhenCostPaid(
+                ability, new GenericManaCost(1),
+                "Pay {1} to tap or untap a permanent?"
+        ), false));
+    }
+
+    private TolarianKraken(final TolarianKraken card) {
+        super(card);
+    }
+
+    @Override
+    public TolarianKraken copy() {
+        return new TolarianKraken(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index f6ce3f540f..267db69735 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -151,6 +151,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Temple of Triumph", 256, Rarity.RARE, mage.cards.t.TempleOfTriumph.class));
         cards.add(new SetCardInfo("Thieves' Guild Enforcer", 125, Rarity.RARE, mage.cards.t.ThievesGuildEnforcer.class));
         cards.add(new SetCardInfo("Tide Skimmer", 79, Rarity.UNCOMMON, mage.cards.t.TideSkimmer.class));
+        cards.add(new SetCardInfo("Tolarian Kraken", 80, Rarity.UNCOMMON, mage.cards.t.TolarianKraken.class));
         cards.add(new SetCardInfo("Tormod's Crypt", 241, Rarity.UNCOMMON, mage.cards.t.TormodsCrypt.class));
         cards.add(new SetCardInfo("Trufflesnout", 212, Rarity.COMMON, mage.cards.t.Trufflesnout.class));
         cards.add(new SetCardInfo("Ugin, the Spirit Dragon", 1, Rarity.MYTHIC, mage.cards.u.UginTheSpiritDragon.class));

From bc2d5caa429aa3509632488023e0c5b0ed71428c Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Fri, 12 Jun 2020 08:30:05 -0400
Subject: [PATCH 236/586] Implemented Traitorous Greed

---
 .../src/mage/cards/t/TraitorousGreed.java     | 42 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 43 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/t/TraitorousGreed.java

diff --git a/Mage.Sets/src/mage/cards/t/TraitorousGreed.java b/Mage.Sets/src/mage/cards/t/TraitorousGreed.java
new file mode 100644
index 0000000000..86870ecba2
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/t/TraitorousGreed.java
@@ -0,0 +1,42 @@
+package mage.cards.t;
+
+import mage.abilities.effects.common.UntapTargetEffect;
+import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
+import mage.abilities.effects.common.continuous.GainControlTargetEffect;
+import mage.abilities.effects.mana.AddManaOfAnyColorEffect;
+import mage.abilities.keyword.HasteAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.target.common.TargetCreaturePermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class TraitorousGreed extends CardImpl {
+
+    public TraitorousGreed(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{R}");
+
+        // Gain control of target creature until end of turn. Untap that creature. It gains haste until end of turn. Add two mana of any one color.
+        this.getSpellAbility().addEffect(new GainControlTargetEffect(Duration.EndOfTurn));
+        this.getSpellAbility().addEffect(new UntapTargetEffect().setText("Untap that creature"));
+        this.getSpellAbility().addEffect(new GainAbilityTargetEffect(
+                HasteAbility.getInstance(), Duration.EndOfTurn
+        ).setText("It gains haste until end of turn."));
+        this.getSpellAbility().addEffect(new AddManaOfAnyColorEffect(2));
+        this.getSpellAbility().addTarget(new TargetCreaturePermanent());
+    }
+
+    private TraitorousGreed(final TraitorousGreed card) {
+        super(card);
+    }
+
+    @Override
+    public TraitorousGreed copy() {
+        return new TraitorousGreed(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 267db69735..227b2e906d 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -153,6 +153,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Tide Skimmer", 79, Rarity.UNCOMMON, mage.cards.t.TideSkimmer.class));
         cards.add(new SetCardInfo("Tolarian Kraken", 80, Rarity.UNCOMMON, mage.cards.t.TolarianKraken.class));
         cards.add(new SetCardInfo("Tormod's Crypt", 241, Rarity.UNCOMMON, mage.cards.t.TormodsCrypt.class));
+        cards.add(new SetCardInfo("Traitorous Greed", 166, Rarity.UNCOMMON, mage.cards.t.TraitorousGreed.class));
         cards.add(new SetCardInfo("Trufflesnout", 212, Rarity.COMMON, mage.cards.t.Trufflesnout.class));
         cards.add(new SetCardInfo("Ugin, the Spirit Dragon", 1, Rarity.MYTHIC, mage.cards.u.UginTheSpiritDragon.class));
         cards.add(new SetCardInfo("Valorous Steed", 42, Rarity.COMMON, mage.cards.v.ValorousSteed.class));

From f480d0bebb84e230539a8a3cf05b2aed7b70c8f1 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Fri, 12 Jun 2020 15:14:28 +0200
Subject: [PATCH 237/586] * Some minor code and text clean up.

---
 .../src/mage/game/OathbreakerFreeForAll.java     | 16 ++++++++--------
 Mage.Sets/src/mage/cards/t/TreeOfPerdition.java  |  2 +-
 .../main/java/mage/game/GameCommanderImpl.java   |  4 ++--
 Mage/src/main/java/mage/players/PlayerImpl.java  |  3 ++-
 4 files changed, 13 insertions(+), 12 deletions(-)

diff --git a/Mage.Server.Plugins/Mage.Game.OathbreakerFreeForAll/src/mage/game/OathbreakerFreeForAll.java b/Mage.Server.Plugins/Mage.Game.OathbreakerFreeForAll/src/mage/game/OathbreakerFreeForAll.java
index 90f673c200..7cbb8eb5b0 100644
--- a/Mage.Server.Plugins/Mage.Game.OathbreakerFreeForAll/src/mage/game/OathbreakerFreeForAll.java
+++ b/Mage.Server.Plugins/Mage.Game.OathbreakerFreeForAll/src/mage/game/OathbreakerFreeForAll.java
@@ -25,8 +25,8 @@ import java.util.*;
 public class OathbreakerFreeForAll extends GameCommanderImpl {
 
     private int numPlayers;
-    private Map<UUID, Set<UUID>> playerSignatureSpells = new HashMap<>();
-    private Map<UUID, Set<UUID>> playerOathbreakers = new HashMap<>();
+    private final Map<UUID, Set<UUID>> playerSignatureSpells = new HashMap<>();
+    private final Map<UUID, Set<UUID>> playerOathbreakers = new HashMap<>();
 
     private static final String COMMANDER_NAME_OATHBREAKER = "Oathbreaker";
     private static final String COMMANDER_NAME_SIGNATURE_SPELL = "Signature Spell";
@@ -122,19 +122,19 @@ public class OathbreakerFreeForAll extends GameCommanderImpl {
         if (player != null) {
             Set<UUID> commanders = this.playerOathbreakers.getOrDefault(player.getId(), new HashSet<>());
             Set<UUID> spells = this.playerSignatureSpells.getOrDefault(player.getId(), new HashSet<>());
-            for (UUID id : player.getCommandersIds()) {
+            for (UUID commanderId : getCommandersIds(player, commanderCardType)) {
                 switch (commanderCardType) {
                     case ANY:
-                        res.add(id);
+                        res.add(commanderId);
                         break;
                     case COMMANDER_OR_OATHBREAKER:
-                        if (commanders.contains(id)) {
-                            res.add(id);
+                        if (commanders.contains(commanderId)) {
+                            res.add(commanderId);
                         }
                         break;
                     case SIGNATURE_SPELL:
-                        if (spells.contains(id)) {
-                            res.add(id);
+                        if (spells.contains(commanderId)) {
+                            res.add(commanderId);
                         }
                         break;
                     default:
diff --git a/Mage.Sets/src/mage/cards/t/TreeOfPerdition.java b/Mage.Sets/src/mage/cards/t/TreeOfPerdition.java
index 78a0d448b8..131b6a1d5d 100644
--- a/Mage.Sets/src/mage/cards/t/TreeOfPerdition.java
+++ b/Mage.Sets/src/mage/cards/t/TreeOfPerdition.java
@@ -54,7 +54,7 @@ class TreeOfPerditionEffect extends OneShotEffect {
 
     public TreeOfPerditionEffect() {
         super(Outcome.Neutral);
-        staticText = "Exchange target opponent's life total with Tree of Perdition's toughness";
+        staticText = "Exchange target opponent's life total with {this}'s toughness";
     }
 
     public TreeOfPerditionEffect(final TreeOfPerditionEffect effect) {
diff --git a/Mage/src/main/java/mage/game/GameCommanderImpl.java b/Mage/src/main/java/mage/game/GameCommanderImpl.java
index 31709e2965..ade44a7727 100644
--- a/Mage/src/main/java/mage/game/GameCommanderImpl.java
+++ b/Mage/src/main/java/mage/game/GameCommanderImpl.java
@@ -55,8 +55,8 @@ public abstract class GameCommanderImpl extends GameImpl {
             Player player = getPlayer(playerId);
             if (player != null) {
                 // add new commanders
-                for (UUID id : player.getSideboard()) {
-                    Card card = this.getCard(id);
+                for (UUID cardId : player.getSideboard()) {
+                    Card card = this.getCard(cardId);
                     if (card != null) {
                         // Check for companions. If it is the only card in the sideboard, it is the commander, not a companion.
                         if (player.getSideboard().size() > 1 && card.getAbilities(this).stream().anyMatch(ability -> ability instanceof CompanionAbility)) {
diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java
index db4f20f661..c7005478da 100644
--- a/Mage/src/main/java/mage/players/PlayerImpl.java
+++ b/Mage/src/main/java/mage/players/PlayerImpl.java
@@ -4272,7 +4272,8 @@ public abstract class PlayerImpl implements Player, Serializable {
         cards.addAll(getLibrary().getTopCards(game, value));
         if (!cards.isEmpty()) {
             TargetCard target = new TargetCard(0, cards.size(), Zone.LIBRARY,
-                    new FilterCard("cards to PUT on the BOTTOM of your library (Scry)"));
+                    new FilterCard("card" + (cards.size() == 1 ? "":"s") 
+                            + " to PUT on the BOTTOM of your library (Scry)"));
             chooseTarget(Outcome.Benefit, cards, target, source, game);
             putCardsOnBottomOfLibrary(new CardsImpl(target.getTargets()), game, source, true);
             cards.removeAll(target.getTargets());

From fe8a334ffe4eebf2da3ee54064fd9c1ca49af317 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Fri, 12 Jun 2020 15:48:41 +0200
Subject: [PATCH 238/586] * Added test for #6291 Unlicensed Disintigration.

---
 .../cards/u/UnlicensedDisintegration.java     |  2 +-
 .../oneshot/ConditionalOneShotEffectTest.java | 78 +++++++++++++++++++
 2 files changed, 79 insertions(+), 1 deletion(-)
 create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/ConditionalOneShotEffectTest.java

diff --git a/Mage.Sets/src/mage/cards/u/UnlicensedDisintegration.java b/Mage.Sets/src/mage/cards/u/UnlicensedDisintegration.java
index 58ea7b8389..712139157a 100644
--- a/Mage.Sets/src/mage/cards/u/UnlicensedDisintegration.java
+++ b/Mage.Sets/src/mage/cards/u/UnlicensedDisintegration.java
@@ -27,7 +27,7 @@ public final class UnlicensedDisintegration extends CardImpl {
         this.getSpellAbility().addEffect(new ConditionalOneShotEffect(
                 new DamageTargetControllerEffect(3),
                 new PermanentsOnTheBattlefieldCondition(new FilterControlledArtifactPermanent()),
-                "If you control an artifact, Unlicensed Disintegration deals 3 damage to that creature's controller"));
+                "If you control an artifact, {this} deals 3 damage to that creature's controller"));
         
     }
 
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/ConditionalOneShotEffectTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/ConditionalOneShotEffectTest.java
new file mode 100644
index 0000000000..3d1b8dca14
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/ConditionalOneShotEffectTest.java
@@ -0,0 +1,78 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.mage.test.cards.abilities.oneshot;
+
+import mage.constants.PhaseStep;
+import mage.constants.Zone;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestPlayerBase;
+
+/**
+ *
+ * @author LevelX2
+ */
+public class ConditionalOneShotEffectTest extends CardTestPlayerBase {
+
+    @Test
+    public void testDisintegrationWithoutArtifact() {
+        addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
+        // Destroy target creature. If you control an artifact, Unlicensed Disintegration deals 3 damage to that creature's controller.        
+        addCard(Zone.HAND, playerA, "Unlicensed Disintegration", 1); // Instant {1}{B}{R}
+
+        addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1);
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Unlicensed Disintegration", "Silvercoat Lion");
+        
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        
+        setStrictChooseMode(true);
+        execute();
+        assertAllCommandsUsed();
+        
+        assertGraveyardCount(playerA, "Unlicensed Disintegration", 1);
+        assertGraveyardCount(playerB, "Silvercoat Lion", 1);
+
+        assertLife(playerA, 20);
+        assertLife(playerB, 20);
+    }
+    
+   /**
+     * Noticed that everytime that i succesfully cast Unlicensed Disintigration
+     * with an artifact on the board the opponent wont lose 3 life. The creature
+     * dies but the last piece of text does not work
+     */
+    @Test
+    public void testDisintegrationWithArtifact() {
+        addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
+        
+        // Whenever a player casts a red spell, you may gain 1 life.
+        addCard(Zone.BATTLEFIELD, playerA, "Dragon's Claw", 1);
+        
+        // Destroy target creature. If you control an artifact, Unlicensed Disintegration deals 3 damage to that creature's controller.        
+        addCard(Zone.HAND, playerA, "Unlicensed Disintegration", 1); // Instant {1}{B}{R}
+
+        addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1);
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Unlicensed Disintegration", "Silvercoat Lion");
+        
+        setChoice(playerA, "Yes"); // Get life from Dragon's Claw
+        
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        
+        setStrictChooseMode(true);
+        execute();
+        assertAllCommandsUsed();
+        
+        assertGraveyardCount(playerA, "Unlicensed Disintegration", 1);
+        assertGraveyardCount(playerB, "Silvercoat Lion", 1);
+
+        assertLife(playerA, 21);
+        assertLife(playerB, 17);
+    }
+
+}

From f0080462fa60ac035766644ceed1c602fb56a4be Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Fri, 12 Jun 2020 16:10:07 +0200
Subject: [PATCH 239/586] * Allow player names with space for the init.txt test
 help.

---
 Mage.Server/src/main/java/mage/server/util/SystemUtil.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Mage.Server/src/main/java/mage/server/util/SystemUtil.java b/Mage.Server/src/main/java/mage/server/util/SystemUtil.java
index 1d64adf871..dec5a49741 100644
--- a/Mage.Server/src/main/java/mage/server/util/SystemUtil.java
+++ b/Mage.Server/src/main/java/mage/server/util/SystemUtil.java
@@ -82,7 +82,7 @@ public final class SystemUtil {
     }
 
     private static final Pattern patternGroup = Pattern.compile("\\[(.+)\\]"); // [test new card]
-    private static final Pattern patternCommand = Pattern.compile("([\\w]+):([\\S]+?):([\\S ]+):([\\d]+)"); // battlefield:Human:Island:10
+    private static final Pattern patternCommand = Pattern.compile("([\\w]+):([\\S ]+?):([\\S ]+):([\\d]+)"); // battlefield:Human:Island:10
     private static final Pattern patternCardInfo = Pattern.compile("([\\S ]+):([\\S ]+)"); // Island:XLN
 
     // show ext info for special commands

From 1e0ffe95e8ddbfddd33794a316c89857cb1ee401 Mon Sep 17 00:00:00 2001
From: 18ths <13023067+18ths@users.noreply.github.com>
Date: Fri, 12 Jun 2020 21:13:42 +0200
Subject: [PATCH 240/586] fixed #1219 - Cavern of Souls and Boseiju, Who
 Shelters All preventing counterspells even after permanent was bounced
 (#6634)

---
 .../mage/cards/b/BoseijuWhoSheltersAll.java   | 18 ++++----
 Mage.Sets/src/mage/cards/c/CavernOfSouls.java | 18 ++++----
 .../cards/single/avr/CavernOfSoulsTest.java   | 41 +++++++++++++++++++
 3 files changed, 59 insertions(+), 18 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/b/BoseijuWhoSheltersAll.java b/Mage.Sets/src/mage/cards/b/BoseijuWhoSheltersAll.java
index 86f68cfe0b..95e22f7488 100644
--- a/Mage.Sets/src/mage/cards/b/BoseijuWhoSheltersAll.java
+++ b/Mage.Sets/src/mage/cards/b/BoseijuWhoSheltersAll.java
@@ -1,10 +1,10 @@
 
 package mage.cards.b;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
+import java.util.*;
+
 import mage.MageObject;
+import mage.MageObjectReference;
 import mage.Mana;
 import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldTappedAbility;
@@ -58,7 +58,7 @@ public final class BoseijuWhoSheltersAll extends CardImpl {
 
 class BoseijuWhoSheltersAllWatcher extends Watcher {
 
-    private List<UUID> spells = new ArrayList<>();
+    private final Set<MageObjectReference> spells = new HashSet<>();
     private final UUID originalId;
 
     public BoseijuWhoSheltersAllWatcher(UUID originalId) {
@@ -69,17 +69,17 @@ class BoseijuWhoSheltersAllWatcher extends Watcher {
     @Override
     public void watch(GameEvent event, Game game) {
         if (event.getType() == GameEvent.EventType.MANA_PAID) {
-            if (event.getData() != null && event.getData().equals(originalId.toString())) {
+            if (event.getData() != null && event.getData().equals(originalId.toString()) && event.getTargetId() != null) {
                 Card spell = game.getSpell(event.getTargetId());
                 if (spell != null && (spell.isInstant() || spell.isSorcery())) {
-                    spells.add(event.getTargetId());
+                    spells.add(new MageObjectReference(game.getObject(event.getTargetId()), game));
                 }
             }
         }
     }
 
-    public boolean spellCantBeCountered(UUID spellId) {
-        return spells.contains(spellId);
+    public boolean spellCantBeCountered(MageObjectReference mor) {
+        return spells.contains(mor);
     }
 
     @Override
@@ -128,6 +128,6 @@ class BoseijuWhoSheltersAllCantCounterEffect extends ContinuousRuleModifyingEffe
     public boolean applies(GameEvent event, Ability source, Game game) {
         BoseijuWhoSheltersAllWatcher watcher = game.getState().getWatcher(BoseijuWhoSheltersAllWatcher.class, source.getSourceId());
         Spell spell = game.getStack().getSpell(event.getTargetId());
-        return spell != null && watcher != null && watcher.spellCantBeCountered(spell.getId());
+        return spell != null && watcher != null && watcher.spellCantBeCountered(new MageObjectReference(spell, game));
     }
 }
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/c/CavernOfSouls.java b/Mage.Sets/src/mage/cards/c/CavernOfSouls.java
index 1700ea17be..bea0f012e5 100644
--- a/Mage.Sets/src/mage/cards/c/CavernOfSouls.java
+++ b/Mage.Sets/src/mage/cards/c/CavernOfSouls.java
@@ -1,11 +1,11 @@
 
 package mage.cards.c;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
+import java.util.*;
+
 import mage.ConditionalMana;
 import mage.MageObject;
+import mage.MageObjectReference;
 import mage.Mana;
 import mage.abilities.Ability;
 import mage.abilities.common.AsEntersBattlefieldAbility;
@@ -121,7 +121,7 @@ class CavernOfSoulsManaCondition extends CreatureCastManaCondition {
 
 class CavernOfSoulsWatcher extends Watcher {
 
-    private List<UUID> spells = new ArrayList<>();
+    private final Set<MageObjectReference> spells = new HashSet<>();
     private final UUID originalId;
 
     public CavernOfSoulsWatcher(UUID originalId) {
@@ -132,14 +132,14 @@ class CavernOfSoulsWatcher extends Watcher {
     @Override
     public void watch(GameEvent event, Game game) {
         if (event.getType() == GameEvent.EventType.MANA_PAID) {
-            if (event.getData() != null && event.getData().equals(originalId.toString())) {
-                spells.add(event.getTargetId());
+            if (event.getData() != null && event.getData().equals(originalId.toString()) && event.getTargetId() != null) {
+                spells.add(new MageObjectReference(game.getObject(event.getTargetId()), game));
             }
         }
     }
 
-    public boolean spellCantBeCountered(UUID spellId) {
-        return spells.contains(spellId);
+    public boolean spellCantBeCountered(MageObjectReference mor) {
+        return spells.contains(mor);
     }
 
     @Override
@@ -188,6 +188,6 @@ class CavernOfSoulsCantCounterEffect extends ContinuousRuleModifyingEffectImpl {
     public boolean applies(GameEvent event, Ability source, Game game) {
         CavernOfSoulsWatcher watcher = game.getState().getWatcher(CavernOfSoulsWatcher.class, source.getSourceId());
         Spell spell = game.getStack().getSpell(event.getTargetId());
-        return spell != null && watcher != null && watcher.spellCantBeCountered(spell.getId());
+        return spell != null && watcher != null && watcher.spellCantBeCountered(new MageObjectReference(spell, game));
     }
 }
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/avr/CavernOfSoulsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/avr/CavernOfSoulsTest.java
index bfbaef65bb..4bbe5029b3 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/single/avr/CavernOfSoulsTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/avr/CavernOfSoulsTest.java
@@ -255,4 +255,45 @@ public class CavernOfSoulsTest extends CardTestPlayerBase {
 
     }
 
+    @Test
+    public void testBouncedCreatureNotCountered() {
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
+        addCard(Zone.HAND, playerA, "Forest");
+        addCard(Zone.HAND, playerA, "Cavern of Souls");
+        addCard(Zone.HAND, playerA, "Runeclaw Bear");
+
+        addCard(Zone.HAND, playerB, "Counterspell", 2);
+        addCard(Zone.HAND, playerB, "Unsummon");
+        addCard(Zone.BATTLEFIELD, playerB, "Island", 5);
+
+        playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cavern of Souls");
+        setChoice(playerA, "Bear");
+
+        //wait for next turn, we'll need our next land drop
+        castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Runeclaw Bear");
+
+        //make sure we used our cavern already and try to counter
+        castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerB, "Counterspell");
+        waitStackResolved(3, PhaseStep.PRECOMBAT_MAIN);
+
+        checkPermanentCount("bear not countered", 3, PhaseStep.PRECOMBAT_MAIN, playerA, "Runeclaw Bear", 1);
+
+        //counterspell fizzled, return bear to hand to try countering it again
+        castSpell(3, PhaseStep.BEGIN_COMBAT, playerB, "Unsummon", "Runeclaw Bear");
+        waitStackResolved(3, PhaseStep.BEGIN_COMBAT);
+
+        //recast bear, without cavern of souls conditional mana
+        playLand(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Forest");
+        castSpell(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Runeclaw Bear");
+        castSpell(3, PhaseStep.POSTCOMBAT_MAIN, playerB, "Counterspell");
+
+        setStopAt(3, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerA, "Cavern of Souls", 1);
+        assertGraveyardCount(playerA, "Runeclaw Bear", 1);
+        assertGraveyardCount(playerB, "Counterspell", 2);
+        assertGraveyardCount(playerB, "Unsummon", 1);
+    }
 }

From d58d5ecd457f5260fea512851db982edbccb884e Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Fri, 12 Jun 2020 18:56:10 -0400
Subject: [PATCH 241/586] updated M21 spoiler and reprints

---
 Mage.Sets/src/mage/sets/CoreSet2021.java |  3 +++
 Utils/mtg-cards-data.txt                 | 19 +++++++++++++++++++
 2 files changed, 22 insertions(+)

diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 227b2e906d..d2674927a1 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -46,6 +46,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Basri's Lieutenant", 9, Rarity.RARE, mage.cards.b.BasrisLieutenant.class));
         cards.add(new SetCardInfo("Basri's Solidarity", 10, Rarity.UNCOMMON, mage.cards.b.BasrisSolidarity.class));
         cards.add(new SetCardInfo("Basri, Devoted Paladin", 320, Rarity.MYTHIC, mage.cards.b.BasriDevotedPaladin.class));
+        cards.add(new SetCardInfo("Battle-Rattle Shaman", 130, Rarity.UNCOMMON, mage.cards.b.BattleRattleShaman.class));
         cards.add(new SetCardInfo("Bolt Hound", 131, Rarity.UNCOMMON, mage.cards.b.BoltHound.class));
         cards.add(new SetCardInfo("Burlfist Oak", 174, Rarity.UNCOMMON, mage.cards.b.BurlfistOak.class));
         cards.add(new SetCardInfo("Carrion Grub", 92, Rarity.UNCOMMON, mage.cards.c.CarrionGrub.class));
@@ -54,6 +55,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Chandra's Magmutt", 137, Rarity.COMMON, mage.cards.c.ChandrasMagmutt.class));
         cards.add(new SetCardInfo("Chandra, Flame's Catalyst", 332, Rarity.MYTHIC, mage.cards.c.ChandraFlamesCatalyst.class));
         cards.add(new SetCardInfo("Chromatic Orrery", 228, Rarity.MYTHIC, mage.cards.c.ChromaticOrrery.class));
+        cards.add(new SetCardInfo("Colossal Dreadmaw", 176, Rarity.COMMON, mage.cards.c.ColossalDreadmaw.class));
         cards.add(new SetCardInfo("Conclave Mentor", 216, Rarity.UNCOMMON, mage.cards.c.ConclaveMentor.class));
         cards.add(new SetCardInfo("Containment Priest", 314, Rarity.RARE, mage.cards.c.ContainmentPriest.class));
         cards.add(new SetCardInfo("Cultivate", 177, Rarity.UNCOMMON, mage.cards.c.Cultivate.class));
@@ -156,6 +158,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Traitorous Greed", 166, Rarity.UNCOMMON, mage.cards.t.TraitorousGreed.class));
         cards.add(new SetCardInfo("Trufflesnout", 212, Rarity.COMMON, mage.cards.t.Trufflesnout.class));
         cards.add(new SetCardInfo("Ugin, the Spirit Dragon", 1, Rarity.MYTHIC, mage.cards.u.UginTheSpiritDragon.class));
+        cards.add(new SetCardInfo("Unsubstantiate", 82, Rarity.UNCOMMON, mage.cards.u.Unsubstantiate.class));
         cards.add(new SetCardInfo("Valorous Steed", 42, Rarity.COMMON, mage.cards.v.ValorousSteed.class));
         cards.add(new SetCardInfo("Village Rites", 126, Rarity.COMMON, mage.cards.v.VillageRites.class));
         cards.add(new SetCardInfo("Vito, Thorn of the Dusk Rose", 127, Rarity.RARE, mage.cards.v.VitoThornOfTheDuskRose.class));
diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt
index b3c1dc34fa..bb51cb03d6 100644
--- a/Utils/mtg-cards-data.txt
+++ b/Utils/mtg-cards-data.txt
@@ -37446,9 +37446,11 @@ Basri Ket|Core Set 2021|7|M|{1}{W}{W}|Legendary Planeswalker - Basri|3|+1: Put a
 Basri's Acolyte|Core Set 2021|8|C|{2}{W}{W}|Creature - Cat Cleric|2|3|Lifelink$When Basri's Acolyte enters the battlefield, put a +1/+1 counter on each of up to two other target creatures you control.|
 Basri's Lieutenant|Core Set 2021|9|R|{3}{W}|Creature - Human Knight|3|4|Vigilance, protection from multicolored$When Basri's Lieutenant enters the battlefield, put a +1/+1 counter on target creature you control.$Whenever Basri's Lieutenant or another creature you control dies, if it had a +1/+1 counter on it, create a 2/2 white Knight creature token with vigilance.|
 Basri's Solidarity|Core Set 2021|10|U|{1}{W}|Sorcery|||Put a +1/+1 counter on each creature you control.|
+Celestial Enforcer|Core Set 2021|11|C|{2}{W}|Creature - Human Cleric|2|3|{1}{W}, {T}: Tap target creature. Activate this ability only if you control a creature with flying.|
 Defiant Strike|Core Set 2021|15|C|{W}|Instant|||Target creature gets +1/+0 until end of turn.$Draw a card.|
 Faith's Fetters|Core Set 2021|17|U|{3}{W}|Enchantment - Aura|||Enchant permanent$When Faith's Fetters enters the battlefield, you gain 4 life.$Enchanted permanent can't attack or block, and its activated abilities can't be activated unless they're mana abilities.|
 Falconer Adept|Core Set 2021|18|U|{3}{W}|Creature - Human Soldier|2|3|Whenever Falconer Adept attacks, create a 1/1 white Bird creature token with flying that's tapped and attacking.|
+Griffin Aerie|Core Set 2021|22|U|{1}{W}|Enchantment|||At the beginning of your end step, if you gained 3 or more life this turn, create a 2/2 white Griffin creature token with flying.|
 Idol of Endurance|Core Set 2021|23|R|{2}{W}|Artifact|||When Idol of Endurance enters the battlefield, exile all creature cards with converted mana cost 3 or less from your graveyard until Idol of Endurance leaves the battlefield.${1}{W}, {T}: Until end of turn, you may cast a creature spell from among the cards exiled with Idol of Endurance without paying its mana cost.|
 Light of Promise|Core Set 2021|25|U|{2}{W}|Enchantment - Aura|||Enchant creature$Enchanted creature has "Whenever you gain life, put that many +1/+1 counters on this creature."|
 Mangara, the Diplomat|Core Set 2021|27|M|{3}{W}|Legendary Creature - Human Cleric|2|4|Lifelink$Whenever an opponent attacks with creatures, if two or more of those creatures are attacking you and/or a planeswalker you control, draw a card.$Whenever an opponent casts their second spell each turn, draw a card.|
@@ -37459,18 +37461,22 @@ Runed Halo|Core Set 2021|32|R|{W}{W}|Enchantment|||As Runed Halo enters the batt
 Seasoned Hallowblade|Core Set 2021|34|U|{1}{W}|Creature - Human Warrior|3|1|Discard a card: Tap Seasoned Hallowblade. It gains indestructible until end of turn.|
 Selfless Savior|Core Set 2021|36|U|{W}|Creature - Dog|1|1|Sacrifice Selfless Savior: Another target creature you control gains indestructible until end of turn.|
 Siege Striker|Core Set 2021|37|U|{2}{W}|Creature - Human Soldier|1|1|Double strike$Whenever Siege Striker attacks, you may tap any number of untapped creatures you control. Siege Striker gets +1/+1 until end of turn for each creature tapped this way.|
+Speaker of the Heavens|Core Set 2021|38|R|{W}|Creature - Human Cleric|1|1|Vigilance, lifelink${T}: Create a 4/4 white Angel creature token with flying. Activate this ability only if you have at least 7 more life than your starting life total and only any time you could cast a sorcery.|
 Tempered Veteran|Core Set 2021|41|U|{1}{W}|Creature - Human Knight|1|2|{W}, {T}: Put a +1/+1 counter on target creature with a +1/+1 counter on it.${4}{W}{W}, {T}: Put a +1/+1 counter on target creature.|
 Valorous Steed|Core Set 2021|42|C|{4}{W}|Creature - Unicorn|3|3|Vigilance$When Valorous Steed enters the battlefield, create a 2/2 white Knight creature token with vigilance.|
 Vryn Wingmare|Core Set 2021|43|U|{2}{W}|Creature - Pegasus|2|1|Flying$Noncreature spells cost {1} more to cast.|
+Discontinuity|Core Set 2021|48|M|{3}{U}{U}{U}|Instant|||As long as it's your turn, this spell costs {2}{U}{U} less to cast.$End the turn.|
 Enthralling Hold|Core Set 2021|49|U|{3}{U}{U}|Enchantment - Aura|||Enchant creature$You can't choose an untapped creature as this spell's target as you cast it.$You control enchanted creature.|
 Frantic Inventory|Core Set 2021|50|C|{1}{U}|Instant|||Draw a card, then draw cards equal to the number of cards named Frantic Inventory in your graveyard.|
 Jeskai Elder|Core Set 2021|53|U|{1}{U}|Creature - Human Monk|1|2|Prowess$Whenever Jeskai Elder deals combat damage to a player, you may draw a card. If you do, discard a card.|
+Library Larcenist|Core Set 2021|55|C|{2}{U}|Creature - Merfolk Rogue|1|2|Whenever Library Larcenist attacks, draw a card.|
 Lofty Denial|Core Set 2021|56|C|{1}{U}|Instant|||Counter target spell unless its controller pays {1}. If you control a creature with flying, counter that spell unless its controller pays {4} instead.|
 Miscast|Core Set 2021|57|U|{U}|Instant|||Counter target instant or sorcery spell unless its controller pays {3}.|
 Pursued Whale|Core Set 2021|60|R|{5}{U}{U}|Creature - Whale|8|8|When Pursued Whale enters the battlefield, each opponent creates a 1/1 red Pirate creature token with "This creature can't block" and "Creatures you control attack each combat if able."$Spells your opponents cast that target Pursued Whale cost {3} more to cast.|
 Rain of Revelation|Core Set 2021|61|U|{3}{U}|Instant|||Draw three cards, then discard a card.|
 Rewind|Core Set 2021|63|U|{2}{U}{U}|Instant|||Counter target spell. Untap up to four lands.|
 Riddleform|Core Set 2021|64|U|{1}{U}|Enchantment|||Whenever you cast a noncreature spell, you may have Riddleform become a 3/3 Sphinx creature with flying in addition to its other types until end of turn.${2}{U}: Scry 1.|
+Rousing Read|Core Set 2021|67|C|{2}{U}|Enchantment - Aura|||Enchant creature$When Rousing Read enters the battlefield, draw two cards, then discard a card.$Enchanted creature gets +1/+1 and has flying.|
 See the Truth|Core Set 2021|69|R|{1}{U}|Sorcery|||Look at the top three cards of your library. Put one of those cards into your hand and the rest on the bottom of your library in any order. If this spell was cast from anywhere other than your hand, put each of those cards into your hand instead.|
 Shipwreck Dowser|Core Set 2021|71|U|{3}{U}{U}|Creature - Merfolk Wizard|3|3|Prowess$When Shipwreck Dowser enters the battlefield, return target instant or sorcery card from your graveyard to your hand.|
 Spined Megalodon|Core Set 2021|72|C|{5}{U}{U}|Creature - Shark|5|7|Hexproof$Whenever Spined Megalodon attacks, scry 1.|
@@ -37481,14 +37487,18 @@ Teferi's Protege|Core Set 2021|77|C|{2}{U}|Creature - Human Wizard|2|3|{1}{U}, {
 Teferi's Tutelage|Core Set 2021|78|U|{2}{U}|Enchantment|||When Teferi's Tutelage enters the battlefield, draw a card, then discard a card.$Whenever you draw a card, target opponent mills two cards.|
 Tide Skimmer|Core Set 2021|79|U|{3}{U}|Creature - Drake|2|3|Flying$Whenever you attack with two or more creatures with flying, draw a card.|
 Tolarian Kraken|Core Set 2021|80|U|{4}{U}{U}|Creature - Kraken|4|6|Whenever you draw a card, you may pay {1}. When you do, you may tap or untap target creature.|
+Unsubstantiate|Core Set 2021|82|U|{1}{U}|Instant|||Return target spell or creature to its owner's hand.|
 Waker of Waves|Core Set 2021|84|U|{5}{U}{U}|Creature - Whale|7|7|Creatures your opponents control get -1/-0.${1}{U}, Discard Waker of Waves: Look at the top two cards of your library. Put one of them into your hand and the other into your graveyard.|
 Archfiend's Vessel|Core Set 2021|88|U|{B}|Creature - Human Cleric|1|1|Lifelink$When Archfiend's Vessel enters the battlefield, if it entered from your graveyard or you cast it from your graveyard, exile it. If you do, create a 5/5 black Demon creature token with flying.|
 Bad Deal|Core Set 2021|89|U|{4}{B}{B}|Sorcery|||You draw two cards and each opponent discards two cards. Each player loses 2 life.|
 Carrion Grub|Core Set 2021|92|U|{3}{B}|Creature - Insect|0|5|Carrion Grub gets +X/+0, where X is the greatest power among creature cards in your graveyard.$When Carrion Grub enters the battlefield, mill four cards.|
 Duress|Core Set 2021|96|C|{B}|Sorcery|||Target opponent reveals their hand. You choose a noncreature, nonland card from it. That player discards that card.|
 Eliminate|Core Set 2021|97|U|{1}{B}|Instant|||Destroy target creature or planeswalker with converted mana cost 3 or less.|
+Finishing Blow|Core Set 2021|99|C|{4}{B}|Instant|||Destroy target creature or planeswalker.|
+Goremand|Core Set 2021|101|U|{4}{B}{B}|Creature - Demon|5|5|As an additional cost to cast this spell, sacrifice a creature.$Flying$Trample$When Goremand enters the battlefield, each opponent sacrifices a creature.|
 Grasp of Darkness|Core Set 2021|102|C|{B}{B}|Instant|||Target creature gets -4/-4 until end of turn.|
 Grim Tutor|Core Set 2021|103|M|{1}{B}{B}|Sorcery|||Search your library for a card and put that card into your hand, then shuffle your library. You lose 3 life.|
+Kaervek, the Spiteful|Core Set 2021|106|R|{2}{B}{B}|Legendary Creature - Human Warlock|3|2|Other creatures get -1/-1.|
 Liliana, Waker of the Dead|Core Set 2021|108|M|{2}{B}{B}|Legendary Planeswalker - Liliana|4|+1: Each player discards a card. Each opponent who can't loses 3 life.$−3: Target creature gets -X/-X until end of turn, where X is the number of cards in your graveyard.$−7: You get an emblem with "At the beginning of combat on your turn, put target creature card from a graveyard onto the battlefield under your control. It gains haste."|
 Liliana's Devotee|Core Set 2021|109|U|{2}{B}|Creature - Human Warlock|2|3|Zombies you control get +1/+0.$At the beginning of your end step, if a creature died this turn, you may pay {1}{B}. If you do, create a 2/2 black Zombie creature token.|
 Liliana's Standard Bearer|Core Set 2021|110|R|{2}{B}|Creature - Zombie Knight|3|1|Flash$When Liliana's Standard Bearer enters the battlefield, draw X cards, where X is the number of creatures that died under your control this turn.|
@@ -37498,9 +37508,11 @@ Massacre Wurm|Core Set 2021|114|M|{3}{B}{B}{B}|Creature - Wurm|6|5|When Massacre
 Necromentia|Core Set 2021|116|R|{1}{B}{B}|Sorcery|||Choose a card name other than a basic land card name. Search target opponent's graveyard, hand, and library for any number of cards with that name and exile them. That player shuffles their library, then creates a 2/2 black Zombie creature token for each card exiled from their hand this way.|
 Peer into the Abyss|Core Set 2021|117|R|{4}{B}{B}{B}|Sorcery|||Target player draws cards equal to half the number of cards in their library and loses half their life. Round up each time.|
 Pestilent Haze|Core Set 2021|118|U|{1}{B}{B}|Sorcery|||Choose one —$• All creatures get -2/-2 until end of turn.$• Remove two loyalty counters from each planeswalker.|
+Silversmote Ghoul|Core Set 2021|122|U|{2}{B}|Creature - Zombie Vampire|3|1|At the beginning of your end step, if you gained 3 or more life this turn, return Silversmote Ghoul from your graveyard to the battlefield tapped.${1}{B}, Sacrifice Silversmote Ghoul: Draw a card.|
 Thieves' Guild Enforcer|Core Set 2021|125|R|{B}|Creature - Human Rogue|1|1|Flash$Whenever Thieves' Guild Enforcer or another Rogue enters the battlefield under your control, each opponent mills two cards.$As long as an opponent has eight or more cards in their graveyard, Thieves' Guild Enforcer gets +2/+1 and has deathtouch.|
 Village Rites|Core Set 2021|126|C|{B}|Instant|||As an additional cost to cast this spell, sacrifice a creature.$Draw two cards.|
 Vito, Thorn of the Dusk Rose|Core Set 2021|127|R|{2}{B}|Legendary Creature - Vampire Cleric|1|3|Whenever you gain life, target opponent loses that much life.${3}{B}{B}: Creatures you control gain lifelink until end of turn.|
+Battle-Rattle Shaman|Core Set 2021|130|U|{3}{R}|Creature - Goblin Shaman|2|2|At the beginning of combat on your turn, you may have target creature get +2/+0 until end of turn.|
 Bolt Hound|Core Set 2021|131|U|{2}{R}|Creature - Elemental Dog|2|2|Haste$Whenever Bolt Hound attacks, other creatures you control get +1/+0 until end of turn.|
 Brash Taunter|Core Set 2021|133|R|{4}{R}|Creature - Goblin|1|1|Indestructible$Whenever Brash Taunter is dealt damage, it deals that much damage to target opponent.${2}{R}, {T}: Brash Taunter fights another target creature.|
 Chandra, Heart of Fire|Core Set 2021|135|M|{3}{R}{R}|Legendary Planeswalker - Chandra|5|+1: Discard your hand, then exile the top three cards of your library. Until end of turn, you may play cards exiled this way.$+1: Chandra, Heart of Fire deals 2 damage to any target.$−9: Search your graveyard and library for any number of red instant and/or sorcery spells, exile them, then shuffle your library. You may cast them this turn. Add six {R}.|
@@ -37519,11 +37531,14 @@ Hellkite Punisher|Core Set 2021|151|U|{5}{R}{R}|Creature - Dragon|6|6|Flying${R}
 Igneous Cur|Core Set 2021|153|C|{1}{R}|Creature - Elemental Dog|1|2|{1}{R}: Igneous Cur gets +2/+0 until end of turn.|
 Kinetic Augur|Core Set 2021|154|U|{3}{R}|Creature - Human Shaman|*|4|Trample$Kinetic Augur's power is equal to the number of instant and sorcery cards in your graveyard.$When Kinetic Augur enters the battlefield, discard up to two cards, then draw that many cards.|
 Soul Sear|Core Set 2021|160|U|{2}{R}|Instant|||Soul Sear deals 5 damage to target creature or planeswalker. That permanent loses indestructible until end of turn.|
+Subira, Tulzidi Caravanner|Core Set 2021|162|R|{2}{R}|Legendary Creature - Human Shaman|2|3|Haste${1}: Another target creature with power 2 or less can't be blocked this turn.${1}{R}, {T}, Discard your hand: Until end of turn, whenever a creature you control with power 2 or less deals combat damage to a player, draw a card.|
+Terror of the Peaks|Core Set 2021|164|M|{3}{R}{R}|Creature - Dragon|5|4|Flying$Spells your opponents cast that target Terror of the Peaks cost an additional 3 life to cast.$Whenever another creature enters the battlefield under your control, Terror of the Peaks deals damage equal to that creature's power to any taret.|
 Traitorous Greed|Core Set 2021|166|U|{3}{R}|Sorcery|||Gain control of target creature until end of turn. Untap that creature. It gains haste until end of turn. Add two mana of any one color.|
 Transmogrify|Core Set 2021|167|R|{3}{R}|Sorcery|||Exile target creature. That creature's controller reveals cards from the top of their library until they reveal a creature card. That player puts that card onto the battlefield, then shuffles the rest into their library.|
 Volcanic Salvo|Core Set 2021|172|R|{10}{R}{R}|Sorcery|||This spell costs {X} less to cast, where X is the total power of creatures you control.$Volcanic Salvo deals 6 damage to each of up to two target creatures and/or planeswalkers.|
 Azusa, Lost but Seeking|Core Set 2021|173|R|{2}{G}|Legendary Creature - Human Monk|1|2|You may play two additional lands on each of your turns.|
 Burlfist Oak|Core Set 2021|174|U|{2}{G}{G}|Creature - Treefolk|2|3|Whenever you draw a card, Burlfist Oak gets +2/+2 until end of turn.|
+Colossal Dreadmaw|Core Set 2021|176|C|{4}{G}{G}|Creature - Dinosaur|6|6|Trample|
 Cultivate|Core Set 2021|177|U|{2}{G}|Sorcery|||Search your library for up to two basic land cards, reveal those cards, and put one onto the battlefield tapped and the other into your hand. Then shuffle your library.|
 Drowsing Tyrannodon|Core Set 2021|178|C|{1}{G}|Creature - Dinosaur|3|3|Defender$As long as you control a creature with power 4 or greater, Drowsing Tyrannodon can attack as though it didn't have defender.|
 Elder Gargaroth|Core Set 2021|179|M|{3}{G}{G}|Creature - Beast|6|6|Vigilance, reach, trample$Whenever Elder Gargaroth attacks or blocks, choose one —$• Create a 3/3 green Beast creature token.$• You gain 3 life.$• Draw a card.|
@@ -37542,6 +37557,8 @@ Pridemalkin|Core Set 2021|196|C|{2}{G}|Creature - Cat|2|1|When Pridemalkin enter
 Primal Might|Core Set 2021|197|R|{X}{G}|Sorcery|||Target creature you control gets +X/+X until end of turn. Then it fights up to one target creature you don't control.|
 Quirion Dryad|Core Set 2021|198|U|{1}{G}|Creature - Dryad|1|1|Whenever you cast a white, blue, black, or red spell, put a +1/+1 counter on Quirion Dryad.|
 Ranger's Guile|Core Set 2021|199|C|{G}|Instant|||Target creature you control gets +1/+1 and gains hexproof until end of turn.|
+Sabertooth Mauler|Core Set 2021|202|C|{3}{G}|Creature - Cat|3|3|At the beginning of your end step, if a creature died this turn, put a +1/+1 counter on Sabertooth Mauler and untap it.|
+Sporeweb Weaver|Core Set 2021|208|R|{2}{G}|Creature - Spider|1|4|Reach, hexproof from blue$Whenever Sporeweb Weaver is dealt damage, you gain 1 life and create a 1/1 green Saproling creature token.|
 Trufflesnout|Core Set 2021|212|C|{2}{G}|Creature - Boar|2|2|When Trufflesnout enters the battlefield, choose one —$• Put a +1/+1 counter on Trufflesnout.$• You gain 4 life.|
 Warden of the Woods|Core Set 2021|213|U|{4}{G}{G}|Creature - Treefolk|5|7|Vigilance$Whenever Warden of the Woods becomes the target of a spell or ability an opponent controls, you may draw two cards.|
 Wildwood Scourge|Core Set 2021|214|U|{X}{G}|Creature - Hydra|0|0|Wildwood Scourge enters the battlefield with X +1/+1 counters on it.$Whenever one or more +1/+1 counters are put on another non-Hydra creature you control, put a +1/+1 counter on Wildwood Scourge.|
@@ -37551,6 +37568,7 @@ Dire Fleet Warmonger|Core Set 2021|217|U|{1}{B}{R}|Creature - Orc Pirate|3|3|At
 Indulging Patrician|Core Set 2021|219|U|{1}{W}{B}|Creature - Vampire Noble|1|4|Flying$Lifelink$At the beginning of your end step, if you gained 3 or more life this turn, each opponent loses 3 life.|
 Leafkin Avenger|Core Set 2021|220|U|{2}{R}{G}|Creature - Elemental Druid|4|3|{T}: Add {G} for each creature you control with power 4 or greater.${7}{R}: Leafkin Avenger deals damage equal to its power to target player or planeswalker.|
 Lorescale Coatl|Core Set 2021|221|U|{1}{G}{U}|Creature - Snake|2|2|Whenever you draw a card, you may put a +1/+1 counter on Lorescale Coatl.|
+Niambi, Esteemed Speaker|Core Set 2021|222|R|{W}{U}|Legendary Creature - Human Cleric|2|1|Flash$When Niambi, Esteemed Speaker enters the battlefield, you may return another target creature you control to its owner's hand. If you do, you gain life equal to that creature's converted mana cost.${1}{W}{U}, {T}, Discard a legendary card: Draw two cards.|
 Obsessive Stitcher|Core Set 2021|223|U|{1}{U}{B}|Creature - Human Wizard|0|3|{T}: Draw a card, then discard a card.${2}{U}{B}, {T}, Sacrifice Obsessive Stitcher: Return target creature card from your graveyard to the battlefield.|
 Watcher of the Spheres|Core Set 2021|227|U|{W}{U}|Creature - Bird Wizard|2|2|Flying$Creature spells with flying you cast cost {1} less to cast.$Whenever another creature with flying enters the battlefield under your control, Watcher of the Spheres gets +1/+1 until end of turn.|
 Chromatic Orrery|Core Set 2021|228|M|{7}|Legendary Artifact|||You may spend mana as though it were mana of any color.${T}: Add {C}{C}{C}{C}{C}.${5}, {T}: Draw a card for each color among permanents you control.|
@@ -37559,6 +37577,7 @@ Meteorite|Core Set 2021|233|U|{5}|Artifact|||When Meteorite enters the battlefie
 Solemn Simulacrum|Core Set 2021|239|R|{4}|Artifact Creature - Golem|2|2|When Solemn Simulacrum enters the battlefield, you may search your library for a basic land card, put that card onto the battlefield tapped, then shuffle your library.$When Solemn Simulacrum dies, you may draw a card.|
 Sparkhunter Masticore|Core Set 2021|240|R|{3}|Artifact Creature - Masticore|3|4|As an additional cost to cast this spell, discard a card.$Protection from planeswalkers${1}: Sparkhunter Masticore deals 1 damage to target planeswalker.${3}: Sparkhunter Masticore gains indestructible until end of turn.|
 Tormod's Crypt|Core Set 2021|241|U|{0}|Artifact|||{T}, Sacrifice Tormod's Crypt: Exile all cards from target player's graveyard.|
+Animal Sanctuary|Core Set 2021|242|R||Land|||{T}: Add {C}.${2}, {T}: Put a +1/+1 counter on target Bird, Cat, Dog, Goat, Ox, or Snake.|
 Fabled Passage|Core Set 2021|246|R||Land|||{T}, Sacrifice Fabled Passage: Search your library for a basic land card, put it onto the battlefield tapped, then shuffle your library. Then if you control four or more lands, untap that land.|
 Radiant Fountain|Core Set 2021|248|C||Land|||When Radiant Fountain enters the battlefield, you gain 2 life.${T}: Add {C}.|
 Temple of Epiphany|Core Set 2021|252|R||Land|||Temple of Epiphany enters the battlefield tapped.$When Temple of Epiphany enters the battlefield, scry 1.${T}: Add {U} or {R}.|

From 053214dcdec7cec3d275444bca4c727f131eb6e4 Mon Sep 17 00:00:00 2001
From: mikalinn777 <mikalinn777@gmail.com>
Date: Fri, 12 Jun 2020 17:53:13 -0600
Subject: [PATCH 242/586] Implement Obsessive Stitcher, Primal Might, Siege
 Striker and Waker of Waves (#6629)

Co-authored-by: Evan Kranzler <theelk801@gmail.com>
---
 .../src/mage/cards/o/ObsessiveStitcher.java   | 57 +++++++++++
 Mage.Sets/src/mage/cards/p/PrimalMight.java   | 48 +++++++++
 Mage.Sets/src/mage/cards/s/SiegeStriker.java  | 98 +++++++++++++++++++
 Mage.Sets/src/mage/cards/w/WakerofWaves.java  | 60 ++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  4 +
 5 files changed, 267 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/o/ObsessiveStitcher.java
 create mode 100644 Mage.Sets/src/mage/cards/p/PrimalMight.java
 create mode 100644 Mage.Sets/src/mage/cards/s/SiegeStriker.java
 create mode 100644 Mage.Sets/src/mage/cards/w/WakerofWaves.java

diff --git a/Mage.Sets/src/mage/cards/o/ObsessiveStitcher.java b/Mage.Sets/src/mage/cards/o/ObsessiveStitcher.java
new file mode 100644
index 0000000000..c7e800b4c6
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/o/ObsessiveStitcher.java
@@ -0,0 +1,57 @@
+
+package mage.cards.o;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.common.SacrificeSourceCost;
+import mage.abilities.costs.common.TapSourceCost;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.effects.common.DrawDiscardControllerEffect;
+import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.Zone;
+import mage.filter.StaticFilters;
+import mage.target.common.TargetCardInYourGraveyard;
+import mage.target.common.TargetAnyTarget;
+
+import java.util.UUID;
+
+/**
+ *
+ * @author mikalinn777
+ */
+public final class ObsessiveStitcher extends CardImpl {
+
+    public ObsessiveStitcher(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{B}");
+        this.subtype.add(SubType.HUMAN);
+        this.subtype.add(SubType.WIZARD);
+
+        this.power = new MageInt(0);
+        this.toughness = new MageInt(3);
+
+        // Draw a card, then discard a card.
+        this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawDiscardControllerEffect(), new TapSourceCost()));
+
+        // {2}{U}{B}, {tap}, Sacrifice Obsessive Stitcher: Return target creature card from your graveyard to the battlefield.
+        Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ReturnFromGraveyardToBattlefieldTargetEffect(), new ManaCostsImpl("{2}{U}{B}"));
+        ability.addCost(new TapSourceCost());
+        ability.addCost(new SacrificeSourceCost());
+        ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD));
+        this.addAbility(ability);
+
+    }
+
+    public ObsessiveStitcher(final ObsessiveStitcher card) {
+        super(card);
+    }
+
+    @Override
+    public ObsessiveStitcher copy() {
+        return new ObsessiveStitcher(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/cards/p/PrimalMight.java b/Mage.Sets/src/mage/cards/p/PrimalMight.java
new file mode 100644
index 0000000000..283911fd58
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/p/PrimalMight.java
@@ -0,0 +1,48 @@
+
+
+package mage.cards.p;
+
+import mage.abilities.dynamicvalue.common.ManacostVariableValue;
+import mage.abilities.effects.common.continuous.BoostTargetEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.filter.common.FilterCreaturePermanent;
+import mage.target.common.TargetCreaturePermanent;
+import mage.abilities.effects.common.FightTargetsEffect;
+import java.util.UUID;
+import mage.filter.FilterPermanent;
+import mage.target.TargetPermanent;
+import mage.target.common.TargetAnyTarget;
+
+/**
+ * @author mikalinn777
+ */
+
+
+public final class PrimalMight extends CardImpl {
+
+    private static final FilterPermanent filter = new FilterCreaturePermanent("creature you don't control");
+
+    public PrimalMight(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{X}{G}");
+        // Target creature gets +X/+X until end of turn.
+        this.getSpellAbility().addTarget(new TargetCreaturePermanent());
+        this.getSpellAbility().addEffect(new BoostTargetEffect(ManacostVariableValue.instance, ManacostVariableValue.instance, Duration.EndOfTurn));
+        // Then, it fights up to one target creature.
+        this.getSpellAbility().addEffect(new FightTargetsEffect());
+        this.getSpellAbility().addTarget(new TargetPermanent(0, 1, filter, false));
+
+    }
+
+    public PrimalMight(final PrimalMight card) {
+        super(card);
+    }
+
+    @Override
+    public PrimalMight copy() {
+        return new PrimalMight(this);
+    }
+
+}
diff --git a/Mage.Sets/src/mage/cards/s/SiegeStriker.java b/Mage.Sets/src/mage/cards/s/SiegeStriker.java
new file mode 100644
index 0000000000..efa64c906e
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/SiegeStriker.java
@@ -0,0 +1,98 @@
+package mage.cards.s;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.AttacksTriggeredAbility;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.continuous.BoostSourceEffect;
+import mage.abilities.keyword.DoubleStrikeAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.*;
+import mage.filter.common.FilterCreaturePermanent;
+import mage.filter.predicate.Predicates;
+import mage.filter.predicate.permanent.TappedPredicate;
+import mage.game.Game;
+import mage.target.common.TargetCreaturePermanent;
+
+import java.util.UUID;
+
+/**
+ *
+ * @author mikalinn777
+ */
+public final class SiegeStriker extends CardImpl {
+
+    public SiegeStriker(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}");
+
+        this.subtype.add(SubType.HUMAN);
+        this.subtype.add(SubType.SOLDIER);
+        this.power = new MageInt(1);
+        this.toughness = new MageInt(1);
+
+        // Double Strike
+        this.addAbility(DoubleStrikeAbility.getInstance());
+
+        // Whenever Siege Striker attacks, you may tap any number of untapped creatures you control. Siege Striker gets +1/+1 until end of turn for each creature tapped this way.
+        this.addAbility(new AttacksTriggeredAbility(
+                new SiegeStrikerEffect(), false
+        ));
+
+    }
+
+    public SiegeStriker(final SiegeStriker card) {
+        super(card);
+    }
+
+    @Override
+    public SiegeStriker copy() {
+        return new SiegeStriker(this);
+    }
+}
+
+class SiegeStrikerEffect extends OneShotEffect {
+
+    private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("untapped creatures you control");
+
+    static {
+        filter.add(TargetController.YOU.getControllerPredicate());
+        filter.add(Predicates.not(TappedPredicate.instance));
+    }
+
+    public SiegeStrikerEffect() {
+        super(Outcome.GainLife);
+        staticText = "you may tap any number of untapped creatures you control. "
+                + "{this} gets +1/+1 until end of turn for each creature tapped this way.";
+    }
+
+    public SiegeStrikerEffect(SiegeStrikerEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        int tappedAmount = 0;
+        TargetCreaturePermanent target = new TargetCreaturePermanent(0, Integer.MAX_VALUE, filter, true);
+        if (target.canChoose(source.getControllerId(), game)
+                && target.choose(Outcome.Tap, source.getControllerId(), source.getSourceId(), game)) {
+            for (UUID creature : target.getTargets()) {
+                if (creature != null) {
+                    game.getPermanent(creature).tap(game);
+                    tappedAmount++;
+                }
+            }
+        }
+        if (tappedAmount > 0) {
+            game.addEffect(new BoostSourceEffect(tappedAmount, tappedAmount, Duration.EndOfTurn), source);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public SiegeStrikerEffect copy() {
+        return new SiegeStrikerEffect(this);
+    }
+
+}
diff --git a/Mage.Sets/src/mage/cards/w/WakerofWaves.java b/Mage.Sets/src/mage/cards/w/WakerofWaves.java
new file mode 100644
index 0000000000..b147fb960a
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/w/WakerofWaves.java
@@ -0,0 +1,60 @@
+
+package mage.cards.w;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.costs.common.DiscardSourceCost;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.dynamicvalue.common.StaticValue;
+import mage.abilities.effects.common.LookLibraryAndPickControllerEffect;
+import mage.abilities.effects.common.continuous.BoostAllEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.*;
+import mage.filter.StaticFilters;
+import mage.filter.common.FilterCreaturePermanent;
+import mage.filter.common.FilterControlledLandPermanent;
+
+import java.util.UUID;
+
+/**
+ *
+ * @author mikalinn777
+ */
+public final class WakerofWaves extends CardImpl {
+
+    private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Creatures your opponents control");
+
+    static {
+        filter.add(TargetController.OPPONENT.getControllerPredicate());
+    }
+
+    public WakerofWaves(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{U}{U}");
+        this.subtype.add(SubType.WHALE);
+
+        this.power = new MageInt(7);
+        this.toughness = new MageInt(7);
+
+        // Creatures your opponents control get -1/-0.
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostAllEffect(-1, -0, Duration.WhileOnBattlefield, filter, false)));
+
+        // {1}{U}, Discard Waker of Waves: Look at the top two cards of your library. Put one of them into your hand and the other into your graveyard.
+        Ability ability = new SimpleActivatedAbility(Zone.HAND, new LookLibraryAndPickControllerEffect(StaticValue.get(2), false, StaticValue.get(1),
+                StaticFilters.FILTER_CARD, Zone.GRAVEYARD, false, false), new ManaCostsImpl("{1}{U}"));
+        ability.addCost(new DiscardSourceCost());
+        this.addAbility(ability);
+    }
+
+    public WakerofWaves(final WakerofWaves card) {
+        super(card);
+    }
+
+    @Override
+    public WakerofWaves copy() {
+        return new WakerofWaves(this);
+    }
+}
+
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index d2674927a1..2a51dee9e9 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -110,12 +110,14 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Mountain", 312, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Mystic Skyfish", 326, Rarity.COMMON, mage.cards.m.MysticSkyfish.class));
         cards.add(new SetCardInfo("Necromentia", 116, Rarity.RARE, mage.cards.n.Necromentia.class));
+        cards.add(new SetCardInfo("Obsessive Stitcher", 223, Rarity.UNCOMMON, mage.cards.o.ObsessiveStitcher.class));
         cards.add(new SetCardInfo("Pack Leader", 29, Rarity.RARE, mage.cards.p.PackLeader.class));
         cards.add(new SetCardInfo("Peer into the Abyss", 117, Rarity.RARE, mage.cards.p.PeerIntoTheAbyss.class));
         cards.add(new SetCardInfo("Pestilent Haze", 118, Rarity.UNCOMMON, mage.cards.p.PestilentHaze.class));
         cards.add(new SetCardInfo("Plains", 309, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Predatory Wurm", 338, Rarity.UNCOMMON, mage.cards.p.PredatoryWurm.class));
         cards.add(new SetCardInfo("Pridemalkin", 196, Rarity.COMMON, mage.cards.p.Pridemalkin.class));
+        cards.add(new SetCardInfo("Primal Might", 197, Rarity.RARE, mage.cards.p.PrimalMight.class));
         cards.add(new SetCardInfo("Pursued Whale", 60, Rarity.RARE, mage.cards.p.PursuedWhale.class));
         cards.add(new SetCardInfo("Quirion Dryad", 198, Rarity.UNCOMMON, mage.cards.q.QuirionDryad.class));
         cards.add(new SetCardInfo("Radiant Fountain", 248, Rarity.COMMON, mage.cards.r.RadiantFountain.class));
@@ -131,6 +133,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("See the Truth", 69, Rarity.RARE, mage.cards.s.SeeTheTruth.class));
         cards.add(new SetCardInfo("Selfless Savior", 36, Rarity.UNCOMMON, mage.cards.s.SelflessSavior.class));
         cards.add(new SetCardInfo("Shipwreck Dowser", 71, Rarity.UNCOMMON, mage.cards.s.ShipwreckDowser.class));
+        cards.add(new SetCardInfo("Siege Striker", 37, Rarity.UNCOMMON, mage.cards.s.SiegeStriker.class));
         cards.add(new SetCardInfo("Sigiled Contender", 323, Rarity.UNCOMMON, mage.cards.s.SigiledContender.class));
         cards.add(new SetCardInfo("Solemn Simulacrum", 239, Rarity.RARE, mage.cards.s.SolemnSimulacrum.class));
         cards.add(new SetCardInfo("Soul Sear", 160, Rarity.UNCOMMON, mage.cards.s.SoulSear.class));
@@ -162,6 +165,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Valorous Steed", 42, Rarity.COMMON, mage.cards.v.ValorousSteed.class));
         cards.add(new SetCardInfo("Village Rites", 126, Rarity.COMMON, mage.cards.v.VillageRites.class));
         cards.add(new SetCardInfo("Vito, Thorn of the Dusk Rose", 127, Rarity.RARE, mage.cards.v.VitoThornOfTheDuskRose.class));
+        cards.add(new SetCardInfo("Waker of Waves", 84, Rarity.UNCOMMON, mage.cards.w.WakerofWaves.class));
         cards.add(new SetCardInfo("Volcanic Salvo", 172, Rarity.RARE, mage.cards.v.VolcanicSalvo.class));
         cards.add(new SetCardInfo("Vryn Wingmare", 43, Rarity.UNCOMMON, mage.cards.v.VrynWingmare.class));
         cards.add(new SetCardInfo("Warden of the Woods", 213, Rarity.UNCOMMON, mage.cards.w.WardenOfTheWoods.class));

From 0f75915b82598bacd6b23bf8ae638c128b2e569d Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Fri, 12 Jun 2020 19:57:57 -0400
Subject: [PATCH 243/586] Implemented Griffin Aerie

---
 Mage.Sets/src/mage/cards/g/GriffinAerie.java  | 45 +++++++++++++++++++
 .../src/mage/cards/i/IndulgingPatrician.java  | 18 +++-----
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 3 files changed, 51 insertions(+), 13 deletions(-)
 create mode 100644 Mage.Sets/src/mage/cards/g/GriffinAerie.java

diff --git a/Mage.Sets/src/mage/cards/g/GriffinAerie.java b/Mage.Sets/src/mage/cards/g/GriffinAerie.java
new file mode 100644
index 0000000000..86d992350b
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/g/GriffinAerie.java
@@ -0,0 +1,45 @@
+package mage.cards.g;
+
+import mage.abilities.common.BeginningOfEndStepTriggeredAbility;
+import mage.abilities.condition.Condition;
+import mage.abilities.condition.common.YouGainedLifeCondition;
+import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
+import mage.abilities.effects.common.CreateTokenEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.ComparisonType;
+import mage.constants.TargetController;
+import mage.game.permanent.token.GriffinToken;
+import mage.watchers.common.PlayerGainedLifeWatcher;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class GriffinAerie extends CardImpl {
+
+    private static final Condition condition = new YouGainedLifeCondition(ComparisonType.MORE_THAN, 2);
+
+    public GriffinAerie(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}");
+
+        // At the beginning of your end step, if you gained 3 or more life this turn, create a 2/2 white Griffin creature token with flying.
+        this.addAbility(new ConditionalInterveningIfTriggeredAbility(
+                new BeginningOfEndStepTriggeredAbility(
+                        new CreateTokenEffect(new GriffinToken()), TargetController.YOU, false
+                ), condition, "At the beginning of your end step, " +
+                "if you gained 3 or more life this turn, create a 2/2 white Griffin creature token with flying."
+        ), new PlayerGainedLifeWatcher());
+    }
+
+    private GriffinAerie(final GriffinAerie card) {
+        super(card);
+    }
+
+    @Override
+    public GriffinAerie copy() {
+        return new GriffinAerie(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/cards/i/IndulgingPatrician.java b/Mage.Sets/src/mage/cards/i/IndulgingPatrician.java
index 8c969db225..91b06ca555 100644
--- a/Mage.Sets/src/mage/cards/i/IndulgingPatrician.java
+++ b/Mage.Sets/src/mage/cards/i/IndulgingPatrician.java
@@ -1,9 +1,9 @@
 package mage.cards.i;
 
 import mage.MageInt;
-import mage.abilities.Ability;
 import mage.abilities.common.BeginningOfEndStepTriggeredAbility;
 import mage.abilities.condition.Condition;
+import mage.abilities.condition.common.YouGainedLifeCondition;
 import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
 import mage.abilities.effects.common.LoseLifeOpponentsEffect;
 import mage.abilities.keyword.FlyingAbility;
@@ -11,9 +11,9 @@ import mage.abilities.keyword.LifelinkAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
+import mage.constants.ComparisonType;
 import mage.constants.SubType;
 import mage.constants.TargetController;
-import mage.game.Game;
 import mage.watchers.common.PlayerGainedLifeWatcher;
 
 import java.util.UUID;
@@ -23,6 +23,8 @@ import java.util.UUID;
  */
 public final class IndulgingPatrician extends CardImpl {
 
+    private static final Condition condition = new YouGainedLifeCondition(ComparisonType.MORE_THAN, 2);
+
     public IndulgingPatrician(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{B}");
 
@@ -41,7 +43,7 @@ public final class IndulgingPatrician extends CardImpl {
         this.addAbility(new ConditionalInterveningIfTriggeredAbility(
                 new BeginningOfEndStepTriggeredAbility(
                         new LoseLifeOpponentsEffect(3), TargetController.YOU, false
-                ), IndulgingPatricianCondition.instance, "At the beginning of your end step, " +
+                ), condition, "At the beginning of your end step, " +
                 "if you gained 3 or more life this turn, each opponent loses 3 life."
         ), new PlayerGainedLifeWatcher());
     }
@@ -55,13 +57,3 @@ public final class IndulgingPatrician extends CardImpl {
         return new IndulgingPatrician(this);
     }
 }
-
-enum IndulgingPatricianCondition implements Condition {
-    instance;
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        PlayerGainedLifeWatcher watcher = game.getState().getWatcher(PlayerGainedLifeWatcher.class);
-        return watcher != null && watcher.getLifeGained(source.getControllerId()) >= 3;
-    }
-}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 2a51dee9e9..338399a472 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -79,6 +79,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Garruk, Savage Herald", 336, Rarity.MYTHIC, mage.cards.g.GarrukSavageHerald.class));
         cards.add(new SetCardInfo("Goblin Arsonist", 147, Rarity.COMMON, mage.cards.g.GoblinArsonist.class));
         cards.add(new SetCardInfo("Grasp of Darkness", 102, Rarity.COMMON, mage.cards.g.GraspOfDarkness.class));
+        cards.add(new SetCardInfo("Griffin Aerie", 22, Rarity.UNCOMMON, mage.cards.g.GriffinAerie.class));
         cards.add(new SetCardInfo("Grim Tutor", 103, Rarity.MYTHIC, mage.cards.g.GrimTutor.class));
         cards.add(new SetCardInfo("Havoc Jester", 149, Rarity.UNCOMMON, mage.cards.h.HavocJester.class));
         cards.add(new SetCardInfo("Heartfire Immolator", 150, Rarity.UNCOMMON, mage.cards.h.HeartfireImmolator.class));

From 3887e974d80397949fdf08b18318d1e7f3a2d7c2 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Fri, 12 Jun 2020 20:00:02 -0400
Subject: [PATCH 244/586] Implemented Kaervek, the Spiteful

---
 .../src/mage/cards/k/KaervekTheSpiteful.java  | 43 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 44 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/k/KaervekTheSpiteful.java

diff --git a/Mage.Sets/src/mage/cards/k/KaervekTheSpiteful.java b/Mage.Sets/src/mage/cards/k/KaervekTheSpiteful.java
new file mode 100644
index 0000000000..493dd68b22
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/k/KaervekTheSpiteful.java
@@ -0,0 +1,43 @@
+package mage.cards.k;
+
+import mage.MageInt;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.effects.common.continuous.BoostAllEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+import mage.constants.SuperType;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class KaervekTheSpiteful extends CardImpl {
+
+    public KaervekTheSpiteful(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{B}");
+
+        this.addSuperType(SuperType.LEGENDARY);
+        this.subtype.add(SubType.HUMAN);
+        this.subtype.add(SubType.WARLOCK);
+        this.power = new MageInt(3);
+        this.toughness = new MageInt(2);
+
+        // Other creatures get -1/-1.
+        this.addAbility(new SimpleStaticAbility(new BoostAllEffect(
+                -1, -1, Duration.WhileOnBattlefield, true
+        )));
+    }
+
+    private KaervekTheSpiteful(final KaervekTheSpiteful card) {
+        super(card);
+    }
+
+    @Override
+    public KaervekTheSpiteful copy() {
+        return new KaervekTheSpiteful(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 338399a472..05eda50c23 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -92,6 +92,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Island", 310, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Jeskai Elder", 53, Rarity.UNCOMMON, mage.cards.j.JeskaiElder.class));
         cards.add(new SetCardInfo("Jolrael, Mwonvuli Recluse", 191, Rarity.RARE, mage.cards.j.JolraelMwonvuliRecluse.class));
+        cards.add(new SetCardInfo("Kaervek, the Spiteful", 106, Rarity.RARE, mage.cards.k.KaervekTheSpiteful.class));
         cards.add(new SetCardInfo("Keral Keep Disciples", 334, Rarity.UNCOMMON, mage.cards.k.KeralKeepDisciples.class));
         cards.add(new SetCardInfo("Liliana's Devotee", 109, Rarity.UNCOMMON, mage.cards.l.LilianasDevotee.class));
         cards.add(new SetCardInfo("Liliana's Scorn", 329, Rarity.RARE, mage.cards.l.LilianasScorn.class));

From 4f78452eb003655104550846a38146aa49847967 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Fri, 12 Jun 2020 20:01:25 -0400
Subject: [PATCH 245/586] Implemented Library Larcenist

---
 .../src/mage/cards/l/LibraryLarcenist.java    | 38 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 39 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/l/LibraryLarcenist.java

diff --git a/Mage.Sets/src/mage/cards/l/LibraryLarcenist.java b/Mage.Sets/src/mage/cards/l/LibraryLarcenist.java
new file mode 100644
index 0000000000..ad1c72df67
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/l/LibraryLarcenist.java
@@ -0,0 +1,38 @@
+package mage.cards.l;
+
+import mage.MageInt;
+import mage.abilities.common.AttacksTriggeredAbility;
+import mage.abilities.effects.common.DrawCardSourceControllerEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class LibraryLarcenist extends CardImpl {
+
+    public LibraryLarcenist(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}");
+
+        this.subtype.add(SubType.MERFOLK);
+        this.subtype.add(SubType.ROGUE);
+        this.power = new MageInt(1);
+        this.toughness = new MageInt(2);
+
+        // Whenever Library Larcenist attacks, draw a card.
+        this.addAbility(new AttacksTriggeredAbility(new DrawCardSourceControllerEffect(1), false));
+    }
+
+    private LibraryLarcenist(final LibraryLarcenist card) {
+        super(card);
+    }
+
+    @Override
+    public LibraryLarcenist copy() {
+        return new LibraryLarcenist(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 05eda50c23..6713a6d44c 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -94,6 +94,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Jolrael, Mwonvuli Recluse", 191, Rarity.RARE, mage.cards.j.JolraelMwonvuliRecluse.class));
         cards.add(new SetCardInfo("Kaervek, the Spiteful", 106, Rarity.RARE, mage.cards.k.KaervekTheSpiteful.class));
         cards.add(new SetCardInfo("Keral Keep Disciples", 334, Rarity.UNCOMMON, mage.cards.k.KeralKeepDisciples.class));
+        cards.add(new SetCardInfo("Library Larcenist", 55, Rarity.COMMON, mage.cards.l.LibraryLarcenist.class));
         cards.add(new SetCardInfo("Liliana's Devotee", 109, Rarity.UNCOMMON, mage.cards.l.LilianasDevotee.class));
         cards.add(new SetCardInfo("Liliana's Scorn", 329, Rarity.RARE, mage.cards.l.LilianasScorn.class));
         cards.add(new SetCardInfo("Liliana's Scrounger", 330, Rarity.UNCOMMON, mage.cards.l.LilianasScrounger.class));

From 21e8c6427bbee24d8c448a76d857846bf425e0ae Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Fri, 12 Jun 2020 20:05:44 -0400
Subject: [PATCH 246/586] Implemented Sabertooth Mauler

---
 .../src/mage/cards/s/SabertoothMauler.java    | 51 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 52 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/s/SabertoothMauler.java

diff --git a/Mage.Sets/src/mage/cards/s/SabertoothMauler.java b/Mage.Sets/src/mage/cards/s/SabertoothMauler.java
new file mode 100644
index 0000000000..4ee840e721
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/SabertoothMauler.java
@@ -0,0 +1,51 @@
+package mage.cards.s;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.BeginningOfEndStepTriggeredAbility;
+import mage.abilities.condition.common.MorbidCondition;
+import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
+import mage.abilities.effects.common.UntapSourceEffect;
+import mage.abilities.effects.common.counter.AddCountersSourceEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.TargetController;
+import mage.counters.CounterType;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class SabertoothMauler extends CardImpl {
+
+    public SabertoothMauler(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}");
+
+        this.subtype.add(SubType.CAT);
+        this.power = new MageInt(3);
+        this.toughness = new MageInt(3);
+
+        // At the beginning of your end step, if a creature died this turn, put a +1/+1 counter on Sabertooth Mauler and untap it.
+        Ability ability = new ConditionalInterveningIfTriggeredAbility(
+                new BeginningOfEndStepTriggeredAbility(
+                        new AddCountersSourceEffect(CounterType.P1P1.createInstance()
+                        ), TargetController.YOU, false
+                ), MorbidCondition.instance, "At the beginning of your end step, " +
+                "if a creature died this turn, put a +1/+1 counter on {this} and untap it."
+        );
+        ability.addEffect(new UntapSourceEffect());
+        this.addAbility(ability);
+    }
+
+    private SabertoothMauler(final SabertoothMauler card) {
+        super(card);
+    }
+
+    @Override
+    public SabertoothMauler copy() {
+        return new SabertoothMauler(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 6713a6d44c..a041023a0e 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -132,6 +132,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Riddleform", 64, Rarity.UNCOMMON, mage.cards.r.Riddleform.class));
         cards.add(new SetCardInfo("Rin and Seri, Inseparable", 278, Rarity.MYTHIC, mage.cards.r.RinAndSeriInseparable.class));
         cards.add(new SetCardInfo("Runed Halo", 32, Rarity.RARE, mage.cards.r.RunedHalo.class));
+        cards.add(new SetCardInfo("Sabertooth Mauler", 202, Rarity.COMMON, mage.cards.s.SabertoothMauler.class));
         cards.add(new SetCardInfo("Seasoned Hallowblade", 34, Rarity.UNCOMMON, mage.cards.s.SeasonedHallowblade.class));
         cards.add(new SetCardInfo("See the Truth", 69, Rarity.RARE, mage.cards.s.SeeTheTruth.class));
         cards.add(new SetCardInfo("Selfless Savior", 36, Rarity.UNCOMMON, mage.cards.s.SelflessSavior.class));

From 3a7e60de6380db142ec188b9f4acce9c3dd0f01e Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Fri, 12 Jun 2020 20:20:00 -0400
Subject: [PATCH 247/586] Implemented Silversmote Ghoul

---
 Mage.Sets/src/mage/cards/g/GriffinAerie.java  |  5 +-
 .../src/mage/cards/i/IndulgingPatrician.java  |  5 +-
 Mage.Sets/src/mage/cards/l/LoneRider.java     | 28 ++++----
 .../src/mage/cards/s/SilversmoteGhoul.java    | 64 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 5 files changed, 90 insertions(+), 13 deletions(-)
 create mode 100644 Mage.Sets/src/mage/cards/s/SilversmoteGhoul.java

diff --git a/Mage.Sets/src/mage/cards/g/GriffinAerie.java b/Mage.Sets/src/mage/cards/g/GriffinAerie.java
index 86d992350b..a3e4a3297c 100644
--- a/Mage.Sets/src/mage/cards/g/GriffinAerie.java
+++ b/Mage.Sets/src/mage/cards/g/GriffinAerie.java
@@ -5,6 +5,8 @@ import mage.abilities.condition.Condition;
 import mage.abilities.condition.common.YouGainedLifeCondition;
 import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
+import mage.abilities.hint.ConditionHint;
+import mage.abilities.hint.Hint;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
@@ -21,6 +23,7 @@ import java.util.UUID;
 public final class GriffinAerie extends CardImpl {
 
     private static final Condition condition = new YouGainedLifeCondition(ComparisonType.MORE_THAN, 2);
+    private static final Hint hint = new ConditionHint(condition, "You gained 3 or more life this turn");
 
     public GriffinAerie(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}");
@@ -31,7 +34,7 @@ public final class GriffinAerie extends CardImpl {
                         new CreateTokenEffect(new GriffinToken()), TargetController.YOU, false
                 ), condition, "At the beginning of your end step, " +
                 "if you gained 3 or more life this turn, create a 2/2 white Griffin creature token with flying."
-        ), new PlayerGainedLifeWatcher());
+        ).addHint(hint), new PlayerGainedLifeWatcher());
     }
 
     private GriffinAerie(final GriffinAerie card) {
diff --git a/Mage.Sets/src/mage/cards/i/IndulgingPatrician.java b/Mage.Sets/src/mage/cards/i/IndulgingPatrician.java
index 91b06ca555..4ddb3b7ad8 100644
--- a/Mage.Sets/src/mage/cards/i/IndulgingPatrician.java
+++ b/Mage.Sets/src/mage/cards/i/IndulgingPatrician.java
@@ -6,6 +6,8 @@ import mage.abilities.condition.Condition;
 import mage.abilities.condition.common.YouGainedLifeCondition;
 import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
 import mage.abilities.effects.common.LoseLifeOpponentsEffect;
+import mage.abilities.hint.ConditionHint;
+import mage.abilities.hint.Hint;
 import mage.abilities.keyword.FlyingAbility;
 import mage.abilities.keyword.LifelinkAbility;
 import mage.cards.CardImpl;
@@ -24,6 +26,7 @@ import java.util.UUID;
 public final class IndulgingPatrician extends CardImpl {
 
     private static final Condition condition = new YouGainedLifeCondition(ComparisonType.MORE_THAN, 2);
+    private static final Hint hint = new ConditionHint(condition, "You gained 3 or more life this turn");
 
     public IndulgingPatrician(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{B}");
@@ -45,7 +48,7 @@ public final class IndulgingPatrician extends CardImpl {
                         new LoseLifeOpponentsEffect(3), TargetController.YOU, false
                 ), condition, "At the beginning of your end step, " +
                 "if you gained 3 or more life this turn, each opponent loses 3 life."
-        ), new PlayerGainedLifeWatcher());
+        ).addHint(hint), new PlayerGainedLifeWatcher());
     }
 
     private IndulgingPatrician(final IndulgingPatrician card) {
diff --git a/Mage.Sets/src/mage/cards/l/LoneRider.java b/Mage.Sets/src/mage/cards/l/LoneRider.java
index bfc9584854..db9fa6f0b0 100644
--- a/Mage.Sets/src/mage/cards/l/LoneRider.java
+++ b/Mage.Sets/src/mage/cards/l/LoneRider.java
@@ -1,31 +1,34 @@
-
 package mage.cards.l;
 
-import java.util.UUID;
-
 import mage.MageInt;
-import mage.abilities.TriggeredAbility;
-import mage.abilities.common.OnEventTriggeredAbility;
+import mage.abilities.common.BeginningOfEndStepTriggeredAbility;
+import mage.abilities.condition.Condition;
 import mage.abilities.condition.common.YouGainedLifeCondition;
 import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
 import mage.abilities.effects.common.TransformSourceEffect;
+import mage.abilities.hint.ConditionHint;
+import mage.abilities.hint.Hint;
 import mage.abilities.keyword.FirstStrikeAbility;
 import mage.abilities.keyword.LifelinkAbility;
 import mage.abilities.keyword.TransformAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.SubType;
 import mage.constants.ComparisonType;
-import mage.game.events.GameEvent;
+import mage.constants.SubType;
+import mage.constants.TargetController;
 import mage.watchers.common.PlayerGainedLifeWatcher;
 
+import java.util.UUID;
+
 /**
  * @author fireshoes
  */
 public final class LoneRider extends CardImpl {
 
     private static final String ruleText = "At the beginning of the end step, if you gained 3 or more life this turn, transform {this}";
+    private static final Condition condition = new YouGainedLifeCondition(ComparisonType.MORE_THAN, 2);
+    private static final Hint hint = new ConditionHint(condition, "You gained 3 or more life this turn");
 
     public LoneRider(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}");
@@ -45,11 +48,14 @@ public final class LoneRider extends CardImpl {
 
         // At the beginning of the end step, if you gained 3 or more life this turn, transform Lone Rider.
         this.addAbility(new TransformAbility());
-        TriggeredAbility triggered = new OnEventTriggeredAbility(GameEvent.EventType.END_TURN_STEP_PRE, "beginning of the end step", true, new TransformSourceEffect(true));
-        this.addAbility(new ConditionalInterveningIfTriggeredAbility(triggered, new YouGainedLifeCondition(ComparisonType.MORE_THAN, 2), ruleText), new PlayerGainedLifeWatcher());
+        this.addAbility(new ConditionalInterveningIfTriggeredAbility(
+                new BeginningOfEndStepTriggeredAbility(
+                        new TransformSourceEffect(true), TargetController.NEXT, false
+                ), condition, ruleText
+        ), new PlayerGainedLifeWatcher());
     }
 
-    public LoneRider(final LoneRider card) {
+    private LoneRider(final LoneRider card) {
         super(card);
     }
 
@@ -57,4 +63,4 @@ public final class LoneRider extends CardImpl {
     public LoneRider copy() {
         return new LoneRider(this);
     }
-}
\ No newline at end of file
+}
diff --git a/Mage.Sets/src/mage/cards/s/SilversmoteGhoul.java b/Mage.Sets/src/mage/cards/s/SilversmoteGhoul.java
new file mode 100644
index 0000000000..85d5dd0288
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/SilversmoteGhoul.java
@@ -0,0 +1,64 @@
+package mage.cards.s;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.BeginningOfEndStepTriggeredAbility;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.condition.Condition;
+import mage.abilities.condition.common.YouGainedLifeCondition;
+import mage.abilities.costs.common.SacrificeSourceCost;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
+import mage.abilities.effects.common.DrawCardSourceControllerEffect;
+import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect;
+import mage.abilities.hint.ConditionHint;
+import mage.abilities.hint.Hint;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.*;
+import mage.watchers.common.PlayerGainedLifeWatcher;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class SilversmoteGhoul extends CardImpl {
+
+    private static final Condition condition = new YouGainedLifeCondition(ComparisonType.MORE_THAN, 2);
+    private static final Hint hint = new ConditionHint(condition, "You gained 3 or more life this turn");
+
+    public SilversmoteGhoul(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}");
+
+        this.subtype.add(SubType.ZOMBIE);
+        this.subtype.add(SubType.VAMPIRE);
+        this.power = new MageInt(3);
+        this.toughness = new MageInt(1);
+
+        // At the beginning of your end step, if you gained 3 or more life this turn, return Silversmote Ghoul from your graveyard to the battlefield tapped.
+        this.addAbility(new ConditionalInterveningIfTriggeredAbility(
+                new BeginningOfEndStepTriggeredAbility(
+                        Zone.GRAVEYARD, new ReturnSourceFromGraveyardToBattlefieldEffect(true),
+                        TargetController.YOU, null, false
+                ), condition, "At the beginning of your end step, " +
+                "if you gained 3 or more life this turn, return {this} from your graveyard to the battlefield tapped."
+        ).addHint(hint), new PlayerGainedLifeWatcher());
+
+        // {1}{B}, Sacrifice Silversmote Ghoul: Draw a card.
+        Ability ability = new SimpleActivatedAbility(
+                new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{1}{B}")
+        );
+        ability.addCost(new SacrificeSourceCost());
+        this.addAbility(ability);
+    }
+
+    private SilversmoteGhoul(final SilversmoteGhoul card) {
+        super(card);
+    }
+
+    @Override
+    public SilversmoteGhoul copy() {
+        return new SilversmoteGhoul(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index a041023a0e..56deb3f700 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -139,6 +139,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Shipwreck Dowser", 71, Rarity.UNCOMMON, mage.cards.s.ShipwreckDowser.class));
         cards.add(new SetCardInfo("Siege Striker", 37, Rarity.UNCOMMON, mage.cards.s.SiegeStriker.class));
         cards.add(new SetCardInfo("Sigiled Contender", 323, Rarity.UNCOMMON, mage.cards.s.SigiledContender.class));
+        cards.add(new SetCardInfo("Silversmote Ghoul", 122, Rarity.UNCOMMON, mage.cards.s.SilversmoteGhoul.class));
         cards.add(new SetCardInfo("Solemn Simulacrum", 239, Rarity.RARE, mage.cards.s.SolemnSimulacrum.class));
         cards.add(new SetCardInfo("Soul Sear", 160, Rarity.UNCOMMON, mage.cards.s.SoulSear.class));
         cards.add(new SetCardInfo("Sparkhunter Masticore", 240, Rarity.RARE, mage.cards.s.SparkhunterMasticore.class));

From 50fe700ee11115d1279c37abea07a2e7c6b5540f Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Fri, 12 Jun 2020 20:22:40 -0400
Subject: [PATCH 248/586] Implemented Sporeweb Weaver

---
 .../src/mage/cards/s/SporewebWeaver.java      | 50 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 51 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/s/SporewebWeaver.java

diff --git a/Mage.Sets/src/mage/cards/s/SporewebWeaver.java b/Mage.Sets/src/mage/cards/s/SporewebWeaver.java
new file mode 100644
index 0000000000..77aeed621e
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/SporewebWeaver.java
@@ -0,0 +1,50 @@
+package mage.cards.s;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.DealtDamageToSourceTriggeredAbility;
+import mage.abilities.effects.common.CreateTokenEffect;
+import mage.abilities.effects.common.GainLifeEffect;
+import mage.abilities.keyword.HexproofFromBlueAbility;
+import mage.abilities.keyword.ReachAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.game.permanent.token.SaprolingToken;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class SporewebWeaver extends CardImpl {
+
+    public SporewebWeaver(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}");
+
+        this.subtype.add(SubType.SPIDER);
+        this.power = new MageInt(1);
+        this.toughness = new MageInt(4);
+
+        // Reach
+        this.addAbility(ReachAbility.getInstance());
+
+        // Hexproof from blue
+        this.addAbility(HexproofFromBlueAbility.getInstance());
+
+        // Whenever Sporeweb Weaver is dealt damage, you gain 1 life and create a 1/1 green Saproling creature token.
+        Ability ability = new DealtDamageToSourceTriggeredAbility(new GainLifeEffect(1), false);
+        ability.addEffect(new CreateTokenEffect(new SaprolingToken()).concatBy("and"));
+        this.addAbility(ability);
+    }
+
+    private SporewebWeaver(final SporewebWeaver card) {
+        super(card);
+    }
+
+    @Override
+    public SporewebWeaver copy() {
+        return new SporewebWeaver(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 56deb3f700..42070dedc5 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -145,6 +145,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Sparkhunter Masticore", 240, Rarity.RARE, mage.cards.s.SparkhunterMasticore.class));
         cards.add(new SetCardInfo("Spined Megalodon", 72, Rarity.COMMON, mage.cards.s.SpinedMegalodon.class));
         cards.add(new SetCardInfo("Spirit of Malevolence", 331, Rarity.COMMON, mage.cards.s.SpiritOfMalevolence.class));
+        cards.add(new SetCardInfo("Sporeweb Weaver", 208, Rarity.RARE, mage.cards.s.SporewebWeaver.class));
         cards.add(new SetCardInfo("Storm Caller", 335, Rarity.COMMON, mage.cards.s.StormCaller.class));
         cards.add(new SetCardInfo("Stormwing Entity", 73, Rarity.RARE, mage.cards.s.StormwingEntity.class));
         cards.add(new SetCardInfo("Swamp", 311, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS));

From 5726c6fa0d52626a73a68b31c6530d252d4dd524 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Fri, 12 Jun 2020 20:24:55 -0400
Subject: [PATCH 249/586] Implemented Finishing Blow

---
 Mage.Sets/src/mage/cards/f/FinishingBlow.java | 32 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 33 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/f/FinishingBlow.java

diff --git a/Mage.Sets/src/mage/cards/f/FinishingBlow.java b/Mage.Sets/src/mage/cards/f/FinishingBlow.java
new file mode 100644
index 0000000000..8376ef033d
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/f/FinishingBlow.java
@@ -0,0 +1,32 @@
+package mage.cards.f;
+
+import mage.abilities.effects.common.DestroyTargetEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.target.common.TargetCreatureOrPlaneswalker;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class FinishingBlow extends CardImpl {
+
+    public FinishingBlow(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{B}");
+
+        // Destroy target creature or planeswalker.
+        this.getSpellAbility().addEffect(new DestroyTargetEffect());
+        this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker());
+    }
+
+    private FinishingBlow(final FinishingBlow card) {
+        super(card);
+    }
+
+    @Override
+    public FinishingBlow copy() {
+        return new FinishingBlow(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 42070dedc5..b5ab27d40c 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -71,6 +71,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Falconer Adept", 18, Rarity.UNCOMMON, mage.cards.f.FalconerAdept.class));
         cards.add(new SetCardInfo("Fierce Empath", 181, Rarity.UNCOMMON, mage.cards.f.FierceEmpath.class));
         cards.add(new SetCardInfo("Fiery Emancipation", 143, Rarity.MYTHIC, mage.cards.f.FieryEmancipation.class));
+        cards.add(new SetCardInfo("Finishing Blow", 99, Rarity.COMMON, mage.cards.f.FinishingBlow.class));
         cards.add(new SetCardInfo("Fungal Rebirth", 182, Rarity.UNCOMMON, mage.cards.f.FungalRebirth.class));
         cards.add(new SetCardInfo("Furious Rise", 144, Rarity.UNCOMMON, mage.cards.f.FuriousRise.class));
         cards.add(new SetCardInfo("Gadrak, the Crown-Scourge", 146, Rarity.RARE, mage.cards.g.GadrakTheCrownScourge.class));

From a26e56f896a8af6d5bf1e8cd52ca8789a8d15efe Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Fri, 12 Jun 2020 20:30:58 -0400
Subject: [PATCH 250/586] Implemented Animal Sanctuary

---
 .../src/mage/cards/a/AnimalSanctuary.java     | 61 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 62 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/a/AnimalSanctuary.java

diff --git a/Mage.Sets/src/mage/cards/a/AnimalSanctuary.java b/Mage.Sets/src/mage/cards/a/AnimalSanctuary.java
new file mode 100644
index 0000000000..b87c98b14c
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/a/AnimalSanctuary.java
@@ -0,0 +1,61 @@
+package mage.cards.a;
+
+import mage.abilities.Ability;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.common.TapSourceCost;
+import mage.abilities.costs.mana.GenericManaCost;
+import mage.abilities.effects.common.counter.AddCountersTargetEffect;
+import mage.abilities.mana.ColorlessManaAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.counters.CounterType;
+import mage.filter.FilterPermanent;
+import mage.filter.predicate.Predicates;
+import mage.target.TargetPermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class AnimalSanctuary extends CardImpl {
+
+    private static final FilterPermanent filter = new FilterPermanent("Bird, Cat, Dog, Goat, Ox, or Snake");
+
+    static {
+        filter.add(Predicates.or(
+                SubType.BIRD.getPredicate(),
+                SubType.CAT.getPredicate(),
+                SubType.DOG.getPredicate(),
+                SubType.GOAT.getPredicate(),
+                SubType.OX.getPredicate(),
+                SubType.SNAKE.getPredicate()
+        ));
+    }
+
+    public AnimalSanctuary(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.LAND}, "");
+
+        // {T}: Add {C}.
+        this.addAbility(new ColorlessManaAbility());
+
+        // {2}, {T}: Put a +1/+1 counter on target Bird, Cat, Dog, Goat, Ox, or Snake.
+        Ability ability = new SimpleActivatedAbility(
+                new AddCountersTargetEffect(CounterType.P1P1.createInstance()), new GenericManaCost(2)
+        );
+        ability.addCost(new TapSourceCost());
+        ability.addTarget(new TargetPermanent(filter));
+        this.addAbility(ability);
+    }
+
+    private AnimalSanctuary(final AnimalSanctuary card) {
+        super(card);
+    }
+
+    @Override
+    public AnimalSanctuary copy() {
+        return new AnimalSanctuary(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index b5ab27d40c..6110afe18f 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -37,6 +37,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Alpine Houndmaster", 215, Rarity.UNCOMMON, mage.cards.a.AlpineHoundmaster.class));
         cards.add(new SetCardInfo("Alpine Watchdog", 2, Rarity.COMMON, mage.cards.a.AlpineWatchdog.class));
         cards.add(new SetCardInfo("Angelic Ascension", 3, Rarity.UNCOMMON, mage.cards.a.AngelicAscension.class));
+        cards.add(new SetCardInfo("Animal Sanctuary", 242, Rarity.RARE, mage.cards.a.AnimalSanctuary.class));
         cards.add(new SetCardInfo("Azusa, Lost but Seeking", 173, Rarity.RARE, mage.cards.a.AzusaLostButSeeking.class));
         cards.add(new SetCardInfo("Bad Deal", 89, Rarity.UNCOMMON, mage.cards.b.BadDeal.class));
         cards.add(new SetCardInfo("Baneslayer Angel", 6, Rarity.MYTHIC, mage.cards.b.BaneslayerAngel.class));

From 56c3435d4c512aa3bb5a01413b73862269c70308 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Fri, 12 Jun 2020 20:33:30 -0400
Subject: [PATCH 251/586] fixed Igneous Cur activation cost

---
 Mage.Sets/src/mage/cards/i/IgneousCur.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Mage.Sets/src/mage/cards/i/IgneousCur.java b/Mage.Sets/src/mage/cards/i/IgneousCur.java
index 9026e996ce..941413c9c4 100644
--- a/Mage.Sets/src/mage/cards/i/IgneousCur.java
+++ b/Mage.Sets/src/mage/cards/i/IgneousCur.java
@@ -27,7 +27,7 @@ public final class IgneousCur extends CardImpl {
 
         // {1}{R}: Igneous Cur gets +2/+0 until end of turn.
         this.addAbility(new SimpleActivatedAbility(
-                new BoostSourceEffect(2, 0, Duration.EndOfTurn), new ManaCostsImpl("{2}{R}")
+                new BoostSourceEffect(2, 0, Duration.EndOfTurn), new ManaCostsImpl("{1}{R}")
         ));
     }
 

From 476cf92ce61021e4b632521af1053fb29996ef14 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sat, 13 Jun 2020 19:13:28 -0400
Subject: [PATCH 252/586] updated M21 spoiler and reprints

---
 .../cards/w/{WakerofWaves.java => WakerOfWaves.java}  | 11 +++++------
 Mage.Sets/src/mage/sets/CoreSet2021.java              |  4 +++-
 Utils/mtg-cards-data.txt                              |  3 +++
 3 files changed, 11 insertions(+), 7 deletions(-)
 rename Mage.Sets/src/mage/cards/w/{WakerofWaves.java => WakerOfWaves.java} (86%)

diff --git a/Mage.Sets/src/mage/cards/w/WakerofWaves.java b/Mage.Sets/src/mage/cards/w/WakerOfWaves.java
similarity index 86%
rename from Mage.Sets/src/mage/cards/w/WakerofWaves.java
rename to Mage.Sets/src/mage/cards/w/WakerOfWaves.java
index b147fb960a..b402d3d56b 100644
--- a/Mage.Sets/src/mage/cards/w/WakerofWaves.java
+++ b/Mage.Sets/src/mage/cards/w/WakerOfWaves.java
@@ -15,7 +15,6 @@ import mage.cards.CardSetInfo;
 import mage.constants.*;
 import mage.filter.StaticFilters;
 import mage.filter.common.FilterCreaturePermanent;
-import mage.filter.common.FilterControlledLandPermanent;
 
 import java.util.UUID;
 
@@ -23,7 +22,7 @@ import java.util.UUID;
  *
  * @author mikalinn777
  */
-public final class WakerofWaves extends CardImpl {
+public final class WakerOfWaves extends CardImpl {
 
     private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Creatures your opponents control");
 
@@ -31,7 +30,7 @@ public final class WakerofWaves extends CardImpl {
         filter.add(TargetController.OPPONENT.getControllerPredicate());
     }
 
-    public WakerofWaves(UUID ownerId, CardSetInfo setInfo) {
+    public WakerOfWaves(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{U}{U}");
         this.subtype.add(SubType.WHALE);
 
@@ -48,13 +47,13 @@ public final class WakerofWaves extends CardImpl {
         this.addAbility(ability);
     }
 
-    public WakerofWaves(final WakerofWaves card) {
+    public WakerOfWaves(final WakerOfWaves card) {
         super(card);
     }
 
     @Override
-    public WakerofWaves copy() {
-        return new WakerofWaves(this);
+    public WakerOfWaves copy() {
+        return new WakerOfWaves(this);
     }
 }
 
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 6110afe18f..3d5a2c80a4 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -117,6 +117,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Necromentia", 116, Rarity.RARE, mage.cards.n.Necromentia.class));
         cards.add(new SetCardInfo("Obsessive Stitcher", 223, Rarity.UNCOMMON, mage.cards.o.ObsessiveStitcher.class));
         cards.add(new SetCardInfo("Pack Leader", 29, Rarity.RARE, mage.cards.p.PackLeader.class));
+        cards.add(new SetCardInfo("Palladium Myr", 234, Rarity.UNCOMMON, mage.cards.p.PalladiumMyr.class));
         cards.add(new SetCardInfo("Peer into the Abyss", 117, Rarity.RARE, mage.cards.p.PeerIntoTheAbyss.class));
         cards.add(new SetCardInfo("Pestilent Haze", 118, Rarity.UNCOMMON, mage.cards.p.PestilentHaze.class));
         cards.add(new SetCardInfo("Plains", 309, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
@@ -135,6 +136,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Rin and Seri, Inseparable", 278, Rarity.MYTHIC, mage.cards.r.RinAndSeriInseparable.class));
         cards.add(new SetCardInfo("Runed Halo", 32, Rarity.RARE, mage.cards.r.RunedHalo.class));
         cards.add(new SetCardInfo("Sabertooth Mauler", 202, Rarity.COMMON, mage.cards.s.SabertoothMauler.class));
+        cards.add(new SetCardInfo("Scavenging Ooze", 204, Rarity.RARE, mage.cards.s.ScavengingOoze.class));
         cards.add(new SetCardInfo("Seasoned Hallowblade", 34, Rarity.UNCOMMON, mage.cards.s.SeasonedHallowblade.class));
         cards.add(new SetCardInfo("See the Truth", 69, Rarity.RARE, mage.cards.s.SeeTheTruth.class));
         cards.add(new SetCardInfo("Selfless Savior", 36, Rarity.UNCOMMON, mage.cards.s.SelflessSavior.class));
@@ -173,7 +175,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Valorous Steed", 42, Rarity.COMMON, mage.cards.v.ValorousSteed.class));
         cards.add(new SetCardInfo("Village Rites", 126, Rarity.COMMON, mage.cards.v.VillageRites.class));
         cards.add(new SetCardInfo("Vito, Thorn of the Dusk Rose", 127, Rarity.RARE, mage.cards.v.VitoThornOfTheDuskRose.class));
-        cards.add(new SetCardInfo("Waker of Waves", 84, Rarity.UNCOMMON, mage.cards.w.WakerofWaves.class));
+        cards.add(new SetCardInfo("Waker of Waves", 84, Rarity.UNCOMMON, mage.cards.w.WakerOfWaves.class));
         cards.add(new SetCardInfo("Volcanic Salvo", 172, Rarity.RARE, mage.cards.v.VolcanicSalvo.class));
         cards.add(new SetCardInfo("Vryn Wingmare", 43, Rarity.UNCOMMON, mage.cards.v.VrynWingmare.class));
         cards.add(new SetCardInfo("Warden of the Woods", 213, Rarity.UNCOMMON, mage.cards.w.WardenOfTheWoods.class));
diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt
index bb51cb03d6..04a90594e8 100644
--- a/Utils/mtg-cards-data.txt
+++ b/Utils/mtg-cards-data.txt
@@ -37498,6 +37498,7 @@ Finishing Blow|Core Set 2021|99|C|{4}{B}|Instant|||Destroy target creature or pl
 Goremand|Core Set 2021|101|U|{4}{B}{B}|Creature - Demon|5|5|As an additional cost to cast this spell, sacrifice a creature.$Flying$Trample$When Goremand enters the battlefield, each opponent sacrifices a creature.|
 Grasp of Darkness|Core Set 2021|102|C|{B}{B}|Instant|||Target creature gets -4/-4 until end of turn.|
 Grim Tutor|Core Set 2021|103|M|{1}{B}{B}|Sorcery|||Search your library for a card and put that card into your hand, then shuffle your library. You lose 3 life.|
+Hooded Blightfang|Core Set 2021|104|R|{2}{B}|Creature - Snake|1|4|Deathtouch$Whenever a creature you control with deathtouch attacks, each opponent loses 1 life and you gain 1 life.$Whenever a creature you control with deathtouch deals damage to a planeswalker, destroy that planeswalker.|
 Kaervek, the Spiteful|Core Set 2021|106|R|{2}{B}{B}|Legendary Creature - Human Warlock|3|2|Other creatures get -1/-1.|
 Liliana, Waker of the Dead|Core Set 2021|108|M|{2}{B}{B}|Legendary Planeswalker - Liliana|4|+1: Each player discards a card. Each opponent who can't loses 3 life.$−3: Target creature gets -X/-X until end of turn, where X is the number of cards in your graveyard.$−7: You get an emblem with "At the beginning of combat on your turn, put target creature card from a graveyard onto the battlefield under your control. It gains haste."|
 Liliana's Devotee|Core Set 2021|109|U|{2}{B}|Creature - Human Warlock|2|3|Zombies you control get +1/+0.$At the beginning of your end step, if a creature died this turn, you may pay {1}{B}. If you do, create a 2/2 black Zombie creature token.|
@@ -37558,6 +37559,7 @@ Primal Might|Core Set 2021|197|R|{X}{G}|Sorcery|||Target creature you control ge
 Quirion Dryad|Core Set 2021|198|U|{1}{G}|Creature - Dryad|1|1|Whenever you cast a white, blue, black, or red spell, put a +1/+1 counter on Quirion Dryad.|
 Ranger's Guile|Core Set 2021|199|C|{G}|Instant|||Target creature you control gets +1/+1 and gains hexproof until end of turn.|
 Sabertooth Mauler|Core Set 2021|202|C|{3}{G}|Creature - Cat|3|3|At the beginning of your end step, if a creature died this turn, put a +1/+1 counter on Sabertooth Mauler and untap it.|
+Scavenging Ooze|Core Set 2021|204|R|{1}{G}|Creature - Ooze|2|2|{G}: Exile target card from a graveyard. If it was a creature card, put a +1/+1 counter on Scavenging Ooze and you gain 1 life.|
 Sporeweb Weaver|Core Set 2021|208|R|{2}{G}|Creature - Spider|1|4|Reach, hexproof from blue$Whenever Sporeweb Weaver is dealt damage, you gain 1 life and create a 1/1 green Saproling creature token.|
 Trufflesnout|Core Set 2021|212|C|{2}{G}|Creature - Boar|2|2|When Trufflesnout enters the battlefield, choose one —$• Put a +1/+1 counter on Trufflesnout.$• You gain 4 life.|
 Warden of the Woods|Core Set 2021|213|U|{4}{G}{G}|Creature - Treefolk|5|7|Vigilance$Whenever Warden of the Woods becomes the target of a spell or ability an opponent controls, you may draw two cards.|
@@ -37574,6 +37576,7 @@ Watcher of the Spheres|Core Set 2021|227|U|{W}{U}|Creature - Bird Wizard|2|2|Fly
 Chromatic Orrery|Core Set 2021|228|M|{7}|Legendary Artifact|||You may spend mana as though it were mana of any color.${T}: Add {C}{C}{C}{C}{C}.${5}, {T}: Draw a card for each color among permanents you control.|
 Mazemind Tome|Core Set 2021|232|R|{2}|Artifact|||{T}, Put a page counter on Mazemind Tome: Scry 1.${2}, {T}, Put a page counter on Mazemind Tome: Draw a card.$When there are four or more page counters on Mazemind Tome, exile it. If you do, you gain 4 life.|
 Meteorite|Core Set 2021|233|U|{5}|Artifact|||When Meteorite enters the battlefield, it deals 2 damage to any target.${T}: Add one mana of any color.|
+Palladium Myr|Core Set 2021|234|U|{3}|Artifact Creature - Myr|2|2|{T}: Add {C}{C}.|
 Solemn Simulacrum|Core Set 2021|239|R|{4}|Artifact Creature - Golem|2|2|When Solemn Simulacrum enters the battlefield, you may search your library for a basic land card, put that card onto the battlefield tapped, then shuffle your library.$When Solemn Simulacrum dies, you may draw a card.|
 Sparkhunter Masticore|Core Set 2021|240|R|{3}|Artifact Creature - Masticore|3|4|As an additional cost to cast this spell, discard a card.$Protection from planeswalkers${1}: Sparkhunter Masticore deals 1 damage to target planeswalker.${3}: Sparkhunter Masticore gains indestructible until end of turn.|
 Tormod's Crypt|Core Set 2021|241|U|{0}|Artifact|||{T}, Sacrifice Tormod's Crypt: Exile all cards from target player's graveyard.|

From ad4be5a9fe4c7e653fbd49c554ce511613d882d2 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Sun, 14 Jun 2020 04:28:58 +0400
Subject: [PATCH 253/586] Fixed effect texts

---
 .../common/DontUntapInControllersUntapStepTargetEffect.java   | 2 +-
 .../mage/abilities/effects/common/FightTargetsEffect.java     | 2 +-
 .../abilities/effects/common/GetEmblemTargetPlayerEffect.java | 2 +-
 .../effects/common/combat/AttacksIfAbleTargetEffect.java      | 4 ++--
 .../effects/common/combat/BlocksIfAbleTargetEffect.java       | 4 ++--
 .../CanAttackAsThoughItDidntHaveDefenderTargetEffect.java     | 4 ++--
 .../common/continuous/LoseAllCreatureTypesTargetEffect.java   | 2 +-
 .../effects/common/counter/AddPoisonCounterTargetEffect.java  | 2 +-
 8 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersUntapStepTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersUntapStepTargetEffect.java
index dbc5aa6ad1..ea1aaf6a44 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersUntapStepTargetEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersUntapStepTargetEffect.java
@@ -71,7 +71,7 @@ public class DontUntapInControllersUntapStepTargetEffect extends ContinuousRuleM
         if (staticText != null) {
             return staticText;
         }
-        return "Target " + mode.getTargets().get(0).getTargetName()
+        return "target " + mode.getTargets().get(0).getTargetName()
                 + " doesn't untap during its controller's untap step" + (getDuration().toString().isEmpty() ? "" : " " + getDuration());
     }
 
diff --git a/Mage/src/main/java/mage/abilities/effects/common/FightTargetsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/FightTargetsEffect.java
index 22fdb166fe..94fa24457f 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/FightTargetsEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/FightTargetsEffect.java
@@ -77,7 +77,7 @@ public class FightTargetsEffect extends OneShotEffect {
             return staticText;
 
         }
-        return "Target " + mode
+        return "target " + mode
                 .getTargets().get(0).getTargetName() + " fights another target " + mode
                 .getTargets().get(1).getTargetName();
     }
diff --git a/Mage/src/main/java/mage/abilities/effects/common/GetEmblemTargetPlayerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/GetEmblemTargetPlayerEffect.java
index a99b93a6f3..eb9aa7b52e 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/GetEmblemTargetPlayerEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/GetEmblemTargetPlayerEffect.java
@@ -53,6 +53,6 @@ public class GetEmblemTargetPlayerEffect extends OneShotEffect {
         if (staticText != null && !staticText.isEmpty()) {
             return staticText;
         }
-        return "Target " + mode.getTargets().get(0).getTargetName() + " gets an emblem with \"" + emblem.getAbilities().getRules(null).stream().collect(Collectors.joining("; ")) + "\"";
+        return "target " + mode.getTargets().get(0).getTargetName() + " gets an emblem with \"" + emblem.getAbilities().getRules(null).stream().collect(Collectors.joining("; ")) + "\"";
     }
 }
diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/AttacksIfAbleTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/AttacksIfAbleTargetEffect.java
index 2a30cb35f3..c545cd2488 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/combat/AttacksIfAbleTargetEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/combat/AttacksIfAbleTargetEffect.java
@@ -48,9 +48,9 @@ public class AttacksIfAbleTargetEffect extends RequirementEffect {
             return staticText;
         }
         if (this.duration == Duration.EndOfTurn) {
-            return "Target " + mode.getTargets().get(0).getTargetName() + " attacks this turn if able";
+            return "target " + mode.getTargets().get(0).getTargetName() + " attacks this turn if able";
         } else {
-            return "Target " + mode.getTargets().get(0).getTargetName() + " attacks each turn if able";
+            return "target " + mode.getTargets().get(0).getTargetName() + " attacks each turn if able";
         }
     }
 
diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/BlocksIfAbleTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/BlocksIfAbleTargetEffect.java
index 85963adf2f..bdf7f4b016 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/combat/BlocksIfAbleTargetEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/combat/BlocksIfAbleTargetEffect.java
@@ -54,10 +54,10 @@ public class BlocksIfAbleTargetEffect extends RequirementEffect {
             return staticText;
         }
         if (this.duration == Duration.EndOfTurn) {
-            return "Target " + mode.getTargets().get(0).getTargetName() + " blocks this turn if able";
+            return "target " + mode.getTargets().get(0).getTargetName() + " blocks this turn if able";
         }
         else {
-            return "Target " + mode.getTargets().get(0).getTargetName() + " blocks each turn if able";
+            return "target " + mode.getTargets().get(0).getTargetName() + " blocks each turn if able";
         }
     }
 
diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/CanAttackAsThoughItDidntHaveDefenderTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/CanAttackAsThoughItDidntHaveDefenderTargetEffect.java
index f21719649a..8c2e07016e 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/combat/CanAttackAsThoughItDidntHaveDefenderTargetEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/combat/CanAttackAsThoughItDidntHaveDefenderTargetEffect.java
@@ -47,9 +47,9 @@ public class CanAttackAsThoughItDidntHaveDefenderTargetEffect extends AsThoughEf
         }
         if (!mode.getTargets().isEmpty()) {
             if (this.duration == Duration.EndOfTurn) {
-                return "Target " + mode.getTargets().get(0).getTargetName() + " can attack this turn as though it didn't have defender";
+                return "target " + mode.getTargets().get(0).getTargetName() + " can attack this turn as though it didn't have defender";
             } else {
-                return "Target " + mode.getTargets().get(0).getTargetName() + " can attack as though it didn't have defender";
+                return "target " + mode.getTargets().get(0).getTargetName() + " can attack as though it didn't have defender";
             }
         } else {
             throw new UnsupportedOperationException("No target defined");
diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAllCreatureTypesTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAllCreatureTypesTargetEffect.java
index e913d661ab..7be4c3ef7b 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAllCreatureTypesTargetEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAllCreatureTypesTargetEffect.java
@@ -40,6 +40,6 @@ public class LoseAllCreatureTypesTargetEffect extends ContinuousEffectImpl {
         if (staticText != null && !staticText.isEmpty()) {
             return staticText;
         }
-        return "Target " + mode.getTargets().get(0).getTargetName() + " loses all creature types " + duration.toString();
+        return "target " + mode.getTargets().get(0).getTargetName() + " loses all creature types " + duration.toString();
     }
 }
diff --git a/Mage/src/main/java/mage/abilities/effects/common/counter/AddPoisonCounterTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/counter/AddPoisonCounterTargetEffect.java
index ba836e76ad..69d595db61 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/counter/AddPoisonCounterTargetEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/counter/AddPoisonCounterTargetEffect.java
@@ -51,6 +51,6 @@ public class AddPoisonCounterTargetEffect extends OneShotEffect {
         if(staticText != null && !staticText.isEmpty()) {
             return staticText;
         }
-        return "Target " + mode.getTargets().get(0).getTargetName() + " gets " + Integer.toString(amount) + " poison counter(s).";
+        return "target " + mode.getTargets().get(0).getTargetName() + " gets " + Integer.toString(amount) + " poison counter(s).";
     }
 }

From b5f45f395faa13d82af7bdc3b83b0d67647965ac Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Sun, 14 Jun 2020 04:32:10 +0400
Subject: [PATCH 254/586] Tests: improved showCardInfo to show texts from
 multiple card names

---
 .../cards/single/ThousandYearStormTest.java   |  2 +-
 .../java/mage/verify/VerifyCardDataTest.java  | 28 ++++++++++++-------
 2 files changed, 19 insertions(+), 11 deletions(-)

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ThousandYearStormTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/ThousandYearStormTest.java
index f777c75bab..f2a88b5b99 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/single/ThousandYearStormTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/ThousandYearStormTest.java
@@ -280,7 +280,7 @@ public class ThousandYearStormTest extends CardTestPlayerBase {
     Cycling {2} ({2}, Discard this card: Draw a card.)
      */
     // Test fails sometimes with the following message:
-    // java.lang.AssertionError: b 0x copy after control - PlayerA have wrong life: 20 <> 17 expected:<17> but was:<20>
+    // Can't find available command - activate:Cast Lightning Bolt$targetPlayer=PlayerA
     @Test
     public void test_GetControlNotCounts() {
         addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5);
diff --git a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java
index 8e32f24aec..2c2d1e3e4e 100644
--- a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java
+++ b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java
@@ -967,17 +967,25 @@ public class VerifyCardDataTest {
     @Test
     public void showCardInfo() throws Exception {
         // debug only: show direct card info (takes it from class file, not from db repository)
-        String cardName = "Essence Capture";
+        // can check multiple cards at once, example: name1;name2;name3
+        String cardNames = "Armed // Dangerous;Beacon Behemoth;Grizzly Bears";
         CardScanner.scan();
-        CardSetInfo testSet = new CardSetInfo(cardName, "test", "123", Rarity.COMMON);
-        CardInfo cardInfo = CardRepository.instance.findCard(cardName);
-        Card card = CardImpl.createCard(cardInfo.getClassName(), testSet);
-        System.out.println(card.getName());
-        if (card instanceof SplitCard) {
-            card.getAbilities().getRules(card.getName()).stream().forEach(System.out::println);
-        } else {
-            card.getRules().stream().forEach(System.out::println);
-        }
+        Arrays.stream(cardNames.split(";")).forEach(cardName -> {
+            cardName = cardName.trim();
+            CardSetInfo testSet = new CardSetInfo(cardName, "test", "123", Rarity.COMMON);
+            CardInfo cardInfo = CardRepository.instance.findCard(cardName);
+            if (cardInfo == null) {
+                Assert.fail("Can't find card name: " + cardName);
+            }
+            Card card = CardImpl.createCard(cardInfo.getClassName(), testSet);
+            System.out.println();
+            System.out.println(card.getName() + " " + card.getManaCost().getText());
+            if (card instanceof SplitCard) {
+                card.getAbilities().getRules(card.getName()).stream().forEach(System.out::println);
+            } else {
+                card.getRules().stream().forEach(System.out::println);
+            }
+        });
     }
 
     private void checkWrongAbilitiesText(Card card, JsonCard ref, int cardIndex) {

From 3ac1f924b35b9dc8e597859468ae339d4520e211 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Sun, 14 Jun 2020 05:03:53 +0400
Subject: [PATCH 255/586] [M21] Siege Striker - fixed optional effect and null
 checks;

---
 Mage.Sets/src/mage/cards/g/GuildSummit.java   | 10 ++++++----
 .../src/mage/cards/j/JaddiLifestrider.java    | 19 ++++++++++---------
 Mage.Sets/src/mage/cards/l/LedevChampion.java | 17 +++++++----------
 Mage.Sets/src/mage/cards/s/SiegeStriker.java  | 11 ++++++-----
 4 files changed, 29 insertions(+), 28 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/g/GuildSummit.java b/Mage.Sets/src/mage/cards/g/GuildSummit.java
index 1558862ef0..e6ef9058de 100644
--- a/Mage.Sets/src/mage/cards/g/GuildSummit.java
+++ b/Mage.Sets/src/mage/cards/g/GuildSummit.java
@@ -1,6 +1,5 @@
 package mage.cards.g;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
@@ -16,11 +15,13 @@ import mage.filter.FilterPermanent;
 import mage.filter.predicate.Predicates;
 import mage.filter.predicate.permanent.TappedPredicate;
 import mage.game.Game;
+import mage.game.permanent.Permanent;
 import mage.players.Player;
 import mage.target.TargetPermanent;
 
+import java.util.UUID;
+
 /**
- *
  * @author TheElk801
  */
 public final class GuildSummit extends CardImpl {
@@ -82,9 +83,10 @@ class GuildSummitEffect extends OneShotEffect {
         Player you = game.getPlayer(source.getControllerId());
         TargetPermanent target = new TargetPermanent(0, Integer.MAX_VALUE, filter, true);
         if (target.canChoose(source.getControllerId(), game) && target.choose(Outcome.Tap, source.getControllerId(), source.getSourceId(), game)) {
-            for (UUID creature : target.getTargets()) {
+            for (UUID creatureId : target.getTargets()) {
+                Permanent creature = game.getPermanent(creatureId);
                 if (creature != null) {
-                    game.getPermanent(creature).tap(game);
+                    creature.tap(game);
                     tappedAmount++;
                 }
             }
diff --git a/Mage.Sets/src/mage/cards/j/JaddiLifestrider.java b/Mage.Sets/src/mage/cards/j/JaddiLifestrider.java
index 802a648f8b..6c38cd8e8c 100644
--- a/Mage.Sets/src/mage/cards/j/JaddiLifestrider.java
+++ b/Mage.Sets/src/mage/cards/j/JaddiLifestrider.java
@@ -1,7 +1,5 @@
-
 package mage.cards.j;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
@@ -9,24 +7,26 @@ import mage.abilities.effects.OneShotEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.SubType;
 import mage.constants.Outcome;
+import mage.constants.SubType;
 import mage.constants.TargetController;
 import mage.filter.common.FilterCreaturePermanent;
 import mage.filter.predicate.Predicates;
 import mage.filter.predicate.permanent.TappedPredicate;
 import mage.game.Game;
+import mage.game.permanent.Permanent;
 import mage.players.Player;
 import mage.target.common.TargetCreaturePermanent;
 
+import java.util.UUID;
+
 /**
- *
  * @author jeffwadsworth
  */
 public final class JaddiLifestrider extends CardImpl {
 
     public JaddiLifestrider(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{G}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}");
         this.subtype.add(SubType.ELEMENTAL);
 
         this.power = new MageInt(2);
@@ -47,9 +47,9 @@ public final class JaddiLifestrider extends CardImpl {
 }
 
 class JaddiLifestriderEffect extends OneShotEffect {
-    
+
     private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("untapped creatures you control");
-    
+
     static {
         filter.add(TargetController.YOU.getControllerPredicate());
         filter.add(Predicates.not(TappedPredicate.instance));
@@ -70,9 +70,10 @@ class JaddiLifestriderEffect extends OneShotEffect {
         Player you = game.getPlayer(source.getControllerId());
         TargetCreaturePermanent target = new TargetCreaturePermanent(0, Integer.MAX_VALUE, filter, true);
         if (target.canChoose(source.getControllerId(), game) && target.choose(Outcome.Tap, source.getControllerId(), source.getSourceId(), game)) {
-            for (UUID creature : target.getTargets()) {
+            for (UUID creatureId : target.getTargets()) {
+                Permanent creature = game.getPermanent(creatureId);
                 if (creature != null) {
-                    game.getPermanent(creature).tap(game);
+                    creature.tap(game);
                     tappedAmount++;
                 }
             }
diff --git a/Mage.Sets/src/mage/cards/l/LedevChampion.java b/Mage.Sets/src/mage/cards/l/LedevChampion.java
index 108ac4328d..10303e8e7d 100644
--- a/Mage.Sets/src/mage/cards/l/LedevChampion.java
+++ b/Mage.Sets/src/mage/cards/l/LedevChampion.java
@@ -1,6 +1,5 @@
 package mage.cards.l;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.AttacksTriggeredAbility;
@@ -9,23 +8,20 @@ import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.abilities.effects.common.continuous.BoostSourceEffect;
-import mage.constants.SubType;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.CardType;
-import mage.constants.Duration;
-import mage.constants.Outcome;
-import mage.constants.TargetController;
-import mage.constants.Zone;
+import mage.constants.*;
 import mage.filter.common.FilterCreaturePermanent;
 import mage.filter.predicate.Predicates;
 import mage.filter.predicate.permanent.TappedPredicate;
 import mage.game.Game;
+import mage.game.permanent.Permanent;
 import mage.game.permanent.token.SoldierLifelinkToken;
 import mage.target.common.TargetCreaturePermanent;
 
+import java.util.UUID;
+
 /**
- *
  * @author TheElk801
  */
 public final class LedevChampion extends CardImpl {
@@ -86,9 +82,10 @@ class LedevChampionEffect extends OneShotEffect {
         TargetCreaturePermanent target = new TargetCreaturePermanent(0, Integer.MAX_VALUE, filter, true);
         if (target.canChoose(source.getControllerId(), game)
                 && target.choose(Outcome.Tap, source.getControllerId(), source.getSourceId(), game)) {
-            for (UUID creature : target.getTargets()) {
+            for (UUID creatureId : target.getTargets()) {
+                Permanent creature = game.getPermanent(creatureId);
                 if (creature != null) {
-                    game.getPermanent(creature).tap(game);
+                    creature.tap(game);
                     tappedAmount++;
                 }
             }
diff --git a/Mage.Sets/src/mage/cards/s/SiegeStriker.java b/Mage.Sets/src/mage/cards/s/SiegeStriker.java
index efa64c906e..33bfd4cc6f 100644
--- a/Mage.Sets/src/mage/cards/s/SiegeStriker.java
+++ b/Mage.Sets/src/mage/cards/s/SiegeStriker.java
@@ -13,12 +13,12 @@ import mage.filter.common.FilterCreaturePermanent;
 import mage.filter.predicate.Predicates;
 import mage.filter.predicate.permanent.TappedPredicate;
 import mage.game.Game;
+import mage.game.permanent.Permanent;
 import mage.target.common.TargetCreaturePermanent;
 
 import java.util.UUID;
 
 /**
- *
  * @author mikalinn777
  */
 public final class SiegeStriker extends CardImpl {
@@ -36,7 +36,7 @@ public final class SiegeStriker extends CardImpl {
 
         // Whenever Siege Striker attacks, you may tap any number of untapped creatures you control. Siege Striker gets +1/+1 until end of turn for each creature tapped this way.
         this.addAbility(new AttacksTriggeredAbility(
-                new SiegeStrikerEffect(), false
+                new SiegeStrikerEffect(), true
         ));
 
     }
@@ -63,7 +63,7 @@ class SiegeStrikerEffect extends OneShotEffect {
     public SiegeStrikerEffect() {
         super(Outcome.GainLife);
         staticText = "you may tap any number of untapped creatures you control. "
-                + "{this} gets +1/+1 until end of turn for each creature tapped this way.";
+                + "{this} gets +1/+1 until end of turn for each creature tapped this way";
     }
 
     public SiegeStrikerEffect(SiegeStrikerEffect effect) {
@@ -76,9 +76,10 @@ class SiegeStrikerEffect extends OneShotEffect {
         TargetCreaturePermanent target = new TargetCreaturePermanent(0, Integer.MAX_VALUE, filter, true);
         if (target.canChoose(source.getControllerId(), game)
                 && target.choose(Outcome.Tap, source.getControllerId(), source.getSourceId(), game)) {
-            for (UUID creature : target.getTargets()) {
+            for (UUID creatureId : target.getTargets()) {
+                Permanent creature = game.getPermanent(creatureId);
                 if (creature != null) {
-                    game.getPermanent(creature).tap(game);
+                    creature.tap(game);
                     tappedAmount++;
                 }
             }

From 7f511275adc563bad191107d56dde9a4c1175207 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Sun, 14 Jun 2020 05:11:11 +0400
Subject: [PATCH 256/586] [M21] Primal Might - fixed wrong targets and text;

---
 Mage.Sets/src/mage/cards/p/PrimalMight.java | 30 ++++++++++-----------
 1 file changed, 14 insertions(+), 16 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/p/PrimalMight.java b/Mage.Sets/src/mage/cards/p/PrimalMight.java
index 283911fd58..20c357b41c 100644
--- a/Mage.Sets/src/mage/cards/p/PrimalMight.java
+++ b/Mage.Sets/src/mage/cards/p/PrimalMight.java
@@ -1,20 +1,17 @@
-
-
 package mage.cards.p;
 
 import mage.abilities.dynamicvalue.common.ManacostVariableValue;
+import mage.abilities.effects.common.FightTargetsEffect;
 import mage.abilities.effects.common.continuous.BoostTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.Duration;
-import mage.filter.common.FilterCreaturePermanent;
-import mage.target.common.TargetCreaturePermanent;
-import mage.abilities.effects.common.FightTargetsEffect;
-import java.util.UUID;
-import mage.filter.FilterPermanent;
+import mage.filter.StaticFilters;
 import mage.target.TargetPermanent;
-import mage.target.common.TargetAnyTarget;
+import mage.target.common.TargetControlledCreaturePermanent;
+
+import java.util.UUID;
 
 /**
  * @author mikalinn777
@@ -23,16 +20,17 @@ import mage.target.common.TargetAnyTarget;
 
 public final class PrimalMight extends CardImpl {
 
-    private static final FilterPermanent filter = new FilterCreaturePermanent("creature you don't control");
-
     public PrimalMight(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{X}{G}");
-        // Target creature gets +X/+X until end of turn.
-        this.getSpellAbility().addTarget(new TargetCreaturePermanent());
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{G}");
+
+        // Target creature you control gets +X/+X until end of turn. Then it fights up to one target creature you don’t control.
+        this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent());
         this.getSpellAbility().addEffect(new BoostTargetEffect(ManacostVariableValue.instance, ManacostVariableValue.instance, Duration.EndOfTurn));
-        // Then, it fights up to one target creature.
-        this.getSpellAbility().addEffect(new FightTargetsEffect());
-        this.getSpellAbility().addTarget(new TargetPermanent(0, 1, filter, false));
+        //
+        this.getSpellAbility().addEffect(new FightTargetsEffect()
+                .concatBy("Then")
+                .setText("it fights up to one target creature you don’t control"));
+        this.getSpellAbility().addTarget(new TargetPermanent(0, 1, StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL, false));
 
     }
 

From a254851f72c9a5eaf4ea54b13483f7edf4972249 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Sun, 14 Jun 2020 05:20:46 +0400
Subject: [PATCH 257/586] [M21] Obsessive Stitcher - fixed text;

---
 Mage.Sets/src/mage/cards/a/ArgivianRestoration.java | 10 ++++------
 Mage.Sets/src/mage/cards/d/DefyDeath.java           |  7 +++----
 Mage.Sets/src/mage/cards/o/ObsessiveStitcher.java   |  9 +++------
 3 files changed, 10 insertions(+), 16 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/a/ArgivianRestoration.java b/Mage.Sets/src/mage/cards/a/ArgivianRestoration.java
index ba7aa7f660..6332eb2124 100644
--- a/Mage.Sets/src/mage/cards/a/ArgivianRestoration.java
+++ b/Mage.Sets/src/mage/cards/a/ArgivianRestoration.java
@@ -1,7 +1,5 @@
-
 package mage.cards.a;
 
-import java.util.UUID;
 import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -9,18 +7,18 @@ import mage.constants.CardType;
 import mage.filter.common.FilterArtifactCard;
 import mage.target.common.TargetCardInYourGraveyard;
 
+import java.util.UUID;
+
 /**
- *
  * @author North
  */
 public final class ArgivianRestoration extends CardImpl {
 
     public ArgivianRestoration(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{U}{U}");
-
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}{U}");
 
         // Return target artifact card from your graveyard to the battlefield.
-        this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect());
+        this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect(false, false));
         this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(new FilterArtifactCard("artifact card from your graveyard")));
     }
 
diff --git a/Mage.Sets/src/mage/cards/d/DefyDeath.java b/Mage.Sets/src/mage/cards/d/DefyDeath.java
index 3a2f5783d2..92fa3bbd0c 100644
--- a/Mage.Sets/src/mage/cards/d/DefyDeath.java
+++ b/Mage.Sets/src/mage/cards/d/DefyDeath.java
@@ -1,7 +1,5 @@
-
 package mage.cards.d;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
@@ -16,8 +14,9 @@ import mage.game.Game;
 import mage.game.permanent.Permanent;
 import mage.target.common.TargetCardInYourGraveyard;
 
+import java.util.UUID;
+
 /**
- *
  * @author North
  */
 public final class DefyDeath extends CardImpl {
@@ -26,7 +25,7 @@ public final class DefyDeath extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{W}{W}");
 
         // Return target creature card from your graveyard to the battlefield. If it's an Angel, put two +1/+1 counters on it.
-        this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect());
+        this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect(false, false));
         this.getSpellAbility().addEffect(new DefyDeathEffect());
         this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD));
     }
diff --git a/Mage.Sets/src/mage/cards/o/ObsessiveStitcher.java b/Mage.Sets/src/mage/cards/o/ObsessiveStitcher.java
index c7e800b4c6..560a10f046 100644
--- a/Mage.Sets/src/mage/cards/o/ObsessiveStitcher.java
+++ b/Mage.Sets/src/mage/cards/o/ObsessiveStitcher.java
@@ -1,4 +1,3 @@
-
 package mage.cards.o;
 
 import mage.MageInt;
@@ -16,12 +15,10 @@ import mage.constants.SubType;
 import mage.constants.Zone;
 import mage.filter.StaticFilters;
 import mage.target.common.TargetCardInYourGraveyard;
-import mage.target.common.TargetAnyTarget;
 
 import java.util.UUID;
 
 /**
- *
  * @author mikalinn777
  */
 public final class ObsessiveStitcher extends CardImpl {
@@ -34,11 +31,11 @@ public final class ObsessiveStitcher extends CardImpl {
         this.power = new MageInt(0);
         this.toughness = new MageInt(3);
 
-        // Draw a card, then discard a card.
+        // {T}: Draw a card, then discard a card.
         this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawDiscardControllerEffect(), new TapSourceCost()));
 
-        // {2}{U}{B}, {tap}, Sacrifice Obsessive Stitcher: Return target creature card from your graveyard to the battlefield.
-        Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ReturnFromGraveyardToBattlefieldTargetEffect(), new ManaCostsImpl("{2}{U}{B}"));
+        // {2}{U}{B}, {T}, Sacrifice Obsessive Stitcher: Return target creature card from your graveyard to the battlefield.
+        Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ReturnFromGraveyardToBattlefieldTargetEffect(false, false), new ManaCostsImpl("{2}{U}{B}"));
         ability.addCost(new TapSourceCost());
         ability.addCost(new SacrificeSourceCost());
         ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD));

From dbd1981fcbf7197da8951cca065d04a69e7f3adc Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Sun, 14 Jun 2020 05:33:32 +0400
Subject: [PATCH 258/586] Merge fix

---
 Mage.Sets/src/mage/cards/p/PrimalMight.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Mage.Sets/src/mage/cards/p/PrimalMight.java b/Mage.Sets/src/mage/cards/p/PrimalMight.java
index 20c357b41c..b0d0e69504 100644
--- a/Mage.Sets/src/mage/cards/p/PrimalMight.java
+++ b/Mage.Sets/src/mage/cards/p/PrimalMight.java
@@ -29,7 +29,7 @@ public final class PrimalMight extends CardImpl {
         //
         this.getSpellAbility().addEffect(new FightTargetsEffect()
                 .concatBy("Then")
-                .setText("it fights up to one target creature you don’t control"));
+                .setText("it fights up to one target creature you don't control"));
         this.getSpellAbility().addTarget(new TargetPermanent(0, 1, StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL, false));
 
     }

From aaf1f7da7bbd1410b214609ac1876024eb89b51f Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Sun, 14 Jun 2020 08:46:25 +0200
Subject: [PATCH 259/586] * Bronzehide Lion - Fixed that activated ability of
 enchanted form applied Indestructibility while on battlefield if activated
 instead of end of the turn. * Tectonic Giant - Fixed that it also triggerd on
 activated abilities instead only for spells. (fixes #6397)

---
 .../src/mage/cards/b/BronzehideLion.java      |  3 +--
 Mage.Sets/src/mage/cards/t/TectonicGiant.java | 27 ++++++++++---------
 Mage/src/main/java/mage/MageObject.java       | 13 ++++-----
 3 files changed, 20 insertions(+), 23 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/b/BronzehideLion.java b/Mage.Sets/src/mage/cards/b/BronzehideLion.java
index 5726a08cb0..737dc12661 100644
--- a/Mage.Sets/src/mage/cards/b/BronzehideLion.java
+++ b/Mage.Sets/src/mage/cards/b/BronzehideLion.java
@@ -4,7 +4,6 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
 import mage.MageInt;
-import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.common.DiesTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
@@ -109,7 +108,7 @@ class BronzehideLionContinuousEffect extends ContinuousEffectImpl {
 
     private final int zoneChangeCounter;
     private final Ability activatedAbility = new SimpleActivatedAbility(new GainAbilityAttachedEffect(
-            IndestructibleAbility.getInstance(), AttachmentType.AURA, Duration.WhileOnBattlefield
+            IndestructibleAbility.getInstance(), AttachmentType.AURA, Duration.EndOfTurn
     ), new ManaCostsImpl("{G}{W}"));
 
     BronzehideLionContinuousEffect(int zoneChangeCounter) {
diff --git a/Mage.Sets/src/mage/cards/t/TectonicGiant.java b/Mage.Sets/src/mage/cards/t/TectonicGiant.java
index 3f72f5440f..b9e8d5a21f 100644
--- a/Mage.Sets/src/mage/cards/t/TectonicGiant.java
+++ b/Mage.Sets/src/mage/cards/t/TectonicGiant.java
@@ -1,6 +1,8 @@
 package mage.cards.t;
 
+import java.util.UUID;
 import mage.MageInt;
+import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.Mode;
 import mage.abilities.TriggeredAbilityImpl;
@@ -10,19 +12,16 @@ import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.DamagePlayersEffect;
 import mage.cards.*;
 import mage.constants.*;
+import static mage.constants.Outcome.Benefit;
 import mage.filter.StaticFilters;
 import mage.game.Game;
 import mage.game.events.GameEvent;
-import mage.game.stack.StackObject;
+import mage.game.stack.Spell;
 import mage.players.Player;
 import mage.target.TargetCard;
 import mage.target.common.TargetCardInExile;
 import mage.target.targetpointer.FixedTarget;
 
-import java.util.UUID;
-
-import static mage.constants.Outcome.Benefit;
-
 /**
  * @author TheElk801
  */
@@ -75,12 +74,14 @@ class TectonicGiantTriggeredAbility extends TriggeredAbilityImpl {
             case DECLARED_ATTACKERS:
                 return game.getCombat().getAttackers().contains(this.getSourceId());
             case TARGETED:
-                StackObject sourceObject = game.getStack().getStackObject(event.getSourceId());
-                Player player = game.getPlayer(getControllerId());
-                return sourceObject != null
-                        && player != null
-                        && player.hasOpponent(sourceObject.getControllerId(), game)
-                        && event.getTargetId().equals(getSourceId());
+                if (event.getTargetId().equals(getSourceId())) {
+                    MageObject mageObject = game.getObject(event.getSourceId());
+                    Player player = game.getPlayer(getControllerId());
+                    return mageObject != null
+                            && mageObject instanceof Spell
+                            && player != null
+                            && player.hasOpponent(((Spell) mageObject).getControllerId(), game);
+                }
         }
         return false;
     }
@@ -100,8 +101,8 @@ class TectonicGiantEffect extends OneShotEffect {
 
     TectonicGiantEffect() {
         super(Benefit);
-        staticText = "exile the top two cards of your library. Choose one of them. " +
-                "Until the end of your next turn, you may play that card";
+        staticText = "exile the top two cards of your library. Choose one of them. "
+                + "Until the end of your next turn, you may play that card";
     }
 
     private TectonicGiantEffect(final TectonicGiantEffect effect) {
diff --git a/Mage/src/main/java/mage/MageObject.java b/Mage/src/main/java/mage/MageObject.java
index b2c21ab27d..d25a2f3e5d 100644
--- a/Mage/src/main/java/mage/MageObject.java
+++ b/Mage/src/main/java/mage/MageObject.java
@@ -1,5 +1,8 @@
 package mage;
 
+import java.io.Serializable;
+import java.util.List;
+import java.util.Set;
 import mage.abilities.Abilities;
 import mage.abilities.Ability;
 import mage.abilities.costs.mana.ManaCost;
@@ -15,12 +18,6 @@ import mage.game.Game;
 import mage.game.events.ZoneChangeEvent;
 import mage.util.SubTypeList;
 
-import java.io.Serializable;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Set;
-import java.util.UUID;
-
 public interface MageObject extends MageItem, Serializable {
 
     String getName();
@@ -42,8 +39,8 @@ public interface MageObject extends MageItem, Serializable {
     Set<SuperType> getSuperType();
 
     /**
-     * For cards: return basic abilities (without dynamic added)
-     * For permanents: return all abilities (dynamic ability inserts into permanent)
+     * For cards: return basic abilities (without dynamic added) For permanents:
+     * return all abilities (dynamic ability inserts into permanent)
      */
     Abilities<Ability> getAbilities();
 

From 033f80c6ed77874aa7de3606594ac7865ee6adca Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Sun, 14 Jun 2020 09:34:29 +0200
Subject: [PATCH 260/586] * Use for automatic mana payment of generic mana
 costs first abilities without additional costs (fixes #6408).

---
 Mage/src/main/java/mage/util/ManaUtil.java | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/Mage/src/main/java/mage/util/ManaUtil.java b/Mage/src/main/java/mage/util/ManaUtil.java
index d153fe9315..ef24b04b43 100644
--- a/Mage/src/main/java/mage/util/ManaUtil.java
+++ b/Mage/src/main/java/mage/util/ManaUtil.java
@@ -1,10 +1,12 @@
 package mage.util;
 
+import java.util.*;
 import mage.MageObject;
 import mage.Mana;
 import mage.ManaSymbol;
 import mage.abilities.Ability;
 import mage.abilities.costs.Cost;
+import mage.abilities.costs.common.TapSourceCost;
 import mage.abilities.costs.mana.*;
 import mage.abilities.dynamicvalue.DynamicValue;
 import mage.abilities.dynamicvalue.common.ManacostVariableValue;
@@ -17,8 +19,6 @@ import mage.filter.FilterMana;
 import mage.game.Game;
 import mage.players.Player;
 
-import java.util.*;
-
 /**
  * @author noxx
  */
@@ -46,7 +46,7 @@ public final class ManaUtil {
      * In case we can't auto choose we'll simply return the useableAbilities map
      * back to caller without any modification.
      *
-     * @param unpaid           Mana we need to pay. Can be null (it is for X costs now).
+     * @param unpaid Mana we need to pay. Can be null (it is for X costs now).
      * @param useableAbilities List of mana abilities permanent may produce
      * @return List of mana abilities permanent may produce and are reasonable
      * for unpaid mana
@@ -373,7 +373,12 @@ public final class ManaUtil {
         if (countColorfull == 0) { // seems there is no colorful mana we can use
             // try to pay {1}
             if (mana.getGeneric() > 0) {
-                // use any (lets choose first)
+                // choose first without addional costs if all have addional costs take the first
+                for (ActivatedManaAbilityImpl manaAbility : useableAbilities.values()) {
+                    if (manaAbility.getCosts().size() == 1 && manaAbility.getCosts().get(0).getClass().equals(TapSourceCost.class)) {
+                        return replace(useableAbilities, manaAbility);
+                    }
+                }
                 return replace(useableAbilities, useableAbilities.values().iterator().next());
             }
 
@@ -499,7 +504,8 @@ public final class ManaUtil {
     }
 
     /**
-     * all ability/effect code with "= new GenericManaCost" must be replaced by createManaCost call
+     * all ability/effect code with "= new GenericManaCost" must be replaced by
+     * createManaCost call
      */
     public static ManaCost createManaCost(int genericManaCount, boolean payAsX) {
         if (payAsX) {

From f7c24e8b7f4c910a112de30b1908ec1d6b6b60bf Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Sun, 14 Jun 2020 12:22:30 +0200
Subject: [PATCH 261/586] * Starfield of Nyx - Fixed that not only controlled
 permanents are effected. Fixed some layered effects problems (fixes #6638).

---
 .../src/mage/cards/s/SongOfTheDryads.java     |   9 +-
 .../src/mage/cards/s/StarfieldOfNyx.java      |  27 ++-
 .../enchantments/StarfieldOfNyxTest.java      |  24 +-
 .../base/impl/CardTestPlayerAPIImpl.java      | 206 +++++++++---------
 4 files changed, 141 insertions(+), 125 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/s/SongOfTheDryads.java b/Mage.Sets/src/mage/cards/s/SongOfTheDryads.java
index ce6b7187a3..14e5f342f8 100644
--- a/Mage.Sets/src/mage/cards/s/SongOfTheDryads.java
+++ b/Mage.Sets/src/mage/cards/s/SongOfTheDryads.java
@@ -1,4 +1,3 @@
-
 package mage.cards.s;
 
 import java.util.UUID;
@@ -11,11 +10,12 @@ import mage.abilities.mana.GreenManaAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.SubType;
+import mage.constants.DependencyType;
 import mage.constants.Duration;
 import mage.constants.Layer;
 import mage.constants.Outcome;
 import mage.constants.SubLayer;
+import mage.constants.SubType;
 import mage.constants.Zone;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
@@ -28,7 +28,7 @@ import mage.target.TargetPermanent;
 public final class SongOfTheDryads extends CardImpl {
 
     public SongOfTheDryads(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{G}");
+        super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}");
         this.subtype.add(SubType.AURA);
 
         // Enchant permanent
@@ -58,6 +58,7 @@ class BecomesColorlessForestLandEffect extends ContinuousEffectImpl {
     public BecomesColorlessForestLandEffect() {
         super(Duration.WhileOnBattlefield, Outcome.Detriment);
         this.staticText = "Enchanted permanent is a colorless Forest land";
+        dependencyTypes.add(DependencyType.BecomeForest);
     }
 
     public BecomesColorlessForestLandEffect(final BecomesColorlessForestLandEffect effect) {
@@ -89,10 +90,10 @@ class BecomesColorlessForestLandEffect extends ContinuousEffectImpl {
                         permanent.getColor(game).setRed(false);
                         break;
                     case AbilityAddingRemovingEffects_6:
-                        permanent.removeAllAbilities(source.getSourceId(), game);
                         permanent.addAbility(new GreenManaAbility(), source.getSourceId(), game);
                         break;
                     case TypeChangingEffects_4:
+                        permanent.removeAllAbilities(source.getSourceId(), game);
                         permanent.getCardType().clear();
                         permanent.addCardType(CardType.LAND);
                         permanent.getSubtype(game).clear();
diff --git a/Mage.Sets/src/mage/cards/s/StarfieldOfNyx.java b/Mage.Sets/src/mage/cards/s/StarfieldOfNyx.java
index 81a49dc528..9e0df8ed14 100644
--- a/Mage.Sets/src/mage/cards/s/StarfieldOfNyx.java
+++ b/Mage.Sets/src/mage/cards/s/StarfieldOfNyx.java
@@ -12,7 +12,7 @@ import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.*;
 import mage.filter.FilterCard;
-import mage.filter.FilterPermanent;
+import mage.filter.common.FilterControlledPermanent;
 import mage.filter.common.FilterEnchantmentPermanent;
 import mage.filter.predicate.Predicates;
 import mage.filter.predicate.permanent.AnotherPredicate;
@@ -47,15 +47,15 @@ public final class StarfieldOfNyx extends CardImpl {
     public StarfieldOfNyx(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{W}");
 
-        // At the beginning of your upkeep, you may return target enchantment card 
+        // At the beginning of your upkeep, you may return target enchantment card
         // from your graveyard to the battlefield.
         Ability ability = new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD,
                 new ReturnFromGraveyardToBattlefieldTargetEffect(), TargetController.YOU, true);
         ability.addTarget(new TargetCardInGraveyard(filterGraveyardEnchantment));
         this.addAbility(ability);
 
-        // As long as you control five or more enchantments, each other non-Aura enchantment 
-        // you control is a creature in addition to its other types and has base power and 
+        // As long as you control five or more enchantments, each other non-Aura enchantment
+        // you control is a creature in addition to its other types and has base power and
         // base toughness each equal to its converted mana cost.
         ConditionalContinuousEffect effect = new ConditionalContinuousEffect(
                 new StarfieldOfNyxEffect(), new PermanentsOnTheBattlefieldCondition(
@@ -74,13 +74,13 @@ public final class StarfieldOfNyx extends CardImpl {
 
     static class StarfieldOfNyxEffect extends ContinuousEffectImpl {
 
-        private static final FilterPermanent filter
-                = new FilterPermanent("Each other non-Aura enchantment you control");
-        private static final FilterEnchantmentPermanent filter2
-                = new FilterEnchantmentPermanent();
+        private static final FilterControlledPermanent filter
+                = new FilterControlledPermanent("Each other non-Aura enchantment you control");
 
         static {
-            filter2.add(TargetController.YOU.getControllerPredicate());
+            filter.add(CardType.ENCHANTMENT.getPredicate());
+            filter.add(Predicates.not(SubType.AURA.getPredicate()));
+            filter.add(AnotherPredicate.instance);
         }
 
         public StarfieldOfNyxEffect() {
@@ -91,8 +91,14 @@ public final class StarfieldOfNyx extends CardImpl {
 
             this.dependendToTypes.add(DependencyType.EnchantmentAddingRemoving); // Enchanted Evening
             this.dependendToTypes.add(DependencyType.AuraAddingRemoving); // Cloudform
+            this.dependendToTypes.add(DependencyType.BecomeForest); // Song of the Dryads
+            this.dependendToTypes.add(DependencyType.BecomeMountain);
+            this.dependendToTypes.add(DependencyType.BecomePlains);
+            this.dependendToTypes.add(DependencyType.BecomeSwamp);
+            this.dependendToTypes.add(DependencyType.BecomeIsland);
 
             this.dependencyTypes.add(DependencyType.BecomeCreature);  // Conspiracy
+
         }
 
         public StarfieldOfNyxEffect(final StarfieldOfNyxEffect effect) {
@@ -106,9 +112,6 @@ public final class StarfieldOfNyx extends CardImpl {
 
         @Override
         public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
-            filter.add(CardType.ENCHANTMENT.getPredicate());
-            filter.add(Predicates.not(SubType.AURA.getPredicate()));
-            filter.add(AnotherPredicate.instance);
             for (Permanent permanent : game.getBattlefield().getActivePermanents(filter,
                     source.getControllerId(), source.getSourceId(), game)) {
                 switch (layer) {
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/StarfieldOfNyxTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/StarfieldOfNyxTest.java
index 157692786a..ad524a51a4 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/StarfieldOfNyxTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/StarfieldOfNyxTest.java
@@ -1,13 +1,14 @@
 package org.mage.test.cards.enchantments;
 
 import mage.abilities.keyword.FlyingAbility;
+import mage.constants.CardType;
 import mage.constants.EmptyNames;
 import mage.constants.PhaseStep;
+import mage.constants.SubType;
 import mage.constants.Zone;
 import mage.filter.Filter;
 import mage.game.permanent.Permanent;
 import org.junit.Assert;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.mage.test.serverside.base.CardTestPlayerBase;
 
@@ -25,7 +26,7 @@ public class StarfieldOfNyxTest extends CardTestPlayerBase {
      */
     @Test
     public void testCloudform() {
-        // At the beginning of your upkeep, if you control an artifact, put a 1/1 
+        // At the beginning of your upkeep, if you control an artifact, put a 1/1
         // colorless Thopter artifact creature token with flying onto the battlefield.
         // Whenever one or more artifact creatures you control deal combat damage to a player, draw a card.
         addCard(Zone.BATTLEFIELD, playerA, "Thopter Spy Network", 2); // {2}{U}{U}  - added to come to 5 enchantments on the battlefield
@@ -35,7 +36,7 @@ public class StarfieldOfNyxTest extends CardTestPlayerBase {
         // As long as you control five or more enchantments, each other non-Aura enchantment you control is a creature in
         // addition to its other types and has base power and base toughness each equal to its converted mana cost.
         addCard(Zone.HAND, playerA, "Starfield of Nyx"); // "{4}{W}"
-        // When Cloudform enters the battlefield, it becomes an Aura with enchant creature. 
+        // When Cloudform enters the battlefield, it becomes an Aura with enchant creature.
         // Manifest the top card of your library and attach Cloudform to it.
         // Enchanted creature has flying and hexproof.
         addCard(Zone.HAND, playerA, "Cloudform"); // {1}{U}{U}
@@ -51,7 +52,7 @@ public class StarfieldOfNyxTest extends CardTestPlayerBase {
         assertAllCommandsUsed();
 
         assertGraveyardCount(playerA, "Thopter Spy Network", 0);
-        assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 
+        assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(),
                 2, 2, Filter.ComparisonScope.All); // the manifested cards
         assertPermanentCount(playerA, "Starfield of Nyx", 1);
         assertPowerToughness(playerA, "Thopter Spy Network", 4, 4, Filter.ComparisonScope.All);
@@ -66,7 +67,7 @@ public class StarfieldOfNyxTest extends CardTestPlayerBase {
      */
     @Test
     public void testHexproof() {
-        // At the beginning of your upkeep, if you control an artifact, put a 1/1 colorless 
+        // At the beginning of your upkeep, if you control an artifact, put a 1/1 colorless
         // Thopter artifact creature token with flying onto the battlefield.
         // Whenever one or more artifact creatures you control deal combat damage to a player, draw a card.
         addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
@@ -114,7 +115,7 @@ public class StarfieldOfNyxTest extends CardTestPlayerBase {
         setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
         execute();
         assertAllCommandsUsed();
-        
+
         assertPowerToughness(playerA, "Master of the Feast", 3, 3, Filter.ComparisonScope.All);
         assertPowerToughness(playerA, "Humility", 4, 4, Filter.ComparisonScope.All);
         // Humility loses its ability in layer 6.  Layer 7 never gets Humility's effect
@@ -139,10 +140,9 @@ public class StarfieldOfNyxTest extends CardTestPlayerBase {
      * there.
      */
     @Test
-    @Ignore
     public void testStarfieldOfNyxAndSongOfTheDryads() {
         // Nontoken creatures you control get +1/+1 and have vigilance.
-        addCard(Zone.BATTLEFIELD, playerA, "Always Watching", 5);
+        addCard(Zone.BATTLEFIELD, playerA, "Always Watching", 5); // Enchantment {1}{W}{W}
         addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
         // At the beginning of your upkeep, you may return target enchantment card from your graveyard to the battlefield.
         // As long as you control five or more enchantments, each other non-Aura enchantment you control is a creature in
@@ -157,15 +157,19 @@ public class StarfieldOfNyxTest extends CardTestPlayerBase {
         castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Song of the Dryads", "Starfield of Nyx");
 
         setStopAt(2, PhaseStep.BEGIN_COMBAT);
+
+        setStrictChooseMode(true);
         execute();
+        assertAllCommandsUsed();
 
         assertPermanentCount(playerA, "Always Watching", 5);
         assertPermanentCount(playerB, "Song of the Dryads", 1);
 
+        assertType("Always Watching", CardType.ENCHANTMENT, true);
+        assertType("Always Watching", CardType.CREATURE, false);
         assertPowerToughness(playerA, "Always Watching", 0, 0, Filter.ComparisonScope.All);
 
-        assertPermanentCount(playerA, "Forest", 1);
-
+        assertType("Starfield of Nyx", CardType.LAND, SubType.FOREST);
     }
 
 }
diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
index 5cc971d404..3d3e4e8eda 100644
--- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
+++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
@@ -1,5 +1,13 @@
 package org.mage.test.serverside.base.impl;
 
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.UUID;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 import mage.MageObject;
 import mage.Mana;
 import mage.ObjectColor;
@@ -35,15 +43,6 @@ import org.mage.test.player.TestPlayer;
 import org.mage.test.serverside.base.CardTestAPI;
 import org.mage.test.serverside.base.MageTestPlayerBase;
 
-import java.io.FileNotFoundException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.UUID;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.stream.Collectors;
-
 /**
  * API for test initialization and asserting the test results.
  *
@@ -274,10 +273,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
             }
         }
         Assert.assertFalse("Wrong stop command on " + this.stopOnTurn + " / " + this.stopAtStep + " (" + this.stopAtStep.getIndex() + ")"
-                        + " (found actions after stop on " + maxTurn + " / " + maxPhase + ")",
+                + " (found actions after stop on " + maxTurn + " / " + maxPhase + ")",
                 (maxTurn > this.stopOnTurn) || (maxTurn == this.stopOnTurn && maxPhase > this.stopAtStep.getIndex()));
 
-
         for (Player player : currentGame.getPlayers().values()) {
             TestPlayer testPlayer = (TestPlayer) player;
             currentGame.cheat(player.getId(), getCommands(testPlayer));
@@ -326,7 +324,6 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     }
 
     // check commands
-
     private void check(String checkName, int turnNum, PhaseStep step, TestPlayer player, String command, String... params) {
         String res = CHECK_PREFIX + command;
         for (String param : params) {
@@ -428,7 +425,6 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     }
 
     // show commands
-
     private void show(String showName, int turnNum, PhaseStep step, TestPlayer player, String command, String... params) {
         String res = "show:" + command;
         for (String param : params) {
@@ -496,8 +492,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     }
 
     /**
-     * Disable auto-payment from mana pool, you must manually fill pool by activateManaAbility and unlock color by setChoice
-     * Use it for pay color order testing (e.g. simulate user clicks on mana pool to pay)
+     * Disable auto-payment from mana pool, you must manually fill pool by
+     * activateManaAbility and unlock color by setChoice Use it for pay color
+     * order testing (e.g. simulate user clicks on mana pool to pay)
      *
      * @param player
      */
@@ -509,8 +506,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Add a card to specified zone of specified player.
      *
      * @param gameZone {@link mage.constants.Zone} to add cards to.
-     * @param player   {@link Player} to add cards for. Use either playerA or
-     *                 playerB.
+     * @param player {@link Player} to add cards for. Use either playerA or
+     * playerB.
      * @param cardName Card name in string format.
      */
     @Override
@@ -522,10 +519,10 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Add any amount of cards to specified zone of specified player.
      *
      * @param gameZone {@link mage.constants.Zone} to add cards to.
-     * @param player   {@link Player} to add cards for. Use either playerA or
-     *                 playerB.
+     * @param player {@link Player} to add cards for. Use either playerA or
+     * playerB.
      * @param cardName Card name in string format.
-     * @param count    Amount of cards to be added.
+     * @param count Amount of cards to be added.
      */
     @Override
     public void addCard(Zone gameZone, TestPlayer player, String cardName, int count) {
@@ -536,13 +533,13 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Add any amount of cards to specified zone of specified player.
      *
      * @param gameZone {@link mage.constants.Zone} to add cards to.
-     * @param player   {@link Player} to add cards for. Use either playerA or
-     *                 playerB.
+     * @param player {@link Player} to add cards for. Use either playerA or
+     * playerB.
      * @param cardName Card name in string format.
-     * @param count    Amount of cards to be added.
-     * @param tapped   In case gameZone is Battlefield, determines whether
-     *                 permanent should be tapped. In case gameZone is other than Battlefield,
-     *                 {@link IllegalArgumentException} is thrown
+     * @param count Amount of cards to be added.
+     * @param tapped In case gameZone is Battlefield, determines whether
+     * permanent should be tapped. In case gameZone is other than Battlefield,
+     * {@link IllegalArgumentException} is thrown
      */
     @Override
     public void addCard(Zone gameZone, TestPlayer player, String cardName, int count, boolean tapped) {
@@ -623,7 +620,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Set player's initial life count.
      *
      * @param player {@link Player} to set life count for.
-     * @param life   Life count to set.
+     * @param life Life count to set.
      */
     @Override
     public void setLife(TestPlayer player, int life) {
@@ -700,7 +697,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert player's life count after test execution.
      *
      * @param player {@link Player} to get life for comparison.
-     * @param life   Expected player's life to compare with.
+     * @param life Expected player's life to compare with.
      */
     @Override
     public void assertLife(Player player, int life) throws AssertionError {
@@ -717,14 +714,14 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * params 3b. all: there is at least one creature with the cardName with the
      * different p\t params
      *
-     * @param player    {@link Player} to get creatures for comparison.
-     * @param cardName  Card name to compare with.
-     * @param power     Expected power to compare with.
+     * @param player {@link Player} to get creatures for comparison.
+     * @param cardName Card name to compare with.
+     * @param power Expected power to compare with.
      * @param toughness Expected toughness to compare with.
-     * @param scope     {@link mage.filter.Filter.ComparisonScope} Use ANY, if you
-     *                  want "at least one creature with given name should have specified p\t"
-     *                  Use ALL, if you want "all creature with gived name should have specified
-     *                  p\t"
+     * @param scope {@link mage.filter.Filter.ComparisonScope} Use ANY, if you
+     * want "at least one creature with given name should have specified p\t"
+     * Use ALL, if you want "all creature with gived name should have specified
+     * p\t"
      */
     @Override
     public void assertPowerToughness(Player player, String cardName, int power, int toughness, Filter.ComparisonScope scope)
@@ -813,9 +810,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * @param player
      * @param cardName
      * @param ability
-     * @param mustHave true if creature should contain ability, false if it should
-     *                 NOT contain it instead
-     * @param count    number of permanents with that ability
+     * @param mustHave true if creature should contain ability, false if it
+     * should NOT contain it instead
+     * @param count number of permanents with that ability
      * @throws AssertionError
      */
     public void assertAbility(Player player, String cardName, Ability ability, boolean mustHave, int count) throws AssertionError {
@@ -848,7 +845,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert permanent count under player's control.
      *
      * @param player {@link Player} which permanents should be counted.
-     * @param count  Expected count.
+     * @param count Expected count.
      */
     @Override
     public void assertPermanentCount(Player player, int count) throws AssertionError {
@@ -864,9 +861,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     /**
      * Assert permanent count under player's control.
      *
-     * @param player   {@link Player} which permanents should be counted.
+     * @param player {@link Player} which permanents should be counted.
      * @param cardName Name of the cards that should be counted.
-     * @param count    Expected count.
+     * @param count Expected count.
      */
     @Override
     public void assertPermanentCount(Player player, String cardName, int count) throws AssertionError {
@@ -916,8 +913,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert counter count on a permanent
      *
      * @param cardName Name of the cards that should be counted.
-     * @param type     Type of the counter that should be counted.
-     * @param count    Expected count.
+     * @param type Type of the counter that should be counted.
+     * @param count Expected count.
      */
     public void assertCounterCount(String cardName, CounterType type, int count) throws AssertionError {
         this.assertCounterCount(null, cardName, type, count);
@@ -940,8 +937,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert counter count on a card in exile
      *
      * @param cardName Name of the cards that should be counted.
-     * @param type     Type of the counter that should be counted.
-     * @param count    Expected count.
+     * @param type Type of the counter that should be counted.
+     * @param count Expected count.
      */
     public void assertCounterOnExiledCardCount(String cardName, CounterType type, int count) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -964,8 +961,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert counter count on a player
      *
      * @param player The player whos counters should be counted.
-     * @param type   Type of the counter that should be counted.
-     * @param count  Expected count.
+     * @param type Type of the counter that should be counted.
+     * @param count Expected count.
      */
     public void assertCounterCount(Player player, CounterType type, int count) throws AssertionError {
         Assert.assertEquals("(Battlefield) Counter counts are not equal (" + player.getName() + ':' + type + ')', count, player.getCounters().getCount(type));
@@ -975,7 +972,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert whether a permanent is a specified type or not
      *
      * @param cardName Name of the permanent that should be checked.
-     * @param type     A type to test for
+     * @param type A type to test for
      * @param mustHave true if creature should have type, false if it should not
      */
     public void assertType(String cardName, CardType type, boolean mustHave) throws AssertionError {
@@ -991,7 +988,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
 
         Assert.assertNotNull("There is no such permanent on the battlefield, cardName=" + cardName, found);
 
-        Assert.assertTrue("(Battlefield) card type not found (" + cardName + ':' + type + ')', (found.getCardType().contains(type) == mustHave));
+        Assert.assertTrue("(Battlefield) card type " + (mustHave ? "not " : "")
+                + "found (" + cardName + ':' + type + ')', (found.getCardType().contains(type) == mustHave));
 
     }
 
@@ -999,8 +997,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert whether a permanent is a specified type
      *
      * @param cardName Name of the permanent that should be checked.
-     * @param type     A type to test for
-     * @param subType  a subtype to test for
+     * @param type A type to test for
+     * @param subType a subtype to test for
      */
     public void assertType(String cardName, CardType type, SubType subType) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -1015,7 +1013,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert whether a permanent is not a specified type
      *
      * @param cardName Name of the permanent that should be checked.
-     * @param type     A type to test for
+     * @param type A type to test for
      */
     public void assertNotType(String cardName, CardType type) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -1027,7 +1025,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert whether a permanent is not a specified subtype
      *
      * @param cardName Name of the permanent that should be checked.
-     * @param subType  a subtype to test for
+     * @param subType a subtype to test for
      */
     public void assertNotSubtype(String cardName, SubType subType) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -1040,10 +1038,10 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     /**
      * Assert permanent color
      *
-     * @param player       player to check
-     * @param cardName     card name on battlefield from player
+     * @param player player to check
+     * @param cardName card name on battlefield from player
      * @param searchColors colors list with searchable values
-     * @param mustHave     must or not must have that colors
+     * @param mustHave must or not must have that colors
      */
     public void assertColor(Player player, String cardName, ObjectColor searchColors, boolean mustHave) {
         //Assert.assertNotEquals("", cardName);
@@ -1078,7 +1076,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert whether a permanent is tapped or not
      *
      * @param cardName Name of the permanent that should be checked.
-     * @param tapped   Whether the permanent is tapped or not
+     * @param tapped Whether the permanent is tapped or not
      */
     public void assertTapped(String cardName, boolean tapped) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -1105,8 +1103,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert whether X permanents of the same name are tapped or not.
      *
      * @param cardName Name of the permanent that should be checked.
-     * @param tapped   Whether the permanent is tapped or not
-     * @param count    The amount of this permanents that should be tapped
+     * @param tapped Whether the permanent is tapped or not
+     * @param count The amount of this permanents that should be tapped
      */
     public void assertTappedCount(String cardName, boolean tapped, int count) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -1128,7 +1126,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     /**
      * Assert whether a permanent is attacking or not
      *
-     * @param cardName  Name of the permanent that should be checked.
+     * @param cardName Name of the permanent that should be checked.
      * @param attacking Whether the permanent is attacking or not
      */
     public void assertAttacking(String cardName, boolean attacking) throws AssertionError {
@@ -1150,7 +1148,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert card count in player's hand.
      *
      * @param player {@link Player} who's hand should be counted.
-     * @param count  Expected count.
+     * @param count Expected count.
      */
     public void assertHandCount(Player player, int count) throws AssertionError {
         int actual = currentGame.getPlayer(player.getId()).getHand().size();
@@ -1160,9 +1158,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     /**
      * Assert card count in player's hand.
      *
-     * @param player   {@link Player} who's hand should be counted.
+     * @param player {@link Player} who's hand should be counted.
      * @param cardName Name of the cards that should be counted.
-     * @param count    Expected count.
+     * @param count Expected count.
      */
     public void assertHandCount(Player player, String cardName, int count) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -1182,7 +1180,6 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
         Assert.assertEquals("(Hand) Card counts for card " + cardName + " for " + player.getName() + " are not equal ", count, actual);
     }
 
-
     public void assertManaPool(Player player, ManaType color, int amount) {
         ManaPool manaPool = currentGame.getPlayer(player.getId()).getManaPool();
         switch (color) {
@@ -1211,7 +1208,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert card count in player's graveyard.
      *
      * @param player {@link Player} who's graveyard should be counted.
-     * @param count  Expected count.
+     * @param count Expected count.
      */
     public void assertGraveyardCount(Player player, int count) throws AssertionError {
         int actual = currentGame.getPlayer(player.getId()).getGraveyard().size();
@@ -1222,7 +1219,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert card count in exile.
      *
      * @param cardName Name of the cards that should be counted.
-     * @param count    Expected count.
+     * @param count Expected count.
      */
     public void assertExileCount(String cardName, int count) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -1260,9 +1257,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     /**
      * Assert card count in player's exile.
      *
-     * @param owner    {@link Player} who's exile should be counted.
+     * @param owner {@link Player} who's exile should be counted.
      * @param cardName Name of the cards that should be counted.
-     * @param count    Expected count.
+     * @param count Expected count.
      */
     public void assertExileCount(Player owner, String cardName, int count) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -1281,9 +1278,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     /**
      * Assert card count in player's graveyard.
      *
-     * @param player   {@link Player} who's graveyard should be counted.
+     * @param player {@link Player} who's graveyard should be counted.
      * @param cardName Name of the cards that should be counted.
-     * @param count    Expected count.
+     * @param count Expected count.
      */
     public void assertGraveyardCount(Player player, String cardName, int count) throws AssertionError {
         assertAliaseSupportInActivateCommand(cardName, true);
@@ -1302,7 +1299,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert library card count.
      *
      * @param player {@link Player} who's library should be counted.
-     * @param count  Expected count.
+     * @param count Expected count.
      */
     public void assertLibraryCount(Player player, int count) throws AssertionError {
         List<Card> libraryList = player.getLibrary().getCards(currentGame);
@@ -1313,9 +1310,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     /**
      * Assert specific card count in player's library.
      *
-     * @param player   {@link Player} who's library should be counted.
+     * @param player {@link Player} who's library should be counted.
      * @param cardName Name of the cards that should be counted.
-     * @param count    Expected count.
+     * @param count Expected count.
      */
     public void assertLibraryCount(Player player, String cardName, int count) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -1426,8 +1423,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     }
 
     /**
-     * AI play one PRIORITY with multi game simulations (calcs and play ONE best action, can be called with stack)
-     * All choices must be made by AI (e.g. strict mode possible)
+     * AI play one PRIORITY with multi game simulations (calcs and play ONE best
+     * action, can be called with stack) All choices must be made by AI (e.g.
+     * strict mode possible)
      */
     public void aiPlayPriority(int turnNum, PhaseStep step, TestPlayer player) {
         assertAiPlayAndGameCompatible(player);
@@ -1435,8 +1433,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     }
 
     /**
-     * AI play STEP to the end with multi game simulations (calcs and play best actions until step ends, can be called in the middle of the step)
-     * All choices must be made by AI (e.g. strict mode possible)
+     * AI play STEP to the end with multi game simulations (calcs and play best
+     * actions until step ends, can be called in the middle of the step) All
+     * choices must be made by AI (e.g. strict mode possible)
      */
     public void aiPlayStep(int turnNum, PhaseStep step, TestPlayer player) {
         assertAiPlayAndGameCompatible(player);
@@ -1494,7 +1493,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * @param player
      * @param cardName
      * @param targetName for modes you can add "mode=3" before target name,
-     *                   multiple targets can be seperated by ^, not target marks as TestPlayer.NO_TARGET
+     * multiple targets can be seperated by ^, not target marks as
+     * TestPlayer.NO_TARGET
      */
     public void castSpell(int turnNum, PhaseStep step, TestPlayer player, String cardName, String targetName) {
         //Assert.assertNotEquals("", cardName);
@@ -1517,8 +1517,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * @param step
      * @param player
      * @param cardName
-     * @param targetName   for modal spells add the mode to the name e.g.
-     *                     "mode=2SilvercoatLion^mode3=PillarfieldOx"
+     * @param targetName for modal spells add the mode to the name e.g.
+     * "mode=2SilvercoatLion^mode3=PillarfieldOx"
      * @param spellOnStack
      */
     public void castSpell(int turnNum, PhaseStep step, TestPlayer player, String cardName, String targetName, String spellOnStack) {
@@ -1605,7 +1605,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * @param step
      * @param player
      * @param ability
-     * @param targetName   use NO_TARGET if there is no target to set
+     * @param targetName use NO_TARGET if there is no target to set
      * @param spellOnStack
      */
     public void activateAbility(int turnNum, PhaseStep step, TestPlayer player, String ability, String targetName, String spellOnStack) {
@@ -1618,8 +1618,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * @param step
      * @param player
      * @param ability
-     * @param targetName   if not target has to be defined use the constant
-     *                     NO_TARGET
+     * @param targetName if not target has to be defined use the constant
+     * NO_TARGET
      * @param spellOnStack
      * @param clause
      */
@@ -1705,10 +1705,10 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      *
      * @param player
      * @param choice starting with "1" for mode 1, "2" for mode 2 and so on (to
-     *               set multiple modes call the command multiple times). If a spell mode can
-     *               be used only once like Demonic Pact, the value has to be set to the
-     *               number of the remaining modes (e.g. if only 2 are left the number need to
-     *               be 1 or 2).
+     * set multiple modes call the command multiple times). If a spell mode can
+     * be used only once like Demonic Pact, the value has to be set to the
+     * number of the remaining modes (e.g. if only 2 are left the number need to
+     * be 1 or 2).
      */
     public void setModeChoice(TestPlayer player, String choice) {
         player.addModeChoice(choice);
@@ -1719,12 +1719,12 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      *
      * @param player
      * @param target you can add multiple targets by separating them by the "^"
-     *               character e.g. "creatureName1^creatureName2" you can qualify the target
-     *               additional by setcode e.g. "creatureName-M15" you can add [no copy] to
-     *               the end of the target name to prohibit targets that are copied you can
-     *               add [only copy] to the end of the target name to allow only targets that
-     *               are copies. For modal spells use a prefix with the mode number:
-     *               mode=1Lightning Bolt^mode=2Silvercoat Lion
+     * character e.g. "creatureName1^creatureName2" you can qualify the target
+     * additional by setcode e.g. "creatureName-M15" you can add [no copy] to
+     * the end of the target name to prohibit targets that are copied you can
+     * add [only copy] to the end of the target name to allow only targets that
+     * are copies. For modal spells use a prefix with the mode number:
+     * mode=1Lightning Bolt^mode=2Silvercoat Lion
      */
     // TODO: mode options doesn't work here (see BrutalExpulsionTest)
     public void addTarget(TestPlayer player, String target) {
@@ -1743,7 +1743,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
 
     /**
      * @param player
-     * @param target use TestPlayer.TARGET_SKIP to 0 targets selects or to stop "up two xxx" selection
+     * @param target use TestPlayer.TARGET_SKIP to 0 targets selects or to stop
+     * "up two xxx" selection
      * @param amount
      */
     public void addTargetAmount(TestPlayer player, String target, int amount) {
@@ -1758,7 +1759,6 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
         addTargetAmount(player, "targetPlayer=" + targetPlayer.getName(), amount);
     }
 
-
     public void addTargetAmount(TestPlayer player, String target) {
         Assert.assertTrue("Only skip command allows here", target.equals(TestPlayer.TARGET_SKIP));
         addTargetAmount(player, target, 0);
@@ -1793,10 +1793,18 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     }
 
     public void waitStackResolved(int turnNum, PhaseStep step) {
-        if (playerA != null) waitStackResolved(turnNum, step, playerA);
-        if (playerB != null) waitStackResolved(turnNum, step, playerB);
-        if (playerC != null) waitStackResolved(turnNum, step, playerC);
-        if (playerD != null) waitStackResolved(turnNum, step, playerD);
+        if (playerA != null) {
+            waitStackResolved(turnNum, step, playerA);
+        }
+        if (playerB != null) {
+            waitStackResolved(turnNum, step, playerB);
+        }
+        if (playerC != null) {
+            waitStackResolved(turnNum, step, playerC);
+        }
+        if (playerD != null) {
+            waitStackResolved(turnNum, step, playerD);
+        }
     }
 
     private void assertAliaseSupportInActivateCommand(String targetName, boolean methodSupportAliases) {

From df42a4695d21bee77004aa9713369efa7418ebd7 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Sun, 14 Jun 2020 15:53:12 +0200
Subject: [PATCH 262/586] * Added test for  #6635.

---
 .../asthough/PlayFromNonHandZoneTest.java     | 94 +++++++++++++++++++
 1 file changed, 94 insertions(+)

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/asthough/PlayFromNonHandZoneTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/asthough/PlayFromNonHandZoneTest.java
index 11fca407dd..763b589500 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/asthough/PlayFromNonHandZoneTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/asthough/PlayFromNonHandZoneTest.java
@@ -173,4 +173,98 @@ public class PlayFromNonHandZoneTest extends CardTestPlayerBase {
 
     }
 
+    /**
+     * Can't cast Karn's Temporal Sundering when exiled with Golos, Tireless
+     * Pilgrim
+     */
+    @Test
+    public void castSunderingWithGolosTest() {
+        // When Golos, Tireless Pilgrim enters the battlefield, you may search your library for a land card, put that card onto the battlefield tapped, then shuffle your library.
+        // {2}{W}{U}{B}{R}{G}: Exile the top three cards of your library. You may play them this turn without paying their mana costs.
+        addCard(Zone.BATTLEFIELD, playerA, "Golos, Tireless Pilgrim", 1);
+
+        // (You may cast a legendary sorcery only if you control a legendary creature or planeswalker.)
+        // Target player takes an extra turn after this one. Return up to one target nonland permanent to its owner's hand. Exile Karn's Temporal Sundering.
+        addCard(Zone.LIBRARY, playerA, "Karn's Temporal Sundering"); // Sorcery
+        addCard(Zone.LIBRARY, playerA, "Silvercoat Lion");
+        addCard(Zone.LIBRARY, playerA, "Mountain");
+        skipInitShuffling();
+
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 3);
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
+
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{W}{U}{B}{R}{G}: Exile");
+
+        playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mountain");
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Silvercoat Lion");
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Karn's Temporal Sundering");
+        addTarget(playerA, playerA);
+        addTarget(playerA, "Silvercoat Lion");
+
+        setStopAt(2, PhaseStep.PRECOMBAT_MAIN);
+
+        setStrictChooseMode(true);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerA, "Mountain", 2);
+        assertPermanentCount(playerA, "Silvercoat Lion", 0);
+        assertExileCount(playerA, "Karn's Temporal Sundering", 1);
+
+        assertHandCount(playerA, "Silvercoat Lion", 1);
+        assertExileCount(playerA, 1);
+
+        assertActivePlayer(playerA);
+    }
+
+    /**
+     * Can't cast Karn's Temporal Sundering when exiled with Golos, Tireless
+     * Pilgrim
+     */
+    @Test
+    public void castSunderingWithGolos2Test() {
+        // When Golos, Tireless Pilgrim enters the battlefield, you may search your library for a land card, put that card onto the battlefield tapped, then shuffle your library.
+        // {2}{W}{U}{B}{R}{G}: Exile the top three cards of your library. You may play them this turn without paying their mana costs.
+        addCard(Zone.BATTLEFIELD, playerA, "Golos, Tireless Pilgrim", 1);
+
+        // (You may cast a legendary sorcery only if you control a legendary creature or planeswalker.)
+        // Target player takes an extra turn after this one. Return up to one target nonland permanent to its owner's hand. Exile Karn's Temporal Sundering.
+        addCard(Zone.LIBRARY, playerA, "Karn's Temporal Sundering"); // Sorcery
+        addCard(Zone.LIBRARY, playerA, "Silvercoat Lion");
+        addCard(Zone.LIBRARY, playerA, "Mountain");
+        skipInitShuffling();
+
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 3);
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
+
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{W}{U}{B}{R}{G}: Exile");
+
+        playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mountain");
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Silvercoat Lion");
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Karn's Temporal Sundering");
+        addTarget(playerA, playerA);
+        addTarget(playerA, "Golos, Tireless Pilgrim"); // Return to hand
+
+        setStopAt(2, PhaseStep.PRECOMBAT_MAIN);
+
+        setStrictChooseMode(true);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerA, "Mountain", 2);
+        assertPermanentCount(playerA, "Silvercoat Lion", 1);
+        assertExileCount(playerA, "Karn's Temporal Sundering", 1);
+
+        assertHandCount(playerA, "Golos, Tireless Pilgrim", 1);
+        assertExileCount(playerA, 1);
+
+        assertActivePlayer(playerA);
+    }
+
 }

From 3c43e544b0035a993ce31080cace95e76e5c9d35 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Sun, 14 Jun 2020 16:29:05 +0200
Subject: [PATCH 263/586] * Fixed a bug that for mana abilities with multiple
 net mana entries wrongly no available mana was calculated (fixes #6617).

---
 Mage/src/main/java/mage/abilities/mana/ManaOptions.java | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/Mage/src/main/java/mage/abilities/mana/ManaOptions.java b/Mage/src/main/java/mage/abilities/mana/ManaOptions.java
index bfd32e4747..b77870e02c 100644
--- a/Mage/src/main/java/mage/abilities/mana/ManaOptions.java
+++ b/Mage/src/main/java/mage/abilities/mana/ManaOptions.java
@@ -50,10 +50,11 @@ public class ManaOptions extends ArrayList<Mana> {
                 } else {
                     List<Mana> copy = copy();
                     this.clear();
-                    boolean hasTapCost = hasTapCost(abilities.get(0));
+//                    boolean hasTapCost = hasTapCost(abilities.get(0)); // needed if checkTappedForManaReplacement is reactivated
                     for (Mana netMana : netManas) {
                         for (Mana mana : copy) {
-                            if (!hasTapCost /* || checkTappedForManaReplacement(abilities.get(0), game, netMana) */) { // Seems to produce endless iterations so deactivated for now:  https://github.com/magefree/mage/issues/5023
+                            // checkTappedForManaReplacement seems in some situations to produce endless iterations so deactivated for now:  https://github.com/magefree/mage/issues/5023
+                            if (true/* !hasTapCost || checkTappedForManaReplacement(abilities.get(0), game, netMana) */) {
                                 Mana newMana = new Mana();
                                 newMana.add(mana);
                                 newMana.add(netMana);

From 65983623b45e90a838a7f6087e0df3db421fe4cf Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sun, 14 Jun 2020 10:28:38 -0400
Subject: [PATCH 264/586] Implemented Discontinuity

---
 Mage.Sets/src/mage/cards/d/Discontinuity.java | 40 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 41 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/d/Discontinuity.java

diff --git a/Mage.Sets/src/mage/cards/d/Discontinuity.java b/Mage.Sets/src/mage/cards/d/Discontinuity.java
new file mode 100644
index 0000000000..93e62861f1
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/d/Discontinuity.java
@@ -0,0 +1,40 @@
+package mage.cards.d;
+
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.condition.common.MyTurnCondition;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.effects.common.EndTurnEffect;
+import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Zone;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class Discontinuity extends CardImpl {
+
+    public Discontinuity(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{U}{U}{U}");
+
+        // As long as it's your turn, this spell costs {2}{U}{U} less to cast.
+        this.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(
+                new ManaCostsImpl("{2}{U}{U}"), MyTurnCondition.instance
+        ).setText("as long as it's your turn, this spell costs {2}{U}{U} less to cast")));
+
+        // End the turn.
+        this.getSpellAbility().addEffect(new EndTurnEffect());
+    }
+
+    private Discontinuity(final Discontinuity card) {
+        super(card);
+    }
+
+    @Override
+    public Discontinuity copy() {
+        return new Discontinuity(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 3d5a2c80a4..5c08b29540 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -62,6 +62,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Cultivate", 177, Rarity.UNCOMMON, mage.cards.c.Cultivate.class));
         cards.add(new SetCardInfo("Defiant Strike", 15, Rarity.COMMON, mage.cards.d.DefiantStrike.class));
         cards.add(new SetCardInfo("Dire Fleet Warmonger", 217, Rarity.UNCOMMON, mage.cards.d.DireFleetWarmonger.class));
+        cards.add(new SetCardInfo("Discontinuity", 48, Rarity.MYTHIC, mage.cards.d.Discontinuity.class));
         cards.add(new SetCardInfo("Double Vision", 142, Rarity.RARE, mage.cards.d.DoubleVision.class));
         cards.add(new SetCardInfo("Drowsing Tyrannodon", 178, Rarity.COMMON, mage.cards.d.DrowsingTyrannodon.class));
         cards.add(new SetCardInfo("Duress", 96, Rarity.COMMON, mage.cards.d.Duress.class));

From 2c6859c60a9069c93389699c7200e1e0481f2482 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sun, 14 Jun 2020 10:32:59 -0400
Subject: [PATCH 265/586] Implemented Frantic Inventory

---
 .../src/mage/cards/f/FranticInventory.java    | 44 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 45 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/f/FranticInventory.java

diff --git a/Mage.Sets/src/mage/cards/f/FranticInventory.java b/Mage.Sets/src/mage/cards/f/FranticInventory.java
new file mode 100644
index 0000000000..7ab40c6030
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/f/FranticInventory.java
@@ -0,0 +1,44 @@
+package mage.cards.f;
+
+import mage.abilities.dynamicvalue.DynamicValue;
+import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount;
+import mage.abilities.effects.common.DrawCardSourceControllerEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.filter.FilterCard;
+import mage.filter.predicate.mageobject.NamePredicate;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class FranticInventory extends CardImpl {
+
+    private static final FilterCard filter = new FilterCard();
+
+    static {
+        filter.add(new NamePredicate("Frantic Inventory"));
+    }
+
+    private static final DynamicValue xValue = new CardsInControllerGraveyardCount(filter);
+
+    public FranticInventory(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}");
+
+        // Draw a card, then draw cards equal to the number of cards named Frantic Inventory in your graveyard.
+        this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1));
+        this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(xValue)
+                .setText(", then draw cards equal to the number of cards named Frantic Inventory in your graveyard"));
+    }
+
+    private FranticInventory(final FranticInventory card) {
+        super(card);
+    }
+
+    @Override
+    public FranticInventory copy() {
+        return new FranticInventory(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 5c08b29540..3f6db8c387 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -74,6 +74,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Fierce Empath", 181, Rarity.UNCOMMON, mage.cards.f.FierceEmpath.class));
         cards.add(new SetCardInfo("Fiery Emancipation", 143, Rarity.MYTHIC, mage.cards.f.FieryEmancipation.class));
         cards.add(new SetCardInfo("Finishing Blow", 99, Rarity.COMMON, mage.cards.f.FinishingBlow.class));
+        cards.add(new SetCardInfo("Frantic Inventory", 50, Rarity.COMMON, mage.cards.f.FranticInventory.class));
         cards.add(new SetCardInfo("Fungal Rebirth", 182, Rarity.UNCOMMON, mage.cards.f.FungalRebirth.class));
         cards.add(new SetCardInfo("Furious Rise", 144, Rarity.UNCOMMON, mage.cards.f.FuriousRise.class));
         cards.add(new SetCardInfo("Gadrak, the Crown-Scourge", 146, Rarity.RARE, mage.cards.g.GadrakTheCrownScourge.class));

From fb287c3e0473c2abe7e40058913f41f50a70f4dd Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sun, 14 Jun 2020 10:36:14 -0400
Subject: [PATCH 266/586] Implemented Goremand

---
 Mage.Sets/src/mage/cards/g/Goremand.java | 55 ++++++++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java |  1 +
 2 files changed, 56 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/g/Goremand.java

diff --git a/Mage.Sets/src/mage/cards/g/Goremand.java b/Mage.Sets/src/mage/cards/g/Goremand.java
new file mode 100644
index 0000000000..89a708bb09
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/g/Goremand.java
@@ -0,0 +1,55 @@
+package mage.cards.g;
+
+import mage.MageInt;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.costs.common.SacrificeTargetCost;
+import mage.abilities.effects.common.SacrificeOpponentsEffect;
+import mage.abilities.keyword.FlyingAbility;
+import mage.abilities.keyword.TrampleAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.filter.StaticFilters;
+import mage.target.common.TargetControlledPermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class Goremand extends CardImpl {
+
+    public Goremand(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}{B}");
+
+        this.subtype.add(SubType.DEMON);
+        this.power = new MageInt(5);
+        this.toughness = new MageInt(5);
+
+        // As an additional cost to cast this spell, sacrifice a creature.
+        this.getSpellAbility().addCost(new SacrificeTargetCost(
+                new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT)
+        ));
+
+        // Flying
+        this.addAbility(FlyingAbility.getInstance());
+
+        // Trample
+        this.addAbility(TrampleAbility.getInstance());
+
+        // When Goremand enters the battlefield, each opponent sacrifices a creature.
+        this.addAbility(new EntersBattlefieldTriggeredAbility(
+                new SacrificeOpponentsEffect(StaticFilters.FILTER_PERMANENT_A_CREATURE)
+        ));
+    }
+
+    private Goremand(final Goremand card) {
+        super(card);
+    }
+
+    @Override
+    public Goremand copy() {
+        return new Goremand(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 3f6db8c387..c9c8384b7e 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -82,6 +82,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Garruk's Warsteed", 337, Rarity.RARE, mage.cards.g.GarruksWarsteed.class));
         cards.add(new SetCardInfo("Garruk, Savage Herald", 336, Rarity.MYTHIC, mage.cards.g.GarrukSavageHerald.class));
         cards.add(new SetCardInfo("Goblin Arsonist", 147, Rarity.COMMON, mage.cards.g.GoblinArsonist.class));
+        cards.add(new SetCardInfo("Goremand", 101, Rarity.UNCOMMON, mage.cards.g.Goremand.class));
         cards.add(new SetCardInfo("Grasp of Darkness", 102, Rarity.COMMON, mage.cards.g.GraspOfDarkness.class));
         cards.add(new SetCardInfo("Griffin Aerie", 22, Rarity.UNCOMMON, mage.cards.g.GriffinAerie.class));
         cards.add(new SetCardInfo("Grim Tutor", 103, Rarity.MYTHIC, mage.cards.g.GrimTutor.class));

From 6e0eda168e366418c462c5d18dfd3fa06e0db777 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sun, 14 Jun 2020 10:49:19 -0400
Subject: [PATCH 267/586] Implemented Liliana's Standard Bearer

---
 .../mage/cards/l/LilianasStandardBearer.java  | 105 ++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |   1 +
 2 files changed, 106 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/l/LilianasStandardBearer.java

diff --git a/Mage.Sets/src/mage/cards/l/LilianasStandardBearer.java b/Mage.Sets/src/mage/cards/l/LilianasStandardBearer.java
new file mode 100644
index 0000000000..791b089bba
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/l/LilianasStandardBearer.java
@@ -0,0 +1,105 @@
+package mage.cards.l;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.dynamicvalue.DynamicValue;
+import mage.abilities.effects.Effect;
+import mage.abilities.effects.common.DrawCardSourceControllerEffect;
+import mage.abilities.keyword.FlashAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.WatcherScope;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+import mage.game.events.ZoneChangeEvent;
+import mage.watchers.Watcher;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class LilianasStandardBearer extends CardImpl {
+
+    public LilianasStandardBearer(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}");
+
+        this.subtype.add(SubType.ZOMBIE);
+        this.subtype.add(SubType.KNIGHT);
+        this.power = new MageInt(3);
+        this.toughness = new MageInt(1);
+
+        // Flash
+        this.addAbility(FlashAbility.getInstance());
+
+        // When Liliana's Standard Bearer enters the battlefield, draw X cards, where X is the number of creatures that died under your control this turn.
+        this.addAbility(new EntersBattlefieldTriggeredAbility(
+                new DrawCardSourceControllerEffect(LilianasStandardBearerCount.instance)
+                        .setText("draw X cards, where X is the number of creatures that died under your control this turn")
+        ), new LilianasStandardBearerWatcher());
+    }
+
+    private LilianasStandardBearer(final LilianasStandardBearer card) {
+        super(card);
+    }
+
+    @Override
+    public LilianasStandardBearer copy() {
+        return new LilianasStandardBearer(this);
+    }
+}
+
+enum LilianasStandardBearerCount implements DynamicValue {
+    instance;
+
+    @Override
+    public int calculate(Game game, Ability sourceAbility, Effect effect) {
+        LilianasStandardBearerWatcher watcher = game.getState().getWatcher(LilianasStandardBearerWatcher.class);
+        return watcher == null ? 0 : watcher.getCount(sourceAbility.getControllerId());
+    }
+
+    @Override
+    public LilianasStandardBearerCount copy() {
+        return instance;
+    }
+
+    @Override
+    public String getMessage() {
+        return "";
+    }
+}
+
+class LilianasStandardBearerWatcher extends Watcher {
+
+    private final Map<UUID, Integer> playerMap = new HashMap<>();
+
+    LilianasStandardBearerWatcher() {
+        super(WatcherScope.GAME);
+    }
+
+    @Override
+    public void watch(GameEvent event, Game game) {
+        if (event.getType() != GameEvent.EventType.ZONE_CHANGE) {
+            return;
+        }
+        ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
+        if (zEvent.isDiesEvent() && zEvent.getTarget().isCreature()) {
+            playerMap.compute(zEvent.getTarget().getControllerId(), (u, i) -> i == null ? 1 : Integer.sum(i, 1));
+        }
+    }
+
+    @Override
+    public void reset() {
+        playerMap.clear();
+        super.reset();
+    }
+
+    int getCount(UUID playerId) {
+        return playerMap.getOrDefault(playerId, 0);
+    }
+}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index c9c8384b7e..e04a19f00c 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -103,6 +103,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Liliana's Devotee", 109, Rarity.UNCOMMON, mage.cards.l.LilianasDevotee.class));
         cards.add(new SetCardInfo("Liliana's Scorn", 329, Rarity.RARE, mage.cards.l.LilianasScorn.class));
         cards.add(new SetCardInfo("Liliana's Scrounger", 330, Rarity.UNCOMMON, mage.cards.l.LilianasScrounger.class));
+        cards.add(new SetCardInfo("Liliana's Standard Bearer", 110, Rarity.RARE, mage.cards.l.LilianasStandardBearer.class));
         cards.add(new SetCardInfo("Liliana's Steward", 111, Rarity.COMMON, mage.cards.l.LilianasSteward.class));
         cards.add(new SetCardInfo("Liliana, Death Mage", 328, Rarity.MYTHIC, mage.cards.l.LilianaDeathMage.class));
         cards.add(new SetCardInfo("Liliana, Waker of the Dead", 108, Rarity.MYTHIC, mage.cards.l.LilianaWakerOfTheDead.class));

From 4ac562a29ed9abe05bda9cef6df811a4752f19e4 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sun, 14 Jun 2020 10:51:34 -0400
Subject: [PATCH 268/586] Implemented Rousing Read

---
 Mage.Sets/src/mage/cards/r/RousingRead.java | 58 +++++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java    |  1 +
 2 files changed, 59 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/r/RousingRead.java

diff --git a/Mage.Sets/src/mage/cards/r/RousingRead.java b/Mage.Sets/src/mage/cards/r/RousingRead.java
new file mode 100644
index 0000000000..80646faf20
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/r/RousingRead.java
@@ -0,0 +1,58 @@
+package mage.cards.r;
+
+import mage.abilities.Ability;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.effects.common.AttachEffect;
+import mage.abilities.effects.common.DrawDiscardControllerEffect;
+import mage.abilities.effects.common.continuous.BoostEnchantedEffect;
+import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect;
+import mage.abilities.keyword.EnchantAbility;
+import mage.abilities.keyword.FlyingAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.*;
+import mage.target.TargetPermanent;
+import mage.target.common.TargetCreaturePermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class RousingRead extends CardImpl {
+
+    public RousingRead(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}");
+
+        this.subtype.add(SubType.AURA);
+
+        // Enchant creature
+        TargetPermanent auraTarget = new TargetCreaturePermanent();
+        this.getSpellAbility().addTarget(auraTarget);
+        this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature));
+        Ability ability = new EnchantAbility(auraTarget.getTargetName());
+        this.addAbility(ability);
+
+        // When Rousing Read enters the battlefield, draw two cards, then discard a card.
+        this.addAbility(new EntersBattlefieldTriggeredAbility(
+                new DrawDiscardControllerEffect(2, 1)
+        ));
+
+        // Enchanted creature gets +1/+1 and has flying.
+        ability = new SimpleStaticAbility(new BoostEnchantedEffect(1, 1, Duration.WhileOnBattlefield));
+        ability.addEffect(new GainAbilityAttachedEffect(
+                FlyingAbility.getInstance(), AttachmentType.AURA
+        ).setText("and has flying"));
+        this.addAbility(ability);
+    }
+
+    private RousingRead(final RousingRead card) {
+        super(card);
+    }
+
+    @Override
+    public RousingRead copy() {
+        return new RousingRead(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index e04a19f00c..ee2e75ad05 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -138,6 +138,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Rewind", 63, Rarity.UNCOMMON, mage.cards.r.Rewind.class));
         cards.add(new SetCardInfo("Riddleform", 64, Rarity.UNCOMMON, mage.cards.r.Riddleform.class));
         cards.add(new SetCardInfo("Rin and Seri, Inseparable", 278, Rarity.MYTHIC, mage.cards.r.RinAndSeriInseparable.class));
+        cards.add(new SetCardInfo("Rousing Read", 67, Rarity.COMMON, mage.cards.r.RousingRead.class));
         cards.add(new SetCardInfo("Runed Halo", 32, Rarity.RARE, mage.cards.r.RunedHalo.class));
         cards.add(new SetCardInfo("Sabertooth Mauler", 202, Rarity.COMMON, mage.cards.s.SabertoothMauler.class));
         cards.add(new SetCardInfo("Scavenging Ooze", 204, Rarity.RARE, mage.cards.s.ScavengingOoze.class));

From 40c508afcf8bad1b1c367db40a4187cbf64466d9 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sun, 14 Jun 2020 11:05:32 -0400
Subject: [PATCH 269/586] Implemented Speaker of the Heavens

---
 .../src/mage/cards/s/SpeakerOfTheHeavens.java | 79 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 80 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/s/SpeakerOfTheHeavens.java

diff --git a/Mage.Sets/src/mage/cards/s/SpeakerOfTheHeavens.java b/Mage.Sets/src/mage/cards/s/SpeakerOfTheHeavens.java
new file mode 100644
index 0000000000..b269c73a90
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/SpeakerOfTheHeavens.java
@@ -0,0 +1,79 @@
+package mage.cards.s;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.condition.Condition;
+import mage.abilities.condition.common.MainPhaseStackEmptyCondition;
+import mage.abilities.costs.common.TapSourceCost;
+import mage.abilities.decorator.ConditionalActivatedAbility;
+import mage.abilities.effects.common.CreateTokenEffect;
+import mage.abilities.keyword.LifelinkAbility;
+import mage.abilities.keyword.VigilanceAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.Zone;
+import mage.game.Game;
+import mage.game.permanent.token.AngelToken;
+import mage.players.Player;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class SpeakerOfTheHeavens extends CardImpl {
+
+    public SpeakerOfTheHeavens(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}");
+
+        this.subtype.add(SubType.HUMAN);
+        this.subtype.add(SubType.CLERIC);
+        this.power = new MageInt(1);
+        this.toughness = new MageInt(1);
+
+        // Vigilance
+        this.addAbility(VigilanceAbility.getInstance());
+
+        // Lifelink
+        this.addAbility(LifelinkAbility.getInstance());
+
+        // {T}: Create a 4/4 white Angel creature token with flying. Activate this ability only if you have at least 7 more life than your starting life total and only any time you could cast a sorcery.
+        this.addAbility(new ConditionalActivatedAbility(
+                Zone.BATTLEFIELD, new CreateTokenEffect(new AngelToken()),
+                new TapSourceCost(), SpeakerOfTheHeavensCondition.instance
+        ));
+    }
+
+    private SpeakerOfTheHeavens(final SpeakerOfTheHeavens card) {
+        super(card);
+    }
+
+    @Override
+    public SpeakerOfTheHeavens copy() {
+        return new SpeakerOfTheHeavens(this);
+    }
+}
+
+enum SpeakerOfTheHeavensCondition implements Condition {
+    instance;
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        if (!MainPhaseStackEmptyCondition.instance.apply(game, source)
+                || !game.isActivePlayer(source.getControllerId())) {
+            return false;
+        }
+        Player player = game.getPlayer(source.getControllerId());
+        if (player == null || player.getLife() < game.getLife() + 7) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "if you have at least 7 more life than your starting life total and only any time you could cast a sorcery";
+    }
+}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index ee2e75ad05..af62a6c5dd 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -152,6 +152,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Solemn Simulacrum", 239, Rarity.RARE, mage.cards.s.SolemnSimulacrum.class));
         cards.add(new SetCardInfo("Soul Sear", 160, Rarity.UNCOMMON, mage.cards.s.SoulSear.class));
         cards.add(new SetCardInfo("Sparkhunter Masticore", 240, Rarity.RARE, mage.cards.s.SparkhunterMasticore.class));
+        cards.add(new SetCardInfo("Speaker of the Heavens", 38, Rarity.RARE, mage.cards.s.SpeakerOfTheHeavens.class));
         cards.add(new SetCardInfo("Spined Megalodon", 72, Rarity.COMMON, mage.cards.s.SpinedMegalodon.class));
         cards.add(new SetCardInfo("Spirit of Malevolence", 331, Rarity.COMMON, mage.cards.s.SpiritOfMalevolence.class));
         cards.add(new SetCardInfo("Sporeweb Weaver", 208, Rarity.RARE, mage.cards.s.SporewebWeaver.class));

From 0571154d525f5250ecbd102547bf1fa67c78fe6c Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sun, 14 Jun 2020 11:16:15 -0400
Subject: [PATCH 270/586] Implemented Celestial Enforcer

---
 .../src/mage/cards/c/CelestialEnforcer.java   | 63 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 64 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/c/CelestialEnforcer.java

diff --git a/Mage.Sets/src/mage/cards/c/CelestialEnforcer.java b/Mage.Sets/src/mage/cards/c/CelestialEnforcer.java
new file mode 100644
index 0000000000..5bf8ffdea1
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/c/CelestialEnforcer.java
@@ -0,0 +1,63 @@
+package mage.cards.c;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.ActivateIfConditionActivatedAbility;
+import mage.abilities.condition.Condition;
+import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
+import mage.abilities.costs.common.TapSourceCost;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.effects.common.TapTargetEffect;
+import mage.abilities.keyword.FlyingAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.Zone;
+import mage.filter.FilterPermanent;
+import mage.filter.common.FilterCreaturePermanent;
+import mage.filter.predicate.mageobject.AbilityPredicate;
+import mage.target.common.TargetCreaturePermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class CelestialEnforcer extends CardImpl {
+
+    private static final FilterPermanent filter
+            = new FilterCreaturePermanent("if you control a creature with flying");
+
+    static {
+        filter.add(new AbilityPredicate(FlyingAbility.class));
+    }
+
+    private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter);
+
+    public CelestialEnforcer(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}");
+
+        this.subtype.add(SubType.HUMAN);
+        this.subtype.add(SubType.CLERIC);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(3);
+
+        // {1}{W}, {T}: Tap target creature. Activate this ability only if you control a creature with flying.
+        Ability ability = new ActivateIfConditionActivatedAbility(
+                Zone.BATTLEFIELD, new TapTargetEffect(), new ManaCostsImpl("{1}{W}"), condition
+        );
+        ability.addCost(new TapSourceCost());
+        ability.addTarget(new TargetCreaturePermanent());
+        this.addAbility(ability);
+    }
+
+    private CelestialEnforcer(final CelestialEnforcer card) {
+        super(card);
+    }
+
+    @Override
+    public CelestialEnforcer copy() {
+        return new CelestialEnforcer(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index af62a6c5dd..c51a66f51d 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -51,6 +51,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Bolt Hound", 131, Rarity.UNCOMMON, mage.cards.b.BoltHound.class));
         cards.add(new SetCardInfo("Burlfist Oak", 174, Rarity.UNCOMMON, mage.cards.b.BurlfistOak.class));
         cards.add(new SetCardInfo("Carrion Grub", 92, Rarity.UNCOMMON, mage.cards.c.CarrionGrub.class));
+        cards.add(new SetCardInfo("Celestial Enforcer", 11, Rarity.COMMON, mage.cards.c.CelestialEnforcer.class));
         cards.add(new SetCardInfo("Chandra's Firemaw", 333, Rarity.RARE, mage.cards.c.ChandrasFiremaw.class));
         cards.add(new SetCardInfo("Chandra's Incinerator", 136, Rarity.RARE, mage.cards.c.ChandrasIncinerator.class));
         cards.add(new SetCardInfo("Chandra's Magmutt", 137, Rarity.COMMON, mage.cards.c.ChandrasMagmutt.class));

From d88a60b63d14fe0f6293065707d4d79afdc493fc Mon Sep 17 00:00:00 2001
From: Tim Haering <tim.haering@gmail.com>
Date: Wed, 27 May 2020 18:10:19 +0200
Subject: [PATCH 271/586] copy chainer triggered ability when used as commander

---
 Mage.Sets/src/mage/cards/c/ChainerNightmareAdept.java | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/Mage.Sets/src/mage/cards/c/ChainerNightmareAdept.java b/Mage.Sets/src/mage/cards/c/ChainerNightmareAdept.java
index 86a5d53c4f..a2d1e11dfb 100644
--- a/Mage.Sets/src/mage/cards/c/ChainerNightmareAdept.java
+++ b/Mage.Sets/src/mage/cards/c/ChainerNightmareAdept.java
@@ -177,6 +177,15 @@ class ChainerNightmareAdeptTriggeredAbility extends EntersBattlefieldAllTriggere
         super(Zone.BATTLEFIELD, gainHasteUntilNextTurnEffect, filter, false, SetTargetPointer.PERMANENT, abilityText);
     }
 
+    ChainerNightmareAdeptTriggeredAbility(final ChainerNightmareAdeptTriggeredAbility effect) {
+        super(effect);
+    }
+
+    @Override
+    public ChainerNightmareAdeptTriggeredAbility copy() {
+        return new ChainerNightmareAdeptTriggeredAbility(this);
+    }
+
     @Override
     public boolean checkTrigger(GameEvent event, Game game) {
         if (!super.checkTrigger(event, game)) {

From b5127fdce2946e0fd5006b9f9d75c226d6b24378 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Sun, 14 Jun 2020 19:22:17 +0200
Subject: [PATCH 272/586] * Icon of Ancestry - some fixes to PR#6509 (fixes
 #6333).

---
 .../src/mage/cards/i/IconOfAncestry.java      | 26 ++++++++++++++-----
 1 file changed, 19 insertions(+), 7 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/i/IconOfAncestry.java b/Mage.Sets/src/mage/cards/i/IconOfAncestry.java
index 19196fd230..a65420d527 100644
--- a/Mage.Sets/src/mage/cards/i/IconOfAncestry.java
+++ b/Mage.Sets/src/mage/cards/i/IconOfAncestry.java
@@ -1,10 +1,13 @@
 package mage.cards.i;
 
+import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.common.AsEntersBattlefieldAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.costs.common.TapSourceCost;
+import mage.abilities.costs.mana.GenericManaCost;
+import mage.abilities.dynamicvalue.common.StaticValue;
 import mage.abilities.effects.common.ChooseCreatureTypeEffect;
 import mage.abilities.effects.common.LookLibraryAndPickControllerEffect;
 import mage.abilities.effects.common.continuous.BoostAllEffect;
@@ -14,10 +17,6 @@ import mage.constants.*;
 import mage.filter.common.FilterCreatureCard;
 import mage.filter.common.FilterCreaturePermanent;
 import mage.filter.predicate.mageobject.ChosenSubtypePredicate;
-
-import java.util.UUID;
-import mage.abilities.costs.mana.ManaCostsImpl;
-import mage.abilities.dynamicvalue.common.StaticValue;
 import mage.game.Game;
 
 /**
@@ -44,10 +43,10 @@ public final class IconOfAncestry extends CardImpl {
                 new BoostAllEffect(1, 1, Duration.WhileOnBattlefield, filter, false)
         ));
 
-        // {3}, {T}: Look at the top three cards of your library. You may reveal a creature card of the 
+        // {3}, {T}: Look at the top three cards of your library. You may reveal a creature card of the
         // chosen type from among them and put it into your hand. Put the rest on the bottom of your library in a random order.
         FilterCreatureCard filter2 = new FilterCreatureCard("creature card that matches the chosen subtype");
-        Ability ability = new SimpleActivatedAbility(new IconOfAncestryEffect(filter2), new ManaCostsImpl("{3}"));
+        Ability ability = new SimpleActivatedAbility(new IconOfAncestryEffect(filter2), new GenericManaCost(3));
         ability.addCost(new TapSourceCost());
         this.addAbility(ability);
     }
@@ -63,6 +62,7 @@ public final class IconOfAncestry extends CardImpl {
 }
 
 class IconOfAncestryEffect extends LookLibraryAndPickControllerEffect {
+
     public IconOfAncestryEffect(FilterCreatureCard filter) {
         super(StaticValue.get(3), false, StaticValue.get(1), filter, Zone.LIBRARY, false,
                 true, false, Zone.HAND, true, false, false);
@@ -75,7 +75,19 @@ class IconOfAncestryEffect extends LookLibraryAndPickControllerEffect {
         SubType subtype = (SubType) game.getState().getValue(source.getSourceId() + "_type");
         if (subtype != null) {
             filter.add(subtype.getPredicate());
+            filter.setMessage("creature card of subtype " + subtype.toString());
+        } else {
+            return false;
         }
         return super.apply(game, source);
     }
-}
\ No newline at end of file
+
+    public IconOfAncestryEffect(final IconOfAncestryEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public IconOfAncestryEffect copy() {
+        return new IconOfAncestryEffect(this);
+    }
+}

From f9479993e2523de8626fb240077fd7f7c2525ea8 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Sun, 14 Jun 2020 19:41:18 +0200
Subject: [PATCH 273/586] * UntapLandsEffect - Changed filter message.

---
 .../java/mage/abilities/effects/common/UntapLandsEffect.java    | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Mage/src/main/java/mage/abilities/effects/common/UntapLandsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/UntapLandsEffect.java
index 490973b6d0..c5a4cb6f28 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/UntapLandsEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/UntapLandsEffect.java
@@ -17,7 +17,7 @@ import mage.util.CardUtil;
  */
 public class UntapLandsEffect extends OneShotEffect {
 
-    private static final FilterLandPermanent filter = new FilterLandPermanent("tapped lands");
+    private static final FilterLandPermanent filter = new FilterLandPermanent("land(s) to untap");
 
     static {
         filter.add(TappedPredicate.instance);

From c5fab9e8f6b099bed5ad06e5c75e7157f2e0c3b6 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sun, 14 Jun 2020 21:47:50 -0400
Subject: [PATCH 274/586] updated M21 spoiler

---
 Mage.Sets/src/mage/sets/CoreSet2021.java | 2 +-
 Utils/mtg-cards-data.txt                 | 2 ++
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index c51a66f51d..96b4ddf578 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -182,9 +182,9 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Valorous Steed", 42, Rarity.COMMON, mage.cards.v.ValorousSteed.class));
         cards.add(new SetCardInfo("Village Rites", 126, Rarity.COMMON, mage.cards.v.VillageRites.class));
         cards.add(new SetCardInfo("Vito, Thorn of the Dusk Rose", 127, Rarity.RARE, mage.cards.v.VitoThornOfTheDuskRose.class));
-        cards.add(new SetCardInfo("Waker of Waves", 84, Rarity.UNCOMMON, mage.cards.w.WakerOfWaves.class));
         cards.add(new SetCardInfo("Volcanic Salvo", 172, Rarity.RARE, mage.cards.v.VolcanicSalvo.class));
         cards.add(new SetCardInfo("Vryn Wingmare", 43, Rarity.UNCOMMON, mage.cards.v.VrynWingmare.class));
+        cards.add(new SetCardInfo("Waker of Waves", 84, Rarity.UNCOMMON, mage.cards.w.WakerOfWaves.class));
         cards.add(new SetCardInfo("Warden of the Woods", 213, Rarity.UNCOMMON, mage.cards.w.WardenOfTheWoods.class));
         cards.add(new SetCardInfo("Wildwood Patrol", 339, Rarity.COMMON, mage.cards.w.WildwoodPatrol.class));
         cards.add(new SetCardInfo("Wildwood Scourge", 214, Rarity.UNCOMMON, mage.cards.w.WildwoodScourge.class));
diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt
index 04a90594e8..b422f3ae6a 100644
--- a/Utils/mtg-cards-data.txt
+++ b/Utils/mtg-cards-data.txt
@@ -37481,6 +37481,7 @@ See the Truth|Core Set 2021|69|R|{1}{U}|Sorcery|||Look at the top three cards of
 Shipwreck Dowser|Core Set 2021|71|U|{3}{U}{U}|Creature - Merfolk Wizard|3|3|Prowess$When Shipwreck Dowser enters the battlefield, return target instant or sorcery card from your graveyard to your hand.|
 Spined Megalodon|Core Set 2021|72|C|{5}{U}{U}|Creature - Shark|5|7|Hexproof$Whenever Spined Megalodon attacks, scry 1.|
 Stormwing Entity|Core Set 2021|73|R|{3}{U}{U}|Creature - Elemental|3|3|This spell costs {2}{U} less to cast if you've cast an instant or sorcery spell this turn.$Flying$Prowess$When Stormwing Entity enters the battlefield, scry 2.|
+Sublime Epiphany|Core Set 2021|74|R|{4}{U}{U}|Instant|||Choose one or more —$• Counter target spell$• Counter target activated or triggered ability.$• Return target nonland permanent to its owner's hand.$• Create a token that's a copy of target creature you control.$• Target player draws a card.|
 Teferi, Master of Time|Core Set 2021|75|M|{2}{U}{U}|Legendary Planeswalker - Teferi|3|You may activate loyalty abilities of Teferi, Master of Time on any player's turn any time you could cast an instant.$+1: Draw a card, then discard a card.$−3: Target creature you don't control phases out.$−10: Take two extra turns after this one.|
 Teferi's Ageless Insight|Core Set 2021|76|R|{2}{U}{U}|Legendary Enchantment|||If you would draw a card except the first one you draw in each of your draw steps, draw two cards instead.|
 Teferi's Protege|Core Set 2021|77|C|{2}{U}|Creature - Human Wizard|2|3|{1}{U}, {T}: Draw a card, then discard a card.|
@@ -37572,6 +37573,7 @@ Leafkin Avenger|Core Set 2021|220|U|{2}{R}{G}|Creature - Elemental Druid|4|3|{T}
 Lorescale Coatl|Core Set 2021|221|U|{1}{G}{U}|Creature - Snake|2|2|Whenever you draw a card, you may put a +1/+1 counter on Lorescale Coatl.|
 Niambi, Esteemed Speaker|Core Set 2021|222|R|{W}{U}|Legendary Creature - Human Cleric|2|1|Flash$When Niambi, Esteemed Speaker enters the battlefield, you may return another target creature you control to its owner's hand. If you do, you gain life equal to that creature's converted mana cost.${1}{W}{U}, {T}, Discard a legendary card: Draw two cards.|
 Obsessive Stitcher|Core Set 2021|223|U|{1}{U}{B}|Creature - Human Wizard|0|3|{T}: Draw a card, then discard a card.${2}{U}{B}, {T}, Sacrifice Obsessive Stitcher: Return target creature card from your graveyard to the battlefield.|
+Radha, Heart of Keld|Core Set 2021|224|R|{1}{R}{G}|Legendary Creature - Elf Warrior|3|3|As long as it's your turn, Radha, Heart of Keld has first strike.$You may look at the top card of your library any time, and you may play lands from the top of your library.${4}{R}{G}: Radha gets +X/+X until end of turn, where X is the number of lands you control.|
 Watcher of the Spheres|Core Set 2021|227|U|{W}{U}|Creature - Bird Wizard|2|2|Flying$Creature spells with flying you cast cost {1} less to cast.$Whenever another creature with flying enters the battlefield under your control, Watcher of the Spheres gets +1/+1 until end of turn.|
 Chromatic Orrery|Core Set 2021|228|M|{7}|Legendary Artifact|||You may spend mana as though it were mana of any color.${T}: Add {C}{C}{C}{C}{C}.${5}, {T}: Draw a card for each color among permanents you control.|
 Mazemind Tome|Core Set 2021|232|R|{2}|Artifact|||{T}, Put a page counter on Mazemind Tome: Scry 1.${2}, {T}, Put a page counter on Mazemind Tome: Draw a card.$When there are four or more page counters on Mazemind Tome, exile it. If you do, you gain 4 life.|

From df32cf67e4cdfc993b6f1deaa99cba51da137668 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sun, 14 Jun 2020 21:58:42 -0400
Subject: [PATCH 275/586] Implemented Sublime Epiphany

---
 .../src/mage/cards/s/SublimeEpiphany.java     | 64 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 65 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/s/SublimeEpiphany.java

diff --git a/Mage.Sets/src/mage/cards/s/SublimeEpiphany.java b/Mage.Sets/src/mage/cards/s/SublimeEpiphany.java
new file mode 100644
index 0000000000..39c8cbbc7f
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/SublimeEpiphany.java
@@ -0,0 +1,64 @@
+package mage.cards.s;
+
+import mage.abilities.Mode;
+import mage.abilities.effects.common.CounterTargetEffect;
+import mage.abilities.effects.common.CreateTokenCopyTargetEffect;
+import mage.abilities.effects.common.DrawCardTargetEffect;
+import mage.abilities.effects.common.ReturnToHandTargetEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.target.TargetPlayer;
+import mage.target.TargetSpell;
+import mage.target.common.TargetActivatedOrTriggeredAbility;
+import mage.target.common.TargetControlledCreaturePermanent;
+import mage.target.common.TargetNonlandPermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class SublimeEpiphany extends CardImpl {
+
+    public SublimeEpiphany(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{U}{U}");
+
+        // Choose one or more —
+        this.getSpellAbility().getModes().setMinModes(1);
+        this.getSpellAbility().getModes().setMaxModes(5);
+
+        // • Counter target spell
+        this.getSpellAbility().addEffect(new CounterTargetEffect());
+        this.getSpellAbility().addTarget(new TargetSpell());
+
+        // • Counter target activated or triggered ability.
+        Mode mode = new Mode(new CounterTargetEffect());
+        mode.addTarget(new TargetActivatedOrTriggeredAbility());
+        this.getSpellAbility().addMode(mode);
+
+        // • Return target nonland permanent to its owner's hand.
+        mode = new Mode(new ReturnToHandTargetEffect());
+        mode.addTarget(new TargetNonlandPermanent());
+        this.getSpellAbility().addMode(mode);
+
+        // • Create a token that's a copy of target creature you control.
+        mode = new Mode(new CreateTokenCopyTargetEffect());
+        mode.addTarget(new TargetControlledCreaturePermanent());
+        this.getSpellAbility().addMode(mode);
+
+        // • Target player draws a card.
+        mode = new Mode(new DrawCardTargetEffect(1));
+        mode.addTarget(new TargetPlayer());
+        this.getSpellAbility().addMode(mode);
+    }
+
+    private SublimeEpiphany(final SublimeEpiphany card) {
+        super(card);
+    }
+
+    @Override
+    public SublimeEpiphany copy() {
+        return new SublimeEpiphany(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 96b4ddf578..1fe118f430 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -159,6 +159,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Sporeweb Weaver", 208, Rarity.RARE, mage.cards.s.SporewebWeaver.class));
         cards.add(new SetCardInfo("Storm Caller", 335, Rarity.COMMON, mage.cards.s.StormCaller.class));
         cards.add(new SetCardInfo("Stormwing Entity", 73, Rarity.RARE, mage.cards.s.StormwingEntity.class));
+        cards.add(new SetCardInfo("Sublime Epiphany", 74, Rarity.RARE, mage.cards.s.SublimeEpiphany.class));
         cards.add(new SetCardInfo("Swamp", 311, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Teferi's Ageless Insight", 76, Rarity.RARE, mage.cards.t.TeferisAgelessInsight.class));
         cards.add(new SetCardInfo("Teferi's Protege", 77, Rarity.COMMON, mage.cards.t.TeferisProtege.class));

From fe3c2cc386f4f9ccd37d7a4e30043c22f3bd8752 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sun, 14 Jun 2020 22:10:19 -0400
Subject: [PATCH 276/586] Implemented Light of Promise

---
 .../src/mage/cards/l/LightOfPromise.java      | 84 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 85 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/l/LightOfPromise.java

diff --git a/Mage.Sets/src/mage/cards/l/LightOfPromise.java b/Mage.Sets/src/mage/cards/l/LightOfPromise.java
new file mode 100644
index 0000000000..622f7ee7ea
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/l/LightOfPromise.java
@@ -0,0 +1,84 @@
+package mage.cards.l;
+
+import mage.abilities.Ability;
+import mage.abilities.common.GainLifeControllerTriggeredAbility;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.AttachEffect;
+import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect;
+import mage.abilities.keyword.EnchantAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.AttachmentType;
+import mage.constants.CardType;
+import mage.constants.Outcome;
+import mage.constants.SubType;
+import mage.counters.CounterType;
+import mage.game.Game;
+import mage.game.permanent.Permanent;
+import mage.target.TargetPermanent;
+import mage.target.common.TargetCreaturePermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class LightOfPromise extends CardImpl {
+
+    public LightOfPromise(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}");
+
+        this.subtype.add(SubType.AURA);
+
+        // Enchant creature
+        TargetPermanent auraTarget = new TargetCreaturePermanent();
+        this.getSpellAbility().addTarget(auraTarget);
+        this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature));
+        Ability ability = new EnchantAbility(auraTarget.getTargetName());
+        this.addAbility(ability);
+
+        // Enchanted creature has "Whenever you gain life, put that many +1/+1 counters on this creature."
+        this.addAbility(new SimpleStaticAbility(new GainAbilityAttachedEffect(
+                new GainLifeControllerTriggeredAbility(
+                        new LightOfPromiseEffect(), false, true
+                ), AttachmentType.AURA
+        )));
+    }
+
+    private LightOfPromise(final LightOfPromise card) {
+        super(card);
+    }
+
+    @Override
+    public LightOfPromise copy() {
+        return new LightOfPromise(this);
+    }
+}
+
+class LightOfPromiseEffect extends OneShotEffect {
+
+    LightOfPromiseEffect() {
+        super(Outcome.Benefit);
+        staticText = "put that many +1/+1 counters on this creature";
+    }
+
+    private LightOfPromiseEffect(final LightOfPromiseEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public LightOfPromiseEffect copy() {
+        return new LightOfPromiseEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Permanent permanent = game.getPermanent(source.getSourceId());
+        if (permanent == null) {
+            return false;
+        }
+        int gainedLife = (int) this.getValue("gainedLife");
+        return permanent.addCounters(CounterType.P1P1.createInstance(gainedLife), source, game);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 1fe118f430..e8345dcebc 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -101,6 +101,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Kaervek, the Spiteful", 106, Rarity.RARE, mage.cards.k.KaervekTheSpiteful.class));
         cards.add(new SetCardInfo("Keral Keep Disciples", 334, Rarity.UNCOMMON, mage.cards.k.KeralKeepDisciples.class));
         cards.add(new SetCardInfo("Library Larcenist", 55, Rarity.COMMON, mage.cards.l.LibraryLarcenist.class));
+        cards.add(new SetCardInfo("Light of Promise", 25, Rarity.UNCOMMON, mage.cards.l.LightOfPromise.class));
         cards.add(new SetCardInfo("Liliana's Devotee", 109, Rarity.UNCOMMON, mage.cards.l.LilianasDevotee.class));
         cards.add(new SetCardInfo("Liliana's Scorn", 329, Rarity.RARE, mage.cards.l.LilianasScorn.class));
         cards.add(new SetCardInfo("Liliana's Scrounger", 330, Rarity.UNCOMMON, mage.cards.l.LilianasScrounger.class));

From cdcef4649d5dc05a22c3c2c125671b6dad291632 Mon Sep 17 00:00:00 2001
From: jasc7636 <jasc7636@gmail.com>
Date: Mon, 15 Jun 2020 10:08:56 +0200
Subject: [PATCH 277/586] Make watcher copy method deepcopy collections

---
 Mage/src/main/java/mage/watchers/Watcher.java |  40 ++++--
 Mage/src/test/java/mage/WatcherTest.java      | 133 +++++++++++++++++-
 2 files changed, 161 insertions(+), 12 deletions(-)

diff --git a/Mage/src/main/java/mage/watchers/Watcher.java b/Mage/src/main/java/mage/watchers/Watcher.java
index 762514ec54..14f2323a38 100644
--- a/Mage/src/main/java/mage/watchers/Watcher.java
+++ b/Mage/src/main/java/mage/watchers/Watcher.java
@@ -115,18 +115,38 @@ public abstract class Watcher implements Serializable {
                         ((Set) field.get(watcher)).clear();
                         ((Set) field.get(watcher)).addAll((Set) field.get(this));
                     } else if (field.getType() == Map.class) {
-                        Map target = ((Map) field.get(watcher));
-                        target.clear();
-                        Map source = (Map) field.get(this);
-
                         ParameterizedType parameterizedType = (ParameterizedType) field.getGenericType();
                         Type valueType = parameterizedType.getActualTypeArguments()[1];
-                        if (valueType.getClass().getSimpleName().contains("Set")) {
-                            source.entrySet().forEach(kv -> {
-                                Object key = ((Map.Entry) kv).getKey();
-                                Set value = (Set) ((Map.Entry) kv).getValue();
-                                target.put(key, new HashSet<>(value));
-                            });
+                        if (valueType.getTypeName().contains("Set")) {
+                            Map<Object, Set<Object>> source = (Map<Object, Set<Object>>) field.get(this);
+                            Map<Object, Set<Object>> target = (Map<Object, Set<Object>>) field.get(watcher);
+                            target.clear();
+                            for (Map.Entry<Object, Set<Object>> e : source.entrySet()) {
+                                Set<Object> set = new HashSet<>();
+                                set.addAll(e.getValue());
+                                target.put(e.getKey(), set);
+                            }
+                        }
+                        else if (valueType.getTypeName().contains("List")) {
+                            Map<Object, List<Object>> source = (Map<Object, List<Object>>) field.get(this);
+                            Map<Object, List<Object>> target = (Map<Object, List<Object>>) field.get(watcher);
+                            target.clear();
+                            for (Map.Entry<Object, List<Object>> e : source.entrySet()) {
+                                List<Object> list = new ArrayList<>();
+                                list.addAll(e.getValue());
+                                target.put(e.getKey(), list);
+                            }
+                        }
+                        else if (valueType.getTypeName().contains("Map")) {
+                            Map<Object, Map<Object, Object>> source = (Map<Object, Map<Object, Object>>) field.get(this);
+                            Map<Object, Map<Object, Object>> target = (Map<Object, Map<Object, Object>>) field.get(watcher);
+                            target.clear();
+                            for (Map.Entry<Object, Map<Object, Object>> e : source.entrySet()) {
+                                Map<Object, Object> map = new HashMap<>();
+                                map.putAll(e.getValue());
+                                target.put(e.getKey(), map);
+                            }
+
                         }
                         else {
                             ((Map) field.get(watcher)).putAll((Map) field.get(this));
diff --git a/Mage/src/test/java/mage/WatcherTest.java b/Mage/src/test/java/mage/WatcherTest.java
index 8ad06fde91..f6b3869aa1 100644
--- a/Mage/src/test/java/mage/WatcherTest.java
+++ b/Mage/src/test/java/mage/WatcherTest.java
@@ -1,13 +1,17 @@
 package mage;
 
 import static mage.constants.WatcherScope.GAME;
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.*;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import mage.constants.WatcherScope;
@@ -19,7 +23,7 @@ import org.junit.Test;
 public class WatcherTest {
 
   @Test
-  public void test() {
+  public void testShallowCopy() {
     // Given
     Map<String, String> mapField = new HashMap<>();
     mapField.put("mapFieldKey1", "mapFieldValue1");
@@ -43,6 +47,104 @@ public class WatcherTest {
     assertEquals(ImmutableMap.of("mapFieldKey1", "mapFieldValue1", "mapFieldKey2", "mapFieldValue2"), copy.getMapField());
   }
 
+  @Test
+  public void testDeepCopyMapOfList() {
+    // Given
+    Map<String, List<String>> listInMapField = new HashMap<>();
+    List<String> list1 = new ArrayList<>();
+    list1.add("v1");
+    list1.add("v1.1");
+    List<String> list2 = new ArrayList<>();
+    list2.add("v2");
+    listInMapField.put("k1", list1);
+    listInMapField.put("k2", list2);
+
+    TestWatcher testWatcher = new TestWatcher(GAME);
+    testWatcher.setListInMapField(listInMapField);
+
+    // When
+    TestWatcher copy = testWatcher.copy();
+
+    // And
+    testWatcher.getListInMapField().get("k1").add("v1.2");
+    List<String> list3 = new ArrayList<>();
+    list3.add("v5");
+    testWatcher.getListInMapField().put("k5", list3);
+
+    // Then
+    Map<String, List<String>> copyListInMap = copy.getListInMapField();
+    assertEquals(2, copyListInMap.size());
+    assertTrue(copyListInMap.containsKey("k1"));
+    assertEquals(ImmutableList.of("v1", "v1.1"), copyListInMap.get("k1"));
+    assertTrue(copyListInMap.containsKey("k2"));
+    assertEquals(ImmutableList.of("v2"), copyListInMap.get("k2"));
+  }
+
+  @Test
+  public void testDeepCopyMapOfSet() {
+    // Given
+    Map<String, Set<String>> setInMapField = new HashMap<>();
+    Set<String> set1 = new HashSet<>();
+    set1.add("v3");
+    Set<String> set2 = new HashSet<>();
+    set2.add("v4");
+    set2.add("v4.1");
+    setInMapField.put("k3", set1);
+    setInMapField.put("k4", set2);
+    setInMapField.put("k", new HashSet<String>());
+
+    TestWatcher testWatcher = new TestWatcher(GAME);
+    testWatcher.setSetInMapField(setInMapField);
+
+    // When
+    TestWatcher copy = testWatcher.copy();
+
+    // And
+    testWatcher.getSetInMapField().get("k3").add("v3.1");
+
+    // Then
+    Map<String, Set<String>> copySetInMap = copy.getSetInMapField();
+    assertEquals(3, copySetInMap.size());
+    assertTrue(copySetInMap.containsKey("k3"));
+    assertEquals(ImmutableSet.of("v3"), copySetInMap.get("k3"));
+    assertTrue(copySetInMap.containsKey("k4"));
+    assertEquals(ImmutableSet.of("v4", "v4.1"), copySetInMap.get("k4"));
+    assertTrue(copySetInMap.containsKey("k"));
+    assertEquals(ImmutableSet.of(), copySetInMap.get("k"));
+  }
+
+  @Test
+  public void testDeepCopyMapOfMap() {
+    // Given
+    Map<String, Map<String, String>> mapInMapField = new HashMap<>();
+    Map<String, String> map1 = new HashMap<>();
+    map1.put("k1.1", "v1.1");
+    map1.put("k1.2", "v1.2");
+    Map<String, String> map2 = new HashMap<>();
+    map2.put("k2.1", "v2.1");
+    mapInMapField.put("k1", map1);
+    mapInMapField.put("k2", map2);
+
+    TestWatcher testWatcher = new TestWatcher(GAME);
+    testWatcher.setMapInMapField(mapInMapField);
+
+    // When
+    TestWatcher copy = testWatcher.copy();
+
+    // And
+    testWatcher.getMapInMapField().get("k2").put("k2.2", "v2.2");
+    testWatcher.getMapInMapField().put("k3", new HashMap<>());
+
+    // Then
+    Map<String, Map<String, String>> copyMapInMap = copy.getMapInMapField();
+    assertEquals(2, copyMapInMap.size());
+    assertTrue(copyMapInMap.containsKey("k1"));
+    assertEquals(ImmutableMap.of("k1.1", "v1.1", "k1.2", "v1.2"), copyMapInMap.get("k1"));
+    assertTrue(copyMapInMap.containsKey("k2"));
+    assertEquals(ImmutableMap.of("k2.1", "v2.1"), copyMapInMap.get("k2"));
+    assertFalse(copyMapInMap.containsKey("k3"));
+  }
+
   private Set<String> set(String... values) {
     return Stream.of(values).collect(Collectors.toSet());
   }
@@ -51,6 +153,9 @@ public class WatcherTest {
     private String stringField;
     private Set<String> setField = new HashSet<>();
     private Map<String, String> mapField = new HashMap<>();
+    private Map<String, List<String>> listInMapField = new HashMap<>();
+    private Map<String, Set<String>> setInMapField = new HashMap<>();
+    private Map<String, Map<String, String>> mapInMapField = new HashMap<>();
 
     public TestWatcher(WatcherScope scope) {
       super(scope);
@@ -84,5 +189,29 @@ public class WatcherTest {
     public void setMapField(Map<String, String> mapField) {
       this.mapField = mapField;
     }
+
+    public Map<String, List<String>> getListInMapField() {
+      return listInMapField;
+    }
+
+    public void setListInMapField(Map<String, List<String>> listInMapField) {
+      this.listInMapField = listInMapField;
+    }
+
+    public Map<String, Set<String>> getSetInMapField() {
+      return setInMapField;
+    }
+
+    public void setSetInMapField(Map<String, Set<String>> setInMapField) {
+      this.setInMapField = setInMapField;
+    }
+
+    public Map<String, Map<String, String>> getMapInMapField() {
+      return mapInMapField;
+    }
+
+    public void setMapInMapField(Map<String, Map<String, String>> mapInMapField) {
+      this.mapInMapField = mapInMapField;
+    }
   }
 }

From 2b212983779e42778c887dd6da2123431528501f Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Mon, 15 Jun 2020 20:21:30 +0400
Subject: [PATCH 278/586] * UI: fixed that tokens in M15 render mode did not
 use a variety of images (all same named tokens uses same image);

---
 .../client/deckeditor/collection/viewer/MageBook.java | 10 +++++++---
 .../org/mage/card/arcane/CardPanelRenderImpl.java     |  3 ++-
 .../java/org/mage/plugins/card/images/ImageCache.java | 11 +++++++----
 .../main/java/mage/game/permanent/PermanentImpl.java  |  7 +++++--
 4 files changed, 21 insertions(+), 10 deletions(-)

diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java b/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java
index 01d5d729d2..ffab6f49d3 100644
--- a/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java
+++ b/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java
@@ -562,8 +562,8 @@ public class MageBook extends JComponent {
                     Constructor<?> cons = c.getConstructor();
                     Object newToken = cons.newInstance();
                     if (newToken instanceof Token) {
-                        ((Token) newToken).setExpansionSetCodeForImage(set);
                         ((Token) newToken).setOriginalExpansionSetCode(set);
+                        ((Token) newToken).setExpansionSetCodeForImage(set);
                         ((Token) newToken).setTokenType(token.getType());
                         tokens.add((Token) newToken);
                     }
@@ -634,6 +634,10 @@ public class MageBook extends JComponent {
             }
         }
 
+        if (emblems.size() == 0) {
+            return emblems;
+        }
+
         int totalTokens = getTotalNumTokens(set);
         int start = 0;
         if (!(page * conf.CARDS_PER_PAGE <= totalTokens && (page + 1) * conf.CARDS_PER_PAGE >= totalTokens)) {
@@ -917,8 +921,8 @@ public class MageBook extends JComponent {
 
     private int currentPage = 0;
     private String currentSet = "RTR";
-    private int currentCardsInSet = 0;
-    private int currentCardsNotInSet = 0;
+    private final int currentCardsInSet = 0;
+    private final int currentCardsNotInSet = 0;
 
     private static CardDimensions cardDimensions = new CardDimensions(1.2d);
     private static final Logger log = Logger.getLogger(MageBook.class);
diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderImpl.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderImpl.java
index e4c84b3ac3..f298ffb70d 100644
--- a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderImpl.java
+++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderImpl.java
@@ -154,6 +154,7 @@ public class CardPanelRenderImpl extends CardPanel {
             sb.append(this.view.getToughness());
             sb.append(this.view.getLoyalty());
             sb.append(this.view.getColor().toString());
+            sb.append(this.view.getType());
             sb.append(this.view.getExpansionSetCode());
             for (CardType type : this.view.getCardTypes()) {
                 sb.append((char) type.ordinal());
@@ -227,7 +228,7 @@ public class CardPanelRenderImpl extends CardPanel {
     private BufferedImage faceArtImage;
 
     // Factory to generate card appropriate views
-    private CardRendererFactory cardRendererFactory = new CardRendererFactory();
+    private final CardRendererFactory cardRendererFactory = new CardRendererFactory();
 
     // The rendered card image, with or without the art image loaded yet
     // = null while invalid
diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java b/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java
index a077bdf967..ae54361ff4 100644
--- a/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java
@@ -47,7 +47,7 @@ public final class ImageCache {
     private static final SoftValuesLoadingCache<String, BufferedImage> FACE_IMAGE_CACHE;
 
     /**
-     * Common pattern for keys. Format: "<cardname>#<setname>#<collectorID>"
+     * Common pattern for keys. See ImageCache.getKey for structure info
      */
     private static final Pattern KEY_PATTERN = Pattern.compile("(.*)#(.*)#(.*)#(.*)#(.*)#(.*)");
 
@@ -428,11 +428,14 @@ public final class ImageCache {
      * Returns the map key for a card, without any suffixes for the image size.
      */
     private static String getKey(CardView card, String name, String suffix) {
-        return name + '#' + card.getExpansionSetCode() + '#' + card.getType() + '#' + card.getCardNumber() + '#'
-                + (card.getTokenSetCode() == null ? "" : card.getTokenSetCode())
+        return name
+                + '#' + card.getExpansionSetCode()
+                + '#' + card.getType()
+                + '#' + card.getCardNumber()
+                + '#' + (card.getTokenSetCode() == null ? "" : card.getTokenSetCode())
                 + suffix
                 + (card.getUsesVariousArt() ? "#usesVariousArt" : "")
-                + (card.getTokenDescriptor() != null ? '#' + card.getTokenDescriptor() : "#");
+                + '#' + (card.getTokenDescriptor() != null ? card.getTokenDescriptor() : "");
     }
 
     /**
diff --git a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java
index 003d33da82..b2e8fa813d 100644
--- a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java
+++ b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java
@@ -370,7 +370,10 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
             copyAbility.setSourceId(objectId);
             // triggered abilities must be added to the state().triggers
             // still as long as the prev. permanent is known to the LKI (e.g. Showstopper) so gained dies triggered ability will trigger
-            game.getState().addAbility(copyAbility, sourceId, this);
+            if (game != null) {
+                // game is null in cards viewer window (MageBook)
+                game.getState().addAbility(copyAbility, sourceId, this);
+            }
             abilities.add(copyAbility);
         }
     }
@@ -400,7 +403,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
     }
 
     @Override
-    public void removeAbilities(List<Ability> abilitiesToRemove, UUID sourceId, Game game){
+    public void removeAbilities(List<Ability> abilitiesToRemove, UUID sourceId, Game game) {
         if (abilitiesToRemove == null) {
             return;
         }

From c2487aec7d316f65dcde7be655e5aaa724579c87 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Mon, 15 Jun 2020 20:29:52 +0400
Subject: [PATCH 279/586] [C20] tokens support and fixes: * Added tokens for
 C20; * Added tokens images download for C20; * Fixed that some tokens uses
 wrong images or images from wrong set (example: Elemental);

---
 .../sources/ScryfallImageSupportTokens.java   | 21 +++++++
 .../src/main/resources/card-pictures-tok.txt  | 39 ++++++++++--
 .../src/mage/cards/a/AbzanAscendancy.java     |  7 +--
 .../src/mage/cards/a/ArtifactMutation.java    |  9 ++-
 .../src/mage/cards/b/BenevolentOffering.java  |  9 ++-
 .../src/mage/cards/c/ChandraFlamecaller.java  |  4 +-
 .../src/mage/cards/c/ConfrontTheAssault.java  |  2 +-
 .../src/mage/cards/c/CustodiSoulbinders.java  | 20 +++---
 .../src/mage/cards/f/FeralLightning.java      | 11 ++--
 .../src/mage/cards/g/GeistHonoredMonk.java    |  2 +-
 .../src/mage/cards/g/GroveOfTheGuardian.java  | 34 ++--------
 .../src/mage/cards/l/LightningCoils.java      | 20 +++---
 .../src/mage/cards/l/LingeringSouls.java      |  9 ++-
 .../src/mage/cards/m/MausoleumGuard.java      |  8 +--
 .../src/mage/cards/m/MidnightHaunting.java    |  9 ++-
 .../src/mage/cards/p/ProsperousPirates.java   |  7 +--
 Mage.Sets/src/mage/cards/r/RequiemAngel.java  |  9 ++-
 .../src/mage/cards/r/ResearchDevelopment.java | 19 +++---
 Mage.Sets/src/mage/cards/r/RevelInRiches.java |  7 +--
 .../src/mage/cards/s/SandsteppeOutcast.java   | 13 ++--
 .../mage/cards/s/SaprolingInfestation.java    |  5 +-
 .../src/mage/cards/s/SpectralReserves.java    |  9 ++-
 Mage.Sets/src/mage/cards/s/SpiritBonds.java   | 13 ++--
 .../src/mage/cards/t/TeysaOrzhovScion.java    | 20 +++---
 Mage.Sets/src/mage/cards/t/TreasureMap.java   |  7 +--
 .../src/mage/cards/t/TriplicateSpirits.java   | 11 ++--
 .../src/mage/cards/t/TwilightDrover.java      |  9 ++-
 .../src/mage/cards/w/WortTheRaidmother.java   | 10 +--
 .../src/mage/cards/z/ZaxaraTheExemplary.java  | 24 +------
 .../java/mage/verify/VerifyCardDataTest.java  |  4 ++
 .../game/permanent/token/BeastToken2.java     |  2 +-
 .../mage/game/permanent/token/BirdToken.java  |  9 ++-
 .../game/permanent/token/ElementalToken.java  | 63 -------------------
 .../token/ElementalTokenWithHaste.java        | 51 +++++++++++++++
 ...therToken.java => GoblinWarriorToken.java} | 20 +++---
 .../token/GreenAndWhiteElementalToken.java    | 41 ++++++++++++
 .../token/HornetQueenInsectToken.java         | 13 ++--
 .../mage/game/permanent/token/HumanToken.java |  2 +-
 .../token/HydraBroodmasterToken.java          | 12 ++--
 .../token/ResearchDevelopmentToken.java       | 39 ++++++++++++
 .../game/permanent/token/SaprolingToken.java  | 24 +++----
 .../game/permanent/token/SoldierToken.java    |  5 +-
 .../permanent/token/SpiritWhiteToken.java     | 14 +----
 .../token/TheLocustGodInsectToken.java        | 15 ++---
 .../mage/game/permanent/token/TokenImpl.java  | 13 ++--
 .../game/permanent/token/TreasureToken.java   | 15 +----
 .../permanent/token/WhiteElementalToken.java  | 13 ++--
 .../token/ZaxaraTheExemplaryHydraToken.java   | 33 ++++++++++
 48 files changed, 406 insertions(+), 349 deletions(-)
 delete mode 100644 Mage/src/main/java/mage/game/permanent/token/ElementalToken.java
 create mode 100644 Mage/src/main/java/mage/game/permanent/token/ElementalTokenWithHaste.java
 rename Mage/src/main/java/mage/game/permanent/token/{WortTheRaidmotherToken.java => GoblinWarriorToken.java} (61%)
 create mode 100644 Mage/src/main/java/mage/game/permanent/token/GreenAndWhiteElementalToken.java
 create mode 100644 Mage/src/main/java/mage/game/permanent/token/ResearchDevelopmentToken.java
 create mode 100644 Mage/src/main/java/mage/game/permanent/token/ZaxaraTheExemplaryHydraToken.java

diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java
index a73ed21c14..3046eb508a 100644
--- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java
@@ -388,6 +388,27 @@ public class ScryfallImageSupportTokens {
             put("PCA/Plane - Turri Island", "https://api.scryfall.com/cards/opca/82/en?format=image");
             put("PCA/Plane - Undercity Reaches", "https://api.scryfall.com/cards/opca/83/en?format=image");
 
+            // C20
+            put("C20/Angel", "https://api.scryfall.com/cards/tc20/1/en?format=image");
+            put("C20/Beast", "https://api.scryfall.com/cards/tc20/11/en?format=image");
+            put("C20/Bird Illusion", "https://api.scryfall.com/cards/tc20/7/en?format=image");
+            put("C20/Bird", "https://api.scryfall.com/cards/tc20/2/en?format=image");
+            put("C20/Dinosaur Cat", "https://api.scryfall.com/cards/tc20/16/en?format=image");
+            put("C20/Drake", "https://api.scryfall.com/cards/tc20/8/en?format=image");
+            put("C20/Elemental/1", "https://api.scryfall.com/cards/tc20/10/en?format=image"); // 3/1
+            put("C20/Elemental/2", "https://api.scryfall.com/cards/tc20/3/en?format=image"); // 4/4
+            put("C20/Goblin Warrior", "https://api.scryfall.com/cards/tc20/17/en?format=image");
+            put("C20/Human", "https://api.scryfall.com/cards/tc20/4/en?format=image");
+            put("C20/Hydra", "https://api.scryfall.com/cards/tc20/12/en?format=image");
+            put("C20/Insect/1", "https://api.scryfall.com/cards/tc20/13/en?format=image"); // deathtouch
+            put("C20/Insect/2", "https://api.scryfall.com/cards/tc20/18/en?format=image"); // haste
+            put("C20/Saproling", "https://api.scryfall.com/cards/tc20/14/en?format=image");
+            put("C20/Snake", "https://api.scryfall.com/cards/tc20/15/en?format=image");
+            put("C20/Soldier", "https://api.scryfall.com/cards/tc20/5/en?format=image");
+            put("C20/Spirit", "https://api.scryfall.com/cards/tc20/6/en?format=image");
+            put("C20/Treasure", "https://api.scryfall.com/cards/tc20/19/en?format=image");
+            put("C20/Zombie", "https://api.scryfall.com/cards/tc20/9/en?format=image");
+
             // generate supported sets
             supportedSets.clear();
             for (String cardName : this.keySet()) {
diff --git a/Mage.Client/src/main/resources/card-pictures-tok.txt b/Mage.Client/src/main/resources/card-pictures-tok.txt
index d3426689e9..e5a153c878 100644
--- a/Mage.Client/src/main/resources/card-pictures-tok.txt
+++ b/Mage.Client/src/main/resources/card-pictures-tok.txt
@@ -571,7 +571,7 @@
 |Generate|TOK:DGM|Wurm|||WurmToken3|
 |Generate|TOK:DIS|Bird|||DovescapeToken|
 |Generate|TOK:DIS|Drake|||LeafdrakeRoostDrakeToken|
-|Generate|TOK:DIS|Elemental|||ElementalToken|
+|Generate|TOK:DIS|Elemental|||ResearchDevelopmentToken|
 |Generate|TOK:DIS|Goblin|||RakdosGuildmageGoblinToken|
 |Generate|TOK:DIS|Saproling|||SaprolingToken|
 |Generate|TOK:DIS|Snake|||PatagiaViperSnakeToken|
@@ -956,7 +956,7 @@
 |Generate|TOK:MOR|Wolf|||WolfToken|
 |Generate|TOK:MRD|Beast|||OneDozenEyesBeastToken|
 |Generate|TOK:MRD|Demon|||ReignOfThePitToken|
-|Generate|TOK:MRD|Elemental|||ElementalToken|
+|Generate|TOK:MRD|Elemental|||ElementalTokenWithHaste|
 |Generate|TOK:MRD|Insect|||InsectToken|
 |Generate|TOK:MRD|Myr|||MyrToken|
 |Generate|TOK:MRD|Pentavite|||PentaviteToken|
@@ -990,7 +990,7 @@
 |Generate|TOK:OGW|Eldrazi Scion|5||EldraziScionToken|
 |Generate|TOK:OGW|Eldrazi Scion|6||EldraziScionToken|
 |Generate|TOK:OGW|Elemental|1||SeedGuardianToken|
-|Generate|TOK:OGW|Elemental|2||ElementalToken|
+|Generate|TOK:OGW|Elemental|2||ElementalTokenWithHaste|
 |Generate|TOK:OGW|Knight Ally|||KnightAllyToken|
 |Generate|TOK:OGW|Kor Ally|||KorAllyToken|
 |Generate|TOK:OGW|Octopus|||OctopusToken|
@@ -1063,7 +1063,7 @@
 |Generate|TOK:RTR|Bird|||BirdToken|
 |Generate|TOK:RTR|Centaur|||CentaurToken|
 |Generate|TOK:RTR|Dragon|||UtvaraHellkiteDragonToken|
-|Generate|TOK:RTR|Elemental||
+|Generate|TOK:RTR|Elemental|||GreenAndWhiteElementalToken|
 |Generate|TOK:RTR|Goblin|||GoblinToken|
 |Generate|TOK:RTR|Knight|||KnightToken|
 |Generate|TOK:RTR|Ooze|||MysticGenesisOozeToken|
@@ -1106,7 +1106,7 @@
 |Generate|TOK:SOI|Vampire Knight|||VampireKnightToken|
 |Generate|TOK:SOI|Wolf|||WolfToken|
 |Generate|TOK:SOI|Zombie|||ZombieToken|
-|Generate|TOK:SOK|Elemental|||ElementalToken|
+|Generate|TOK:SOK|Elemental|||ElementalTokenWithHaste|
 |Generate|TOK:SOK|Snake|||SnakeToken|
 |Generate|TOK:SOK|Spirit|||SpiritToken|
 |Generate|TOK:SOK|Urami|||UramiToken|
@@ -1328,6 +1328,8 @@
 |Generate|TOK:M20|Treasure|||TreasureToken|
 |Generate|TOK:M20|Wolf|||WolfToken|
 |Generate|TOK:M20|Zombie|||ZombieToken|
+
+# ELD
 |Generate|TOK:ELD|Bear|||BearToken|
 |Generate|TOK:ELD|Boar|||WolfsQuarryToken|
 |Generate|TOK:ELD|Dwarf|||DwarfToken|
@@ -1346,6 +1348,8 @@
 |Generate|TOK:ELD|Mouse|||MouseToken|
 |Generate|TOK:ELD|Rat|||RatToken|
 |Generate|TOK:ELD|Wolf|||GarrukCursedHuntsmanToken|
+
+# THB
 |Generate|TOK:THB|Goat|||GoatToken|
 |Generate|TOK:THB|Human Soldier|||HumanSoldierToken|
 |Generate|TOK:THB|Pegasus|||PegasusToken2|
@@ -1360,6 +1364,8 @@
 |Generate|TOK:THB|Nightmare|||AshiokNightmareMuseToken|
 |Generate|TOK:THB|Gold|||GoldToken|
 |Generate|TOK:THB|Wall|||ArtifactWallToken|
+
+# IKO
 |Generate|TOK:IKO|Beast|||BeastToken|
 |Generate|TOK:IKO|Cat Bird|||CatBirdToken|
 |Generate|TOK:IKO|Cat|||CatToken|
@@ -1370,4 +1376,25 @@
 |Generate|TOK:IKO|Human Soldier|2||HumanSoldierToken|
 |Generate|TOK:IKO|Human Soldier|3||HumanSoldierToken|
 |Generate|TOK:IKO|Kraken|||KrakenToken|
-|Generate|TOK:IKO|Shark|||SharkToken|
\ No newline at end of file
+|Generate|TOK:IKO|Shark|||SharkToken|
+
+# C20
+|Generate|TOK:C20|Angel|||AngelToken|
+|Generate|TOK:C20|Beast|||BeastToken2|
+|Generate|TOK:C20|Bird|||BirdToken|
+|Generate|TOK:C20|Bird Illusion|||BirdIllusionToken|
+|Generate|TOK:C20|Dinosaur Cat|||DinosaurCatToken|
+|Generate|TOK:C20|Drake|||DrakeToken|
+|Generate|TOK:C20|Elemental|1||ElementalTokenWithHaste|
+|Generate|TOK:C20|Elemental|2||WhiteElementalToken|
+|Generate|TOK:C20|Goblin Warrior|||GoblinWarriorToken|
+|Generate|TOK:C20|Human|||HumanToken|
+|Generate|TOK:C20|Hydra|||ZaxaraTheExemplaryHydraToken|
+|Generate|TOK:C20|Insect|1||HornetQueenInsectToken|
+|Generate|TOK:C20|Insect|2||TheLocustGodInsectToken|
+|Generate|TOK:C20|Saproling|||SaprolingToken|
+|Generate|TOK:C20|Snake|||SnakeToken|
+|Generate|TOK:C20|Soldier|||SoldierToken|
+|Generate|TOK:C20|Spirit|||SpiritWhiteToken|
+|Generate|TOK:C20|Treasure|||TreasureToken|
+|Generate|TOK:C20|Zombie|||ZombieToken|
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/a/AbzanAscendancy.java b/Mage.Sets/src/mage/cards/a/AbzanAscendancy.java
index 558cfe4241..ffea394e3d 100644
--- a/Mage.Sets/src/mage/cards/a/AbzanAscendancy.java
+++ b/Mage.Sets/src/mage/cards/a/AbzanAscendancy.java
@@ -1,7 +1,5 @@
-
 package mage.cards.a;
 
-import java.util.UUID;
 import mage.abilities.common.DiesCreatureTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
@@ -17,8 +15,9 @@ import mage.filter.predicate.Predicates;
 import mage.filter.predicate.permanent.TokenPredicate;
 import mage.game.permanent.token.SpiritWhiteToken;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class AbzanAscendancy extends CardImpl {
@@ -37,7 +36,7 @@ public final class AbzanAscendancy extends CardImpl {
         this.addAbility(new EntersBattlefieldTriggeredAbility(new AddCountersAllEffect(CounterType.P1P1.createInstance(), StaticFilters.FILTER_CONTROLLED_CREATURE), false));
 
         // Whenever a nontoken creature you control dies, create a 1/1 white Spirit creature token with flying.
-        this.addAbility(new DiesCreatureTriggeredAbility(new CreateTokenEffect(new SpiritWhiteToken("KTK")), false, filter));
+        this.addAbility(new DiesCreatureTriggeredAbility(new CreateTokenEffect(new SpiritWhiteToken()), false, filter));
 
     }
 
diff --git a/Mage.Sets/src/mage/cards/a/ArtifactMutation.java b/Mage.Sets/src/mage/cards/a/ArtifactMutation.java
index 416308846b..abc4b8b08c 100644
--- a/Mage.Sets/src/mage/cards/a/ArtifactMutation.java
+++ b/Mage.Sets/src/mage/cards/a/ArtifactMutation.java
@@ -1,7 +1,5 @@
-
 package mage.cards.a;
 
-import java.util.UUID;
 import mage.abilities.dynamicvalue.common.TargetConvertedManaCost;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.abilities.effects.common.DestroyTargetEffect;
@@ -11,19 +9,20 @@ import mage.constants.CardType;
 import mage.game.permanent.token.SaprolingToken;
 import mage.target.common.TargetArtifactPermanent;
 
+import java.util.UUID;
+
 /**
- *
  * @author North
  */
 public final class ArtifactMutation extends CardImpl {
 
     public ArtifactMutation(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{R}{G}");
-
+        super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R}{G}");
 
         // Destroy target artifact. It can't be regenerated.
         this.getSpellAbility().addEffect(new DestroyTargetEffect(true));
         this.getSpellAbility().addTarget(new TargetArtifactPermanent());
+
         // create X 1/1 green Saproling creature tokens, where X is that artifact's converted mana cost.
         this.getSpellAbility().addEffect(new CreateTokenEffect(new SaprolingToken(), TargetConvertedManaCost.instance).setText("create X 1/1 green Saproling creature tokens, where X is that artifact's converted mana cost"));
     }
diff --git a/Mage.Sets/src/mage/cards/b/BenevolentOffering.java b/Mage.Sets/src/mage/cards/b/BenevolentOffering.java
index 1a708b2343..1e4ea4310b 100644
--- a/Mage.Sets/src/mage/cards/b/BenevolentOffering.java
+++ b/Mage.Sets/src/mage/cards/b/BenevolentOffering.java
@@ -1,7 +1,5 @@
-
 package mage.cards.b;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.OneShotEffect;
@@ -19,8 +17,9 @@ import mage.target.Target;
 import mage.target.common.TargetOpponent;
 import mage.target.targetpointer.FixedTarget;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class BenevolentOffering extends CardImpl {
@@ -69,10 +68,10 @@ class BenevolentOfferingEffect1 extends OneShotEffect {
             target.choose(Outcome.Sacrifice, source.getControllerId(), source.getSourceId(), game);
             Player opponent = game.getPlayer(target.getFirstTarget());
             if (opponent != null) {
-                Effect effect = new CreateTokenTargetEffect(new SpiritWhiteToken("C14"), 3);
+                Effect effect = new CreateTokenTargetEffect(new SpiritWhiteToken(), 3);
                 effect.setTargetPointer(new FixedTarget(opponent.getId()));
                 effect.apply(game, source);
-                new CreateTokenEffect(new SpiritWhiteToken("C14"), 3).apply(game, source);
+                new CreateTokenEffect(new SpiritWhiteToken(), 3).apply(game, source);
                 return true;
             }
         }
diff --git a/Mage.Sets/src/mage/cards/c/ChandraFlamecaller.java b/Mage.Sets/src/mage/cards/c/ChandraFlamecaller.java
index 2987b764e4..a4e54c3fd3 100644
--- a/Mage.Sets/src/mage/cards/c/ChandraFlamecaller.java
+++ b/Mage.Sets/src/mage/cards/c/ChandraFlamecaller.java
@@ -18,7 +18,7 @@ import mage.constants.SubType;
 import mage.constants.SuperType;
 import mage.filter.StaticFilters;
 import mage.game.Game;
-import mage.game.permanent.token.ElementalToken;
+import mage.game.permanent.token.ElementalTokenWithHaste;
 import mage.players.Player;
 
 import java.util.UUID;
@@ -75,7 +75,7 @@ class ChandraElementalEffect extends OneShotEffect {
     public boolean apply(Game game, Ability source) {
         Player controller = game.getPlayer(source.getControllerId());
         if (controller != null) {
-            CreateTokenEffect effect = new CreateTokenEffect(new ElementalToken("OGW", 2, true), 2);
+            CreateTokenEffect effect = new CreateTokenEffect(new ElementalTokenWithHaste(), 2);
             effect.apply(game, source);
             effect.exileTokensCreatedAtNextEndStep(game, source);
             return true;
diff --git a/Mage.Sets/src/mage/cards/c/ConfrontTheAssault.java b/Mage.Sets/src/mage/cards/c/ConfrontTheAssault.java
index 985bc5f919..2c45b4c15d 100644
--- a/Mage.Sets/src/mage/cards/c/ConfrontTheAssault.java
+++ b/Mage.Sets/src/mage/cards/c/ConfrontTheAssault.java
@@ -31,7 +31,7 @@ public final class ConfrontTheAssault extends CardImpl {
         this.addAbility(ability);
 
         // Create three 1/1 white Spirit creature tokens with flying.
-        this.getSpellAbility().addEffect(new CreateTokenEffect(new SpiritWhiteToken("ANA"), 3));
+        this.getSpellAbility().addEffect(new CreateTokenEffect(new SpiritWhiteToken(), 3));
     }
 
     public ConfrontTheAssault(final ConfrontTheAssault card) {
diff --git a/Mage.Sets/src/mage/cards/c/CustodiSoulbinders.java b/Mage.Sets/src/mage/cards/c/CustodiSoulbinders.java
index ab23d88f7d..968e1360af 100644
--- a/Mage.Sets/src/mage/cards/c/CustodiSoulbinders.java
+++ b/Mage.Sets/src/mage/cards/c/CustodiSoulbinders.java
@@ -1,7 +1,5 @@
-
 package mage.cards.c;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldAbility;
@@ -21,19 +19,21 @@ import mage.filter.common.FilterCreaturePermanent;
 import mage.filter.predicate.permanent.AnotherPredicate;
 import mage.game.permanent.token.SpiritWhiteToken;
 
+import java.util.UUID;
+
 /**
- *
  * @author emerald000
  */
 public final class CustodiSoulbinders extends CardImpl {
-    
+
     private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("other creatures");
+
     static {
         filter.add(AnotherPredicate.instance);
     }
 
     public CustodiSoulbinders(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{W}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}");
         this.subtype.add(SubType.HUMAN);
         this.subtype.add(SubType.CLERIC);
         this.power = new MageInt(0);
@@ -42,14 +42,14 @@ public final class CustodiSoulbinders extends CardImpl {
         // Custodi Soulbinders enters the battlefield with X +1/+1 counters on it, where X is the number of other creatures on the battlefield.
         this.addAbility(new EntersBattlefieldAbility(
                 new AddCountersSourceEffect(
-                        CounterType.P1P1.createInstance(), 
-                        new PermanentsOnBattlefieldCount(filter), 
-                        false), 
+                        CounterType.P1P1.createInstance(),
+                        new PermanentsOnBattlefieldCount(filter),
+                        false),
                 "with X +1/+1 counters on it, where X is the number of other creatures on the battlefield"));
 
-        
+
         // {2}{W}, Remove a +1/+1 counter from Custodi Soulbinders: Create a 1/1 white Spirit creature token with flying.
-        Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new SpiritWhiteToken("CNS")), new ManaCostsImpl<>("{2}{W}"));
+        Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new SpiritWhiteToken()), new ManaCostsImpl<>("{2}{W}"));
         ability.addCost(new RemoveCountersSourceCost(CounterType.P1P1.createInstance()));
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/f/FeralLightning.java b/Mage.Sets/src/mage/cards/f/FeralLightning.java
index e15766daf2..d87cf22bad 100644
--- a/Mage.Sets/src/mage/cards/f/FeralLightning.java
+++ b/Mage.Sets/src/mage/cards/f/FeralLightning.java
@@ -1,7 +1,5 @@
-
 package mage.cards.f;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.CreateTokenEffect;
@@ -10,17 +8,18 @@ import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.game.Game;
-import mage.game.permanent.token.ElementalToken;
+import mage.game.permanent.token.ElementalTokenWithHaste;
 import mage.players.Player;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class FeralLightning extends CardImpl {
 
     public FeralLightning(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{R}{R}{R}");
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{R}{R}{R}");
 
         // Create three 3/1 red Elemental creature tokens with haste. Exile them at the beginning of the next end step.
         this.getSpellAbility().addEffect(new FeralLightningEffect());
@@ -57,7 +56,7 @@ class FeralLightningEffect extends OneShotEffect {
     public boolean apply(Game game, Ability source) {
         Player controller = game.getPlayer(source.getControllerId());
         if (controller != null) {
-            CreateTokenEffect effect = new CreateTokenEffect(new ElementalToken("CON", 1, true), 3);
+            CreateTokenEffect effect = new CreateTokenEffect(new ElementalTokenWithHaste(), 3);
             effect.apply(game, source);
             effect.exileTokensCreatedAtNextEndStep(game, source);
             return true;
diff --git a/Mage.Sets/src/mage/cards/g/GeistHonoredMonk.java b/Mage.Sets/src/mage/cards/g/GeistHonoredMonk.java
index 879fce467c..d6d1174281 100644
--- a/Mage.Sets/src/mage/cards/g/GeistHonoredMonk.java
+++ b/Mage.Sets/src/mage/cards/g/GeistHonoredMonk.java
@@ -38,7 +38,7 @@ public final class GeistHonoredMonk extends CardImpl {
                 .addHint(CreaturesYouControlHint.instance));
 
         // When Geist-Honored Monk enters the battlefield, create two 1/1 white Spirit creature tokens with flying.
-        this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new SpiritWhiteToken("ISD"), 2)));
+        this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new SpiritWhiteToken(), 2)));
     }
 
     public GeistHonoredMonk(final GeistHonoredMonk card) {
diff --git a/Mage.Sets/src/mage/cards/g/GroveOfTheGuardian.java b/Mage.Sets/src/mage/cards/g/GroveOfTheGuardian.java
index d7c445de70..2237f3b4c8 100644
--- a/Mage.Sets/src/mage/cards/g/GroveOfTheGuardian.java
+++ b/Mage.Sets/src/mage/cards/g/GroveOfTheGuardian.java
@@ -1,8 +1,5 @@
-
 package mage.cards.g;
 
-import java.util.UUID;
-import mage.MageInt;
 import mage.Mana;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleActivatedAbility;
@@ -11,20 +8,19 @@ import mage.abilities.costs.common.TapSourceCost;
 import mage.abilities.costs.common.TapTargetCost;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.common.CreateTokenEffect;
-import mage.abilities.keyword.VigilanceAbility;
 import mage.abilities.mana.SimpleManaAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.SubType;
 import mage.constants.Zone;
 import mage.filter.common.FilterControlledCreaturePermanent;
 import mage.filter.predicate.Predicates;
 import mage.filter.predicate.permanent.TappedPredicate;
-import mage.game.permanent.token.TokenImpl;
-import mage.game.permanent.token.Token;
+import mage.game.permanent.token.GreenAndWhiteElementalToken;
 import mage.target.common.TargetControlledCreaturePermanent;
 
+import java.util.UUID;
+
 /**
  * @author LevelX2
  */
@@ -43,7 +39,7 @@ public final class GroveOfTheGuardian extends CardImpl {
         this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.ColorlessMana(1), new TapSourceCost()));
 
         // {3}{G}{W}, {T}, Tap two untapped creatures you control, Sacrifice Grove of the Guardian: Create an 8/8 green and white Elemental creature token with vigilance.
-        Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new ElementalToken(), 1), new ManaCostsImpl("{3}{G}{W}"));
+        Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new GreenAndWhiteElementalToken(), 1), new ManaCostsImpl("{3}{G}{W}"));
         ability.addCost(new TapSourceCost());
         ability.addCost(new TapTargetCost(new TargetControlledCreaturePermanent(2, 2, filter, false)));
         ability.addCost(new SacrificeSourceCost());
@@ -58,26 +54,4 @@ public final class GroveOfTheGuardian extends CardImpl {
     public GroveOfTheGuardian copy() {
         return new GroveOfTheGuardian(this);
     }
-
-    private static class ElementalToken extends TokenImpl {
-
-        ElementalToken() {
-            super("Elemental", "8/8 green and white Elemental creature token with vigilance");
-
-            cardType.add(CardType.CREATURE);
-            color.setGreen(true);
-            color.setWhite(true);
-            this.subtype.add(SubType.ELEMENTAL);
-            power = new MageInt(8);
-            toughness = new MageInt(8);
-            this.addAbility(VigilanceAbility.getInstance());
-        }
-        public ElementalToken(final ElementalToken token) {
-            super(token);
-        }
-
-        public ElementalToken copy() {
-            return new ElementalToken(this);
-        }
-    }
 }
diff --git a/Mage.Sets/src/mage/cards/l/LightningCoils.java b/Mage.Sets/src/mage/cards/l/LightningCoils.java
index b3b8a32f3a..fbc516c5b5 100644
--- a/Mage.Sets/src/mage/cards/l/LightningCoils.java
+++ b/Mage.Sets/src/mage/cards/l/LightningCoils.java
@@ -1,7 +1,5 @@
-
 package mage.cards.l;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
 import mage.abilities.common.DiesCreatureTriggeredAbility;
@@ -19,16 +17,18 @@ import mage.filter.predicate.Predicates;
 import mage.filter.predicate.permanent.TokenPredicate;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
-import mage.game.permanent.token.ElementalToken;
+import mage.game.permanent.token.ElementalTokenWithHaste;
 import mage.players.Player;
 
+import java.util.UUID;
+
 /**
- *
  * @author escplan9 - Derek Monturo
  */
 public final class LightningCoils extends CardImpl {
-    
+
     private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a nontoken creature you control");
+
     static {
         filter.add(TargetController.YOU.getControllerPredicate());
         filter.add(Predicates.not(TokenPredicate.instance));
@@ -36,13 +36,13 @@ public final class LightningCoils extends CardImpl {
 
     public LightningCoils(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}");
-        
+
         // Whenever a nontoken creature you control dies, put a charge counter on Lightning Coils.
         this.addAbility(
                 new DiesCreatureTriggeredAbility(
-                        new AddCountersSourceEffect(CounterType.CHARGE.createInstance(), true), 
+                        new AddCountersSourceEffect(CounterType.CHARGE.createInstance(), true),
                         false, filter));
-        
+
         // At the beginning of your upkeep, if Lightning Coils has five or more charge counters on it, remove all of them from it 
         // and put that many 3/1 red Elemental creature tokens with haste onto the battlefield. 
         // Exile them at the beginning of the next end step.
@@ -79,9 +79,9 @@ class LightningCoilsEffect extends OneShotEffect {
             if (counters >= 5) {
                 // remove all the counters and create that many tokens
                 p.removeCounters(CounterType.CHARGE.getName(), p.getCounters(game).getCount(CounterType.CHARGE), game);
-                CreateTokenEffect effect = new CreateTokenEffect(new ElementalToken("CON", 1, true), counters);
+                CreateTokenEffect effect = new CreateTokenEffect(new ElementalTokenWithHaste(), counters);
                 effect.apply(game, source);
-                
+
                 // exile those tokens at next end step
                 effect.exileTokensCreatedAtNextEndStep(game, source);
                 return true;
diff --git a/Mage.Sets/src/mage/cards/l/LingeringSouls.java b/Mage.Sets/src/mage/cards/l/LingeringSouls.java
index e6d972cb4e..0ebfd5ca77 100644
--- a/Mage.Sets/src/mage/cards/l/LingeringSouls.java
+++ b/Mage.Sets/src/mage/cards/l/LingeringSouls.java
@@ -1,7 +1,5 @@
-
 package mage.cards.l;
 
-import java.util.UUID;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.abilities.keyword.FlashbackAbility;
@@ -11,18 +9,19 @@ import mage.constants.CardType;
 import mage.constants.TimingRule;
 import mage.game.permanent.token.SpiritWhiteToken;
 
+import java.util.UUID;
+
 /**
- *
  * @author Loki
  */
 public final class LingeringSouls extends CardImpl {
 
     public LingeringSouls(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{W}");
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{W}");
 
 
         // Create two 1/1 white Spirit creature tokens with flying.
-        this.getSpellAbility().addEffect(new CreateTokenEffect(new SpiritWhiteToken("ISD"), 2));
+        this.getSpellAbility().addEffect(new CreateTokenEffect(new SpiritWhiteToken(), 2));
         // Flashback {1}{B}
         this.addAbility(new FlashbackAbility(new ManaCostsImpl("{1}{B}"), TimingRule.SORCERY));
     }
diff --git a/Mage.Sets/src/mage/cards/m/MausoleumGuard.java b/Mage.Sets/src/mage/cards/m/MausoleumGuard.java
index e25cd9df41..37cc2384c7 100644
--- a/Mage.Sets/src/mage/cards/m/MausoleumGuard.java
+++ b/Mage.Sets/src/mage/cards/m/MausoleumGuard.java
@@ -1,7 +1,5 @@
-
 package mage.cards.m;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.common.DiesTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
@@ -11,13 +9,15 @@ import mage.constants.CardType;
 import mage.constants.SubType;
 import mage.game.permanent.token.SpiritWhiteToken;
 
+import java.util.UUID;
+
 /**
  * @author nantuko
  */
 public final class MausoleumGuard extends CardImpl {
 
     public MausoleumGuard(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{W}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}");
         this.subtype.add(SubType.HUMAN);
         this.subtype.add(SubType.SCOUT);
 
@@ -25,7 +25,7 @@ public final class MausoleumGuard extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Mausoleum Guard dies, create two 1/1 white Spirit creature tokens with flying.
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new SpiritWhiteToken("ISD"), 2)));
+        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new SpiritWhiteToken(), 2)));
     }
 
     public MausoleumGuard(final MausoleumGuard card) {
diff --git a/Mage.Sets/src/mage/cards/m/MidnightHaunting.java b/Mage.Sets/src/mage/cards/m/MidnightHaunting.java
index d54a8eef61..93e947eacd 100644
--- a/Mage.Sets/src/mage/cards/m/MidnightHaunting.java
+++ b/Mage.Sets/src/mage/cards/m/MidnightHaunting.java
@@ -1,25 +1,24 @@
-
 package mage.cards.m;
 
-import java.util.UUID;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.game.permanent.token.SpiritWhiteToken;
 
+import java.util.UUID;
+
 /**
- *
  * @author nantuko
  */
 public final class MidnightHaunting extends CardImpl {
 
     public MidnightHaunting(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{W}");
+        super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{W}");
 
 
         // Create two 1/1 white Spirit creature tokens with flying.
-        this.getSpellAbility().addEffect(new CreateTokenEffect(new SpiritWhiteToken("ISD"), 2));
+        this.getSpellAbility().addEffect(new CreateTokenEffect(new SpiritWhiteToken(), 2));
     }
 
     public MidnightHaunting(final MidnightHaunting card) {
diff --git a/Mage.Sets/src/mage/cards/p/ProsperousPirates.java b/Mage.Sets/src/mage/cards/p/ProsperousPirates.java
index 8d3de1a966..06183091c9 100644
--- a/Mage.Sets/src/mage/cards/p/ProsperousPirates.java
+++ b/Mage.Sets/src/mage/cards/p/ProsperousPirates.java
@@ -1,7 +1,5 @@
-
 package mage.cards.p;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
@@ -11,8 +9,9 @@ import mage.constants.CardType;
 import mage.constants.SubType;
 import mage.game.permanent.token.TreasureToken;
 
+import java.util.UUID;
+
 /**
- *
  * @author TheElk801
  */
 public final class ProsperousPirates extends CardImpl {
@@ -26,7 +25,7 @@ public final class ProsperousPirates extends CardImpl {
         this.toughness = new MageInt(4);
 
         // When Prosperous Pirates enters the battlefield, create two colorless Treasure artifact tokens with "{T}, Sacrifice this artifact: Add one mana of any color."
-        this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new TreasureToken("XLN"), 2)));
+        this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new TreasureToken(), 2)));
     }
 
     public ProsperousPirates(final ProsperousPirates card) {
diff --git a/Mage.Sets/src/mage/cards/r/RequiemAngel.java b/Mage.Sets/src/mage/cards/r/RequiemAngel.java
index 20cfb3f4db..0ccb69b287 100644
--- a/Mage.Sets/src/mage/cards/r/RequiemAngel.java
+++ b/Mage.Sets/src/mage/cards/r/RequiemAngel.java
@@ -1,7 +1,5 @@
-
 package mage.cards.r;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.common.DiesCreatureTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
@@ -16,8 +14,9 @@ import mage.filter.predicate.Predicates;
 import mage.filter.predicate.permanent.AnotherPredicate;
 import mage.game.permanent.token.SpiritWhiteToken;
 
+import java.util.UUID;
+
 /**
- *
  * @author intimidatingant
  */
 public final class RequiemAngel extends CardImpl {
@@ -30,7 +29,7 @@ public final class RequiemAngel extends CardImpl {
     }
 
     public RequiemAngel(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{5}{W}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{W}");
         this.subtype.add(SubType.ANGEL);
 
         this.power = new MageInt(5);
@@ -39,7 +38,7 @@ public final class RequiemAngel extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
 
         // Whenever another non-Spirit creature you control dies, create a 1/1 white Spirit creature token with flying.
-        this.addAbility(new DiesCreatureTriggeredAbility(new CreateTokenEffect(new SpiritWhiteToken("ISD"), 1), false, filter));
+        this.addAbility(new DiesCreatureTriggeredAbility(new CreateTokenEffect(new SpiritWhiteToken(), 1), false, filter));
     }
 
     public RequiemAngel(final RequiemAngel card) {
diff --git a/Mage.Sets/src/mage/cards/r/ResearchDevelopment.java b/Mage.Sets/src/mage/cards/r/ResearchDevelopment.java
index cc9497ea6f..6bfeeb052f 100644
--- a/Mage.Sets/src/mage/cards/r/ResearchDevelopment.java
+++ b/Mage.Sets/src/mage/cards/r/ResearchDevelopment.java
@@ -1,28 +1,23 @@
-
 package mage.cards.r;
 
-import java.util.Set;
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.CreateTokenEffect;
-import mage.cards.Card;
-import mage.cards.CardSetInfo;
-import mage.cards.Cards;
-import mage.cards.CardsImpl;
-import mage.cards.SplitCard;
+import mage.cards.*;
 import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.constants.SpellAbilityType;
 import mage.constants.Zone;
 import mage.filter.FilterCard;
 import mage.game.Game;
-import mage.game.permanent.token.ElementalToken;
+import mage.game.permanent.token.ResearchDevelopmentToken;
 import mage.players.Player;
 import mage.target.TargetCard;
 
+import java.util.Set;
+import java.util.UUID;
+
 /**
- *
  * @author magenoxx
  */
 public final class ResearchDevelopment extends SplitCard {
@@ -144,7 +139,7 @@ class DevelopmentEffect extends OneShotEffect {
                 for (UUID opponentUuid : opponents) {
                     Player opponent = game.getPlayer(opponentUuid);
                     if (opponent != null && opponent.chooseUse(Outcome.Detriment,
-                            "Allow " + player.getLogName() + " to draw a card instead? (" + Integer.toString(i + 1) + ')', source, game)) {
+                            "Allow " + player.getLogName() + " to draw a card instead? (" + (i + 1) + ')', source, game)) {
                         game.informPlayers(opponent.getLogName() + " had chosen to let " + player.getLogName() + " draw a card.");
                         player.drawCards(1, source.getSourceId(), game);
                         putToken = false;
@@ -152,7 +147,7 @@ class DevelopmentEffect extends OneShotEffect {
                     }
                 }
                 if (putToken) {
-                    new CreateTokenEffect(new ElementalToken("DIS", 1)).apply(game, source);
+                    new CreateTokenEffect(new ResearchDevelopmentToken()).apply(game, source);
                 }
             }
 
diff --git a/Mage.Sets/src/mage/cards/r/RevelInRiches.java b/Mage.Sets/src/mage/cards/r/RevelInRiches.java
index b1011b4465..ca8bf841b2 100644
--- a/Mage.Sets/src/mage/cards/r/RevelInRiches.java
+++ b/Mage.Sets/src/mage/cards/r/RevelInRiches.java
@@ -1,7 +1,5 @@
-
 package mage.cards.r;
 
-import java.util.UUID;
 import mage.abilities.TriggeredAbility;
 import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
 import mage.abilities.common.DiesCreatureTriggeredAbility;
@@ -19,8 +17,9 @@ import mage.filter.FilterPermanent;
 import mage.filter.common.FilterCreaturePermanent;
 import mage.game.permanent.token.TreasureToken;
 
+import java.util.UUID;
+
 /**
- *
  * @author TheElk801
  */
 public final class RevelInRiches extends CardImpl {
@@ -37,7 +36,7 @@ public final class RevelInRiches extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{B}");
 
         // Whenever a creature an opponent controls dies, create a colorless Treasure artifact token with "{T}, Sacrifice this artifact: Add one mana of any color."
-        this.addAbility(new DiesCreatureTriggeredAbility(new CreateTokenEffect(new TreasureToken("XLN")), false, filter));
+        this.addAbility(new DiesCreatureTriggeredAbility(new CreateTokenEffect(new TreasureToken()), false, filter));
         // At the beginning of your upkeep, if you control ten or more Treasures, you win the game.
         TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new WinGameSourceControllerEffect(), TargetController.YOU, false);
         this.addAbility(new ConditionalInterveningIfTriggeredAbility(
diff --git a/Mage.Sets/src/mage/cards/s/SandsteppeOutcast.java b/Mage.Sets/src/mage/cards/s/SandsteppeOutcast.java
index fcc419c66a..72b0150e3e 100644
--- a/Mage.Sets/src/mage/cards/s/SandsteppeOutcast.java
+++ b/Mage.Sets/src/mage/cards/s/SandsteppeOutcast.java
@@ -1,7 +1,5 @@
-
 package mage.cards.s;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.Mode;
@@ -15,14 +13,15 @@ import mage.constants.SubType;
 import mage.counters.CounterType;
 import mage.game.permanent.token.SpiritWhiteToken;
 
+import java.util.UUID;
+
 /**
- *
  * @author emerald000
  */
 public final class SandsteppeOutcast extends CardImpl {
 
     public SandsteppeOutcast(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}");
         this.subtype.add(SubType.HUMAN);
         this.subtype.add(SubType.WARRIOR);
         this.power = new MageInt(2);
@@ -31,12 +30,12 @@ public final class SandsteppeOutcast extends CardImpl {
         // When Sandsteppe Outcast enters the battlefield, choose one -
         // * Put a +1/+1 counter on Sandsteppe Outcast.
         Ability ability = new EntersBattlefieldTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()));
-        
+
         // * Create a 1/1 white Spirit creature token with flying. 
         Mode mode = new Mode();
-        mode.addEffect(new CreateTokenEffect(new SpiritWhiteToken("FRF")));
+        mode.addEffect(new CreateTokenEffect(new SpiritWhiteToken()));
         ability.addMode(mode);
-        this.addAbility(ability);  
+        this.addAbility(ability);
     }
 
     public SandsteppeOutcast(final SandsteppeOutcast card) {
diff --git a/Mage.Sets/src/mage/cards/s/SaprolingInfestation.java b/Mage.Sets/src/mage/cards/s/SaprolingInfestation.java
index 7fd3df301e..3a604196c9 100644
--- a/Mage.Sets/src/mage/cards/s/SaprolingInfestation.java
+++ b/Mage.Sets/src/mage/cards/s/SaprolingInfestation.java
@@ -13,14 +13,13 @@ import mage.game.permanent.token.SaprolingToken;
 import java.util.UUID;
 
 /**
- *
  * @author noahg
  */
 public final class SaprolingInfestation extends CardImpl {
 
     public SaprolingInfestation(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{G}");
-        
+
 
         // Whenever a player kicks a spell, you put a 1/1 green Saproling creature token onto the battlefield.
         this.addAbility(new SaprolingInfestationTriggeredAbility());
@@ -38,7 +37,7 @@ public final class SaprolingInfestation extends CardImpl {
     class SaprolingInfestationTriggeredAbility extends TriggeredAbilityImpl {
 
         SaprolingInfestationTriggeredAbility() {
-            super(Zone.BATTLEFIELD, new CreateTokenEffect(new SaprolingToken("INV")), false);
+            super(Zone.BATTLEFIELD, new CreateTokenEffect(new SaprolingToken()), false);
         }
 
         SaprolingInfestationTriggeredAbility(final SaprolingInfestationTriggeredAbility ability) {
diff --git a/Mage.Sets/src/mage/cards/s/SpectralReserves.java b/Mage.Sets/src/mage/cards/s/SpectralReserves.java
index feb8019187..22caf3fa9c 100644
--- a/Mage.Sets/src/mage/cards/s/SpectralReserves.java
+++ b/Mage.Sets/src/mage/cards/s/SpectralReserves.java
@@ -1,7 +1,5 @@
-
 package mage.cards.s;
 
-import java.util.UUID;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.abilities.effects.common.GainLifeEffect;
@@ -10,17 +8,18 @@ import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.game.permanent.token.SpiritWhiteToken;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class SpectralReserves extends CardImpl {
 
     public SpectralReserves(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{W}");
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{W}");
 
         // Create two 1/1 white Spirit creature tokens with flying. You gain 2 life.
-        Effect effect = new CreateTokenEffect(new SpiritWhiteToken("EMN"), 2);
+        Effect effect = new CreateTokenEffect(new SpiritWhiteToken(), 2);
         effect.setText("Create two 1/1 white Spirit creature tokens with flying");
         this.getSpellAbility().addEffect(effect);
         this.getSpellAbility().addEffect(new GainLifeEffect(2));
diff --git a/Mage.Sets/src/mage/cards/s/SpiritBonds.java b/Mage.Sets/src/mage/cards/s/SpiritBonds.java
index 2827034a87..2bdb334f1f 100644
--- a/Mage.Sets/src/mage/cards/s/SpiritBonds.java
+++ b/Mage.Sets/src/mage/cards/s/SpiritBonds.java
@@ -1,7 +1,5 @@
-
 package mage.cards.s;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
@@ -25,8 +23,9 @@ import mage.game.permanent.token.SpiritWhiteToken;
 import mage.target.common.TargetControlledCreaturePermanent;
 import mage.target.common.TargetControlledPermanent;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class SpiritBonds extends CardImpl {
@@ -42,16 +41,16 @@ public final class SpiritBonds extends CardImpl {
     }
 
     public SpiritBonds(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{W}");
+        super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}");
 
 
         // Whenever a nontoken creature enters the battlefield under your control, you may pay {W}. If you do, but a 1/1 white Spirit creature token with flying into play.
-        this.addAbility(new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, new DoIfCostPaid(new CreateTokenEffect(new SpiritWhiteToken("M15")), new ManaCostsImpl("{W}")), filterNontoken, false));
-        
+        this.addAbility(new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, new DoIfCostPaid(new CreateTokenEffect(new SpiritWhiteToken()), new ManaCostsImpl("{W}")), filterNontoken, false));
+
         // {1}{W}, Sacrifice a Spirit: Target non-Spirit creature you control gains indestructible until end of turn.
         Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD,
                 new GainAbilityTargetEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn), new ManaCostsImpl("{1}{W}"));
-        ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(1,1,filterSpirit, true)));
+        ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(1, 1, filterSpirit, true)));
         ability.addTarget(new TargetControlledCreaturePermanent(filter));
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/t/TeysaOrzhovScion.java b/Mage.Sets/src/mage/cards/t/TeysaOrzhovScion.java
index 16a82f9d07..520f0b35a5 100644
--- a/Mage.Sets/src/mage/cards/t/TeysaOrzhovScion.java
+++ b/Mage.Sets/src/mage/cards/t/TeysaOrzhovScion.java
@@ -1,7 +1,5 @@
-
 package mage.cards.t;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.ObjectColor;
 import mage.abilities.Ability;
@@ -12,11 +10,7 @@ import mage.abilities.effects.common.CreateTokenEffect;
 import mage.abilities.effects.common.ExileTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.CardType;
-import mage.constants.SubType;
-import mage.constants.SuperType;
-import mage.constants.TargetController;
-import mage.constants.Zone;
+import mage.constants.*;
 import mage.filter.common.FilterControlledCreaturePermanent;
 import mage.filter.common.FilterCreaturePermanent;
 import mage.filter.predicate.mageobject.ColorPredicate;
@@ -25,14 +19,16 @@ import mage.game.permanent.token.SpiritWhiteToken;
 import mage.target.common.TargetControlledCreaturePermanent;
 import mage.target.common.TargetCreaturePermanent;
 
+import java.util.UUID;
+
 /**
- *
  * @author emerald000
  */
 public final class TeysaOrzhovScion extends CardImpl {
-    
+
     private static final FilterControlledCreaturePermanent filterWhite = new FilterControlledCreaturePermanent("three white creatures");
     private static final FilterCreaturePermanent filterBlack = new FilterCreaturePermanent("another black creature you control");
+
     static {
         filterWhite.add(new ColorPredicate(ObjectColor.WHITE));
         filterBlack.add(new ColorPredicate(ObjectColor.BLACK));
@@ -41,7 +37,7 @@ public final class TeysaOrzhovScion extends CardImpl {
     }
 
     public TeysaOrzhovScion(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{W}{B}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{B}");
         this.addSuperType(SuperType.LEGENDARY);
         this.subtype.add(SubType.HUMAN);
         this.subtype.add(SubType.ADVISOR);
@@ -53,9 +49,9 @@ public final class TeysaOrzhovScion extends CardImpl {
         Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileTargetEffect(), new SacrificeTargetCost(new TargetControlledCreaturePermanent(3, 3, filterWhite, true)));
         ability.addTarget(new TargetCreaturePermanent());
         this.addAbility(ability);
-        
+
         // Whenever another black creature you control dies, create a 1/1 white Spirit creature token with flying.
-        this.addAbility(new DiesCreatureTriggeredAbility(new CreateTokenEffect(new SpiritWhiteToken("GPT")), false, filterBlack));
+        this.addAbility(new DiesCreatureTriggeredAbility(new CreateTokenEffect(new SpiritWhiteToken()), false, filterBlack));
     }
 
     public TeysaOrzhovScion(final TeysaOrzhovScion card) {
diff --git a/Mage.Sets/src/mage/cards/t/TreasureMap.java b/Mage.Sets/src/mage/cards/t/TreasureMap.java
index 7c0035dcbd..b0abbf0a74 100644
--- a/Mage.Sets/src/mage/cards/t/TreasureMap.java
+++ b/Mage.Sets/src/mage/cards/t/TreasureMap.java
@@ -1,7 +1,5 @@
-
 package mage.cards.t;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.common.TapSourceCost;
@@ -21,8 +19,9 @@ import mage.game.permanent.Permanent;
 import mage.game.permanent.token.TreasureToken;
 import mage.players.Player;
 
+import java.util.UUID;
+
 /**
- *
  * @author TheElk801
  */
 public final class TreasureMap extends CardImpl {
@@ -81,7 +80,7 @@ class TreasureMapEffect extends OneShotEffect {
                 if (counters > 2) {
                     permanent.removeCounters("landmark", counters, game);
                     new TransformSourceEffect(true).apply(game, source);
-                    new CreateTokenEffect(new TreasureToken("XLN"), 3).apply(game, source);
+                    new CreateTokenEffect(new TreasureToken(), 3).apply(game, source);
                 }
                 return true;
             }
diff --git a/Mage.Sets/src/mage/cards/t/TriplicateSpirits.java b/Mage.Sets/src/mage/cards/t/TriplicateSpirits.java
index fcdd7040df..c78169a471 100644
--- a/Mage.Sets/src/mage/cards/t/TriplicateSpirits.java
+++ b/Mage.Sets/src/mage/cards/t/TriplicateSpirits.java
@@ -1,7 +1,5 @@
-
 package mage.cards.t;
 
-import java.util.UUID;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.abilities.keyword.ConvokeAbility;
 import mage.cards.CardImpl;
@@ -9,21 +7,22 @@ import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.game.permanent.token.SpiritWhiteToken;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class TriplicateSpirits extends CardImpl {
 
     public TriplicateSpirits(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{4}{W}{W}");
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{W}{W}");
 
 
         // Convoke
         this.addAbility(new ConvokeAbility());
         // Create three 1/1 white Spirit creature tokens with flying.
-        this.getSpellAbility().addEffect(new CreateTokenEffect(new SpiritWhiteToken("M15"), 3));
-        
+        this.getSpellAbility().addEffect(new CreateTokenEffect(new SpiritWhiteToken(), 3));
+
     }
 
     public TriplicateSpirits(final TriplicateSpirits card) {
diff --git a/Mage.Sets/src/mage/cards/t/TwilightDrover.java b/Mage.Sets/src/mage/cards/t/TwilightDrover.java
index 9b230eea48..2a7f1c2dd6 100644
--- a/Mage.Sets/src/mage/cards/t/TwilightDrover.java
+++ b/Mage.Sets/src/mage/cards/t/TwilightDrover.java
@@ -1,7 +1,5 @@
-
 package mage.cards.t;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.LeavesBattlefieldAllTriggeredAbility;
@@ -20,8 +18,9 @@ import mage.filter.common.FilterCreaturePermanent;
 import mage.filter.predicate.permanent.TokenPredicate;
 import mage.game.permanent.token.SpiritWhiteToken;
 
+import java.util.UUID;
+
 /**
- *
  * @author emerald000
  */
 public final class TwilightDrover extends CardImpl {
@@ -33,7 +32,7 @@ public final class TwilightDrover extends CardImpl {
     }
 
     public TwilightDrover(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}");
         this.subtype.add(SubType.SPIRIT);
 
         this.power = new MageInt(1);
@@ -43,7 +42,7 @@ public final class TwilightDrover extends CardImpl {
         this.addAbility(new LeavesBattlefieldAllTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), filter));
 
         // {2}{W}, Remove a +1/+1 counter from Twilight Drover: Create two 1/1 white Spirit creature tokens with flying.
-        Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new SpiritWhiteToken("RAV"), 2), new ManaCostsImpl<>("{2}{W}"));
+        Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new SpiritWhiteToken(), 2), new ManaCostsImpl<>("{2}{W}"));
         ability.addCost(new RemoveCountersSourceCost(CounterType.P1P1.createInstance()));
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/w/WortTheRaidmother.java b/Mage.Sets/src/mage/cards/w/WortTheRaidmother.java
index 9e7608a8e1..a45c1d1b00 100644
--- a/Mage.Sets/src/mage/cards/w/WortTheRaidmother.java
+++ b/Mage.Sets/src/mage/cards/w/WortTheRaidmother.java
@@ -1,7 +1,5 @@
-
 package mage.cards.w;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.ObjectColor;
 import mage.abilities.Ability;
@@ -17,12 +15,13 @@ import mage.filter.common.FilterInstantOrSorcerySpell;
 import mage.filter.predicate.Predicates;
 import mage.filter.predicate.mageobject.ColorPredicate;
 import mage.game.Game;
-import mage.game.permanent.token.WortTheRaidmotherToken;
+import mage.game.permanent.token.GoblinWarriorToken;
 import mage.game.stack.Spell;
 import mage.game.stack.StackObject;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class WortTheRaidmother extends CardImpl {
@@ -36,7 +35,7 @@ public final class WortTheRaidmother extends CardImpl {
         this.toughness = new MageInt(3);
 
         // When Wort, the Raidmother enters the battlefield, create two 1/1 red and green Goblin Warrior creature tokens.
-        this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new WortTheRaidmotherToken(), 2), false));
+        this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new GoblinWarriorToken(), 2), false));
 
         // Each red or green instant or sorcery spell you cast has conspire.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new WortGainConspireEffect()));
@@ -59,6 +58,7 @@ class WortGainConspireEffect extends ContinuousEffectImpl {
     static {
         filter.add(Predicates.or(new ColorPredicate(ObjectColor.RED), new ColorPredicate(ObjectColor.GREEN)));
     }
+
     private final ConspireAbility conspireAbility;
 
     public WortGainConspireEffect() {
diff --git a/Mage.Sets/src/mage/cards/z/ZaxaraTheExemplary.java b/Mage.Sets/src/mage/cards/z/ZaxaraTheExemplary.java
index 257729d52b..a5b097d41d 100644
--- a/Mage.Sets/src/mage/cards/z/ZaxaraTheExemplary.java
+++ b/Mage.Sets/src/mage/cards/z/ZaxaraTheExemplary.java
@@ -6,7 +6,6 @@ import mage.abilities.TriggeredAbility;
 import mage.abilities.TriggeredAbilityImpl;
 import mage.abilities.costs.common.TapSourceCost;
 import mage.abilities.effects.OneShotEffect;
-import mage.abilities.effects.common.CreateTokenEffect;
 import mage.abilities.effects.mana.AddManaOfAnyColorEffect;
 import mage.abilities.keyword.DeathtouchAbility;
 import mage.abilities.mana.SimpleManaAbility;
@@ -18,7 +17,7 @@ import mage.game.Game;
 import mage.game.events.GameEvent;
 import mage.game.permanent.Permanent;
 import mage.game.permanent.token.Token;
-import mage.game.permanent.token.TokenImpl;
+import mage.game.permanent.token.ZaxaraTheExemplaryHydraToken;
 import mage.game.stack.Spell;
 import mage.players.Player;
 
@@ -144,25 +143,4 @@ class ZaxaraTheExemplaryHydraTokenEffect extends OneShotEffect {
         }
         return false;
     }
-}
-
-class ZaxaraTheExemplaryHydraToken extends TokenImpl {
-    public ZaxaraTheExemplaryHydraToken() {
-        super("Hydra", "0/0 green Hydra creature token");
-        setExpansionSetCodeForImage("C20");
-        cardType.add(CardType.CREATURE);
-        color.setGreen(true);
-        subtype.add(SubType.HYDRA);
-        power = new MageInt(0);
-        toughness = new MageInt(0);
-    }
-
-    private ZaxaraTheExemplaryHydraToken(final ZaxaraTheExemplaryHydraToken token) {
-        super(token);
-    }
-
-    @Override
-    public ZaxaraTheExemplaryHydraToken copy() {
-        return new ZaxaraTheExemplaryHydraToken(this);
-    }
 }
\ No newline at end of file
diff --git a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java
index 2c2d1e3e4e..d8fcf90517 100644
--- a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java
+++ b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java
@@ -701,6 +701,10 @@ public class VerifyCardDataTest {
         if (errorsList.size() > 0) {
             Assert.fail("Found token errors: " + errorsList.size());
         }
+
+        // TODO: all token must have correct availableImageSetCodes (all sets with that token)
+        // Some sets have original card, but don't have token card at all. So you must use scryfall tokens list above to find
+        // all token's sets and compare with xmage
     }
 
     @Test
diff --git a/Mage/src/main/java/mage/game/permanent/token/BeastToken2.java b/Mage/src/main/java/mage/game/permanent/token/BeastToken2.java
index f59cbc94a6..a1d9745f62 100644
--- a/Mage/src/main/java/mage/game/permanent/token/BeastToken2.java
+++ b/Mage/src/main/java/mage/game/permanent/token/BeastToken2.java
@@ -16,7 +16,7 @@ public final class BeastToken2 extends TokenImpl {
     static final private List<String> tokenImageSets = new ArrayList<>();
 
     static {
-        tokenImageSets.addAll(Arrays.asList("ZEN", "C14", "DDD", "C15", "DD3GVL", "MM3", "CMA", "E01", "C19"));
+        tokenImageSets.addAll(Arrays.asList("ZEN", "C14", "DDD", "C15", "DD3GVL", "MM3", "CMA", "E01", "C19", "C20"));
     }
 
     public BeastToken2() {
diff --git a/Mage/src/main/java/mage/game/permanent/token/BirdToken.java b/Mage/src/main/java/mage/game/permanent/token/BirdToken.java
index c73f04867b..fd7a4e8712 100644
--- a/Mage/src/main/java/mage/game/permanent/token/BirdToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/BirdToken.java
@@ -1,15 +1,13 @@
-
-
 package mage.game.permanent.token;
 
-import java.util.Arrays;
 import mage.MageInt;
 import mage.abilities.keyword.FlyingAbility;
 import mage.constants.CardType;
 import mage.constants.SubType;
 
+import java.util.Arrays;
+
 /**
- *
  * @author LoneFox
  */
 public final class BirdToken extends TokenImpl {
@@ -22,6 +20,7 @@ public final class BirdToken extends TokenImpl {
         power = new MageInt(1);
         toughness = new MageInt(1);
         addAbility(FlyingAbility.getInstance());
+
         availableImageSetCodes.addAll(Arrays.asList("BNG", "RTR", "ZEN", "C16", "MM3", "DGM"));
     }
 
@@ -30,7 +29,7 @@ public final class BirdToken extends TokenImpl {
     }
 
     @Override
-        public BirdToken copy() {
+    public BirdToken copy() {
         return new BirdToken(this);
     }
 
diff --git a/Mage/src/main/java/mage/game/permanent/token/ElementalToken.java b/Mage/src/main/java/mage/game/permanent/token/ElementalToken.java
deleted file mode 100644
index 5369b5bff6..0000000000
--- a/Mage/src/main/java/mage/game/permanent/token/ElementalToken.java
+++ /dev/null
@@ -1,63 +0,0 @@
-
-
-package mage.game.permanent.token;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import mage.MageInt;
-import mage.abilities.keyword.HasteAbility;
-import mage.constants.CardType;
-import mage.constants.SubType;
-
-/**
- *
- * @author magenoxx
- */
-public final class ElementalToken extends TokenImpl {
-
-    static final private List<String> tokenImageSets = new ArrayList<>();
-
-    static {
-        tokenImageSets.addAll(Arrays.asList("OGW", "CON", "DIS"));
-    }
-
-    public ElementalToken() {
-        this ("OGW", 0);
-    }
-
-    public ElementalToken(String setCode, int tokenType) {
-        super("Elemental", "3/1 red Elemental creature token");
-        availableImageSetCodes = tokenImageSets;
-        setOriginalExpansionSetCode(setCode);
-        cardType.add(CardType.CREATURE);
-        color.setRed(true);
-        subtype.add(SubType.ELEMENTAL);
-        power = new MageInt(3);
-        toughness = new MageInt(1);
-
-        this.setOriginalExpansionSetCode("CON");
-    }
-    
-    public ElementalToken(String setCode, int tokenType, boolean hasHaste) {
-        super("Elemental", "3/1 red Elemental creature token");
-        setTokenType(tokenType);
-        availableImageSetCodes = tokenImageSets;
-        setOriginalExpansionSetCode(setCode);
-        cardType.add(CardType.CREATURE);
-        color.setRed(true);
-        subtype.add(SubType.ELEMENTAL);
-        power = new MageInt(3);
-        toughness = new MageInt(1);
-        
-        if (hasHaste) this.addAbility(HasteAbility.getInstance());
-    }
-
-    public ElementalToken(final ElementalToken token) {
-        super(token);
-    }
-
-    public ElementalToken copy() {
-        return new ElementalToken(this);
-    }
-}
diff --git a/Mage/src/main/java/mage/game/permanent/token/ElementalTokenWithHaste.java b/Mage/src/main/java/mage/game/permanent/token/ElementalTokenWithHaste.java
new file mode 100644
index 0000000000..f916272c55
--- /dev/null
+++ b/Mage/src/main/java/mage/game/permanent/token/ElementalTokenWithHaste.java
@@ -0,0 +1,51 @@
+package mage.game.permanent.token;
+
+import mage.MageInt;
+import mage.abilities.keyword.HasteAbility;
+import mage.constants.CardType;
+import mage.constants.SubType;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @author magenoxx
+ */
+public final class ElementalTokenWithHaste extends TokenImpl {
+
+    static final private List<String> tokenImageSets = new ArrayList<>();
+
+    static {
+        tokenImageSets.addAll(Arrays.asList("C20", "MBP", "OGW", "SOK", "MRD"));
+    }
+
+    public ElementalTokenWithHaste() {
+        super("Elemental", "3/1 red Elemental creature token with haste");
+        availableImageSetCodes = tokenImageSets;
+        cardType.add(CardType.CREATURE);
+        color.setRed(true);
+        subtype.add(SubType.ELEMENTAL);
+        power = new MageInt(3);
+        toughness = new MageInt(1);
+        this.addAbility(HasteAbility.getInstance());
+
+        if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("OGW")) {
+            setTokenType(2);
+        }
+        if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("C20")) {
+            setTokenType(1);
+        }
+        if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("SOK")) {
+            setTokenType(1);
+        }
+    }
+
+    public ElementalTokenWithHaste(final ElementalTokenWithHaste token) {
+        super(token);
+    }
+
+    public ElementalTokenWithHaste copy() {
+        return new ElementalTokenWithHaste(this);
+    }
+}
diff --git a/Mage/src/main/java/mage/game/permanent/token/WortTheRaidmotherToken.java b/Mage/src/main/java/mage/game/permanent/token/GoblinWarriorToken.java
similarity index 61%
rename from Mage/src/main/java/mage/game/permanent/token/WortTheRaidmotherToken.java
rename to Mage/src/main/java/mage/game/permanent/token/GoblinWarriorToken.java
index b59b644580..ec79599e77 100644
--- a/Mage/src/main/java/mage/game/permanent/token/WortTheRaidmotherToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/GoblinWarriorToken.java
@@ -1,17 +1,17 @@
-
-
 package mage.game.permanent.token;
+
+import mage.MageInt;
 import mage.constants.CardType;
 import mage.constants.SubType;
-import mage.MageInt;
+
+import java.util.Arrays;
 
 /**
- *
  * @author spjspj
  */
-public final class WortTheRaidmotherToken extends TokenImpl {
+public final class GoblinWarriorToken extends TokenImpl {
 
-    public WortTheRaidmotherToken() {
+    public GoblinWarriorToken() {
         super("Goblin Warrior", "1/1 red and green Goblin Warrior creature token");
         cardType.add(CardType.CREATURE);
         color.setRed(true);
@@ -20,13 +20,15 @@ public final class WortTheRaidmotherToken extends TokenImpl {
         subtype.add(SubType.WARRIOR);
         power = new MageInt(1);
         toughness = new MageInt(1);
+
+        availableImageSetCodes.addAll(Arrays.asList("C20"));
     }
 
-    public WortTheRaidmotherToken(final WortTheRaidmotherToken token) {
+    public GoblinWarriorToken(final GoblinWarriorToken token) {
         super(token);
     }
 
-    public WortTheRaidmotherToken copy() {
-        return new WortTheRaidmotherToken(this);
+    public GoblinWarriorToken copy() {
+        return new GoblinWarriorToken(this);
     }
 }
diff --git a/Mage/src/main/java/mage/game/permanent/token/GreenAndWhiteElementalToken.java b/Mage/src/main/java/mage/game/permanent/token/GreenAndWhiteElementalToken.java
new file mode 100644
index 0000000000..ca444ebbb5
--- /dev/null
+++ b/Mage/src/main/java/mage/game/permanent/token/GreenAndWhiteElementalToken.java
@@ -0,0 +1,41 @@
+package mage.game.permanent.token;
+
+import mage.MageInt;
+import mage.abilities.keyword.VigilanceAbility;
+import mage.constants.CardType;
+import mage.constants.SubType;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @author JayDi85
+ */
+public final class GreenAndWhiteElementalToken extends TokenImpl {
+
+    static final private List<String> tokenImageSets = new ArrayList<>();
+
+    static {
+        tokenImageSets.addAll(Arrays.asList("GK1", "PTC", "RTR"));
+    }
+
+    public GreenAndWhiteElementalToken() {
+        super("Elemental", "8/8 green and white Elemental creature token with vigilance");
+        cardType.add(CardType.CREATURE);
+        color.setGreen(true);
+        color.setWhite(true);
+        this.subtype.add(SubType.ELEMENTAL);
+        power = new MageInt(8);
+        toughness = new MageInt(8);
+        this.addAbility(VigilanceAbility.getInstance());
+    }
+
+    public GreenAndWhiteElementalToken(final GreenAndWhiteElementalToken token) {
+        super(token);
+    }
+
+    public GreenAndWhiteElementalToken copy() {
+        return new GreenAndWhiteElementalToken(this);
+    }
+}
diff --git a/Mage/src/main/java/mage/game/permanent/token/HornetQueenInsectToken.java b/Mage/src/main/java/mage/game/permanent/token/HornetQueenInsectToken.java
index 91fea8b5ef..cc93e12e1d 100644
--- a/Mage/src/main/java/mage/game/permanent/token/HornetQueenInsectToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/HornetQueenInsectToken.java
@@ -1,21 +1,20 @@
-
-
 package mage.game.permanent.token;
-import mage.constants.CardType;
-import mage.constants.SubType;
+
 import mage.MageInt;
 import mage.abilities.keyword.DeathtouchAbility;
 import mage.abilities.keyword.FlyingAbility;
+import mage.constants.CardType;
+import mage.constants.SubType;
+
+import java.util.Arrays;
 
 /**
- *
  * @author spjspj
  */
 public final class HornetQueenInsectToken extends TokenImpl {
 
     public HornetQueenInsectToken() {
         super("Insect", "1/1 green Insect creature token with flying and deathtouch");
-        setOriginalExpansionSetCode("M15");
         cardType.add(CardType.CREATURE);
         color.setGreen(true);
         subtype.add(SubType.INSECT);
@@ -23,6 +22,8 @@ public final class HornetQueenInsectToken extends TokenImpl {
         toughness = new MageInt(1);
         addAbility(FlyingAbility.getInstance());
         addAbility(DeathtouchAbility.getInstance());
+
+        availableImageSetCodes = Arrays.asList("M15", "C20");
     }
 
     public HornetQueenInsectToken(final HornetQueenInsectToken token) {
diff --git a/Mage/src/main/java/mage/game/permanent/token/HumanToken.java b/Mage/src/main/java/mage/game/permanent/token/HumanToken.java
index 79f039152e..f0df12ed62 100644
--- a/Mage/src/main/java/mage/game/permanent/token/HumanToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/HumanToken.java
@@ -18,7 +18,7 @@ public final class HumanToken extends TokenImpl {
         subtype.add(SubType.HUMAN);
         power = new MageInt(1);
         toughness = new MageInt(1);
-        availableImageSetCodes.addAll(Arrays.asList("DKA", "AVR", "FNMP", "RNA", "ELD", "C19"));
+        availableImageSetCodes.addAll(Arrays.asList("DKA", "AVR", "FNMP", "RNA", "ELD", "C19", "C20"));
     }
 
     public HumanToken(final HumanToken token) {
diff --git a/Mage/src/main/java/mage/game/permanent/token/HydraBroodmasterToken.java b/Mage/src/main/java/mage/game/permanent/token/HydraBroodmasterToken.java
index 5dace1e254..82b0acf3bb 100644
--- a/Mage/src/main/java/mage/game/permanent/token/HydraBroodmasterToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/HydraBroodmasterToken.java
@@ -1,29 +1,29 @@
-
-
 package mage.game.permanent.token;
 
+import mage.MageInt;
 import mage.constants.CardType;
 import mage.constants.SubType;
-import mage.MageInt;
+
+import java.util.Arrays;
 
 /**
- *
  * @author spjspj
  */
 public final class HydraBroodmasterToken extends TokenImpl {
 
     public HydraBroodmasterToken() {
-        this(1,1);
+        this(1, 1);
     }
 
     public HydraBroodmasterToken(int power, int toughness) {
         super("Hydra", "green Hydra creature token");
-        this.setOriginalExpansionSetCode("JOU");
         cardType.add(CardType.CREATURE);
         color.setGreen(true);
         subtype.add(SubType.HYDRA);
         this.power = new MageInt(power);
         this.toughness = new MageInt(toughness);
+
+        availableImageSetCodes = Arrays.asList("JOU");
     }
 
     public HydraBroodmasterToken(final HydraBroodmasterToken token) {
diff --git a/Mage/src/main/java/mage/game/permanent/token/ResearchDevelopmentToken.java b/Mage/src/main/java/mage/game/permanent/token/ResearchDevelopmentToken.java
new file mode 100644
index 0000000000..29938baeb3
--- /dev/null
+++ b/Mage/src/main/java/mage/game/permanent/token/ResearchDevelopmentToken.java
@@ -0,0 +1,39 @@
+package mage.game.permanent.token;
+
+import mage.MageInt;
+import mage.constants.CardType;
+import mage.constants.SubType;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @author JayDi85
+ */
+public final class ResearchDevelopmentToken extends TokenImpl {
+
+    static final private List<String> tokenImageSets = new ArrayList<>();
+
+    static {
+        tokenImageSets.addAll(Arrays.asList("DIS"));
+    }
+
+    public ResearchDevelopmentToken() {
+        super("Elemental", "3/1 red Elemental creature token");
+        availableImageSetCodes = tokenImageSets;
+        cardType.add(CardType.CREATURE);
+        color.setRed(true);
+        subtype.add(SubType.ELEMENTAL);
+        power = new MageInt(3);
+        toughness = new MageInt(1);
+    }
+
+    public ResearchDevelopmentToken(final ResearchDevelopmentToken token) {
+        super(token);
+    }
+
+    public ResearchDevelopmentToken copy() {
+        return new ResearchDevelopmentToken(this);
+    }
+}
diff --git a/Mage/src/main/java/mage/game/permanent/token/SaprolingToken.java b/Mage/src/main/java/mage/game/permanent/token/SaprolingToken.java
index 828a2545d8..955d409e92 100644
--- a/Mage/src/main/java/mage/game/permanent/token/SaprolingToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/SaprolingToken.java
@@ -32,39 +32,33 @@ public final class SaprolingToken extends TokenImpl {
                 "RTR",
                 "C15",
                 "MM3",
+                "INV",
                 "C16", // 2 different token images...
                 "CMA",
                 "VMA", // 2 different token, one with DIFFERENT stats, "Saproling Burst" create different token, see https://scryfall.com/card/tvma/12
                 "E02",
                 "RIX",
                 "DOM", // 3 different token images
-                "C19"
+                "C19",
+                "C20"
         ));
     }
 
     public SaprolingToken() {
-        this(null, 0);
-    }
-
-    public SaprolingToken(String setCode) {
-        this(setCode, 0);
-    }
-
-    public SaprolingToken(String setCode, int tokenType) {
         super("Saproling", "1/1 green Saproling creature token");
         availableImageSetCodes = tokenImageSets;
-        setOriginalExpansionSetCode(setCode);
+        cardType.add(CardType.CREATURE);
+        color.setGreen(true);
+        subtype.add(SubType.SAPROLING);
+        power = new MageInt(1);
+        toughness = new MageInt(1);
+
         if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("C16")) {
             this.setTokenType(RandomUtil.nextInt(2) + 1);
         }
         if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("DOM")) {
             this.setTokenType(RandomUtil.nextInt(3) + 1);
         }
-        cardType.add(CardType.CREATURE);
-        color.setGreen(true);
-        subtype.add(SubType.SAPROLING);
-        power = new MageInt(1);
-        toughness = new MageInt(1);
     }
 
     public SaprolingToken(final SaprolingToken token) {
diff --git a/Mage/src/main/java/mage/game/permanent/token/SoldierToken.java b/Mage/src/main/java/mage/game/permanent/token/SoldierToken.java
index 6275b24333..d4b1bfa80d 100644
--- a/Mage/src/main/java/mage/game/permanent/token/SoldierToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/SoldierToken.java
@@ -18,7 +18,7 @@ public final class SoldierToken extends TokenImpl {
 
     static {
         tokenImageSets.addAll(Arrays.asList("10E", "M15", "C14", "ORI", "ALA", "DDF", "THS", "M12", "M13", "MM2", "MMA", "RTR",
-                "SOM", "DDO", "M10", "ORI", "EMN", "EMA", "CN2", "C16", "MM3", "E01", "DOM", "MH1", "M20"));
+                "SOM", "DDO", "M10", "ORI", "EMN", "EMA", "CN2", "C16", "MM3", "E01", "DOM", "MH1", "M20", "C20"));
     }
 
     public SoldierToken() {
@@ -30,7 +30,6 @@ public final class SoldierToken extends TokenImpl {
         subtype.add(SubType.SOLDIER);
         power = new MageInt(1);
         toughness = new MageInt(1);
-
     }
 
     @Override
@@ -39,7 +38,7 @@ public final class SoldierToken extends TokenImpl {
         if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("THS")) {
             this.setTokenType(RandomUtil.nextInt(2) + 1);
         }
-        if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("CN2") || getOriginalExpansionSetCode().equals("MM3")) {
+        if (getOriginalExpansionSetCode() != null && (getOriginalExpansionSetCode().equals("CN2") || getOriginalExpansionSetCode().equals("MM3"))) {
             setTokenType(1);
         }
     }
diff --git a/Mage/src/main/java/mage/game/permanent/token/SpiritWhiteToken.java b/Mage/src/main/java/mage/game/permanent/token/SpiritWhiteToken.java
index bfcf62c011..0d1ab1807b 100644
--- a/Mage/src/main/java/mage/game/permanent/token/SpiritWhiteToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/SpiritWhiteToken.java
@@ -18,24 +18,12 @@ public final class SpiritWhiteToken extends TokenImpl {
 
     static {
         tokenImageSets.addAll(Arrays.asList("AVR", "C14", "CNS", "DDC", "DDK", "FRF", "ISD", "KTK", "M15", "MM2", "SHM",
-                "SOI", "EMA", "C16", "MM3", "CMA", "E01", "ANA", "RNA", "M20"));
+                "SOI", "EMA", "C16", "MM3", "CMA", "E01", "ANA", "GPT", "RAV", "EMN", "RNA", "M20", "C20"));
     }
 
     public SpiritWhiteToken() {
-        this(null, 0);
-    }
-
-    public SpiritWhiteToken(String setCode) {
-        this(setCode, 0);
-    }
-
-    public SpiritWhiteToken(String setCode, int tokenType) {
         super("Spirit", "1/1 white Spirit creature token with flying");
         availableImageSetCodes = tokenImageSets;
-        setOriginalExpansionSetCode(setCode);
-        if (tokenType > 0) {
-            setTokenType(tokenType);
-        }
         cardType.add(CardType.CREATURE);
         subtype.add(SubType.SPIRIT);
         color.setWhite(true);
diff --git a/Mage/src/main/java/mage/game/permanent/token/TheLocustGodInsectToken.java b/Mage/src/main/java/mage/game/permanent/token/TheLocustGodInsectToken.java
index 439d6ba897..c22ec75b43 100644
--- a/Mage/src/main/java/mage/game/permanent/token/TheLocustGodInsectToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/TheLocustGodInsectToken.java
@@ -1,21 +1,20 @@
-
-
 package mage.game.permanent.token;
+
+import mage.MageInt;
+import mage.abilities.keyword.FlyingAbility;
+import mage.abilities.keyword.HasteAbility;
 import mage.constants.CardType;
 import mage.constants.SubType;
-import mage.MageInt;
-import mage.abilities.keyword.HasteAbility;
-import mage.abilities.keyword.FlyingAbility;
+
+import java.util.Arrays;
 
 /**
- *
  * @author spjspj
  */
 public final class TheLocustGodInsectToken extends TokenImpl {
 
     public TheLocustGodInsectToken() {
         super("Insect", "1/1 blue and red Insect creature token with flying and haste");
-        setOriginalExpansionSetCode("HOU");
         cardType.add(CardType.CREATURE);
         color.setBlue(true);
         color.setRed(true);
@@ -24,6 +23,8 @@ public final class TheLocustGodInsectToken extends TokenImpl {
         toughness = new MageInt(1);
         addAbility(FlyingAbility.getInstance());
         addAbility(HasteAbility.getInstance());
+
+        availableImageSetCodes = Arrays.asList("HOU", "C20");
     }
 
     public TheLocustGodInsectToken(final TheLocustGodInsectToken token) {
diff --git a/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java b/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java
index 922d5893db..30919e478f 100644
--- a/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java
+++ b/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java
@@ -2,15 +2,12 @@ package mage.game.permanent.token;
 
 import mage.MageObject;
 import mage.MageObjectImpl;
-import mage.MageObjectReference;
 import mage.abilities.Ability;
 import mage.cards.Card;
 import mage.constants.Zone;
 import mage.game.Game;
 import mage.game.events.CreateTokenEvent;
 import mage.game.events.CreatedTokenEvent;
-import mage.game.events.GameEvent;
-import mage.game.events.GameEvent.EventType;
 import mage.game.events.ZoneChangeEvent;
 import mage.game.permanent.Permanent;
 import mage.game.permanent.PermanentToken;
@@ -53,7 +50,8 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
         }
     }
 
-    public TokenImpl() { }
+    public TokenImpl() {
+    }
 
     public TokenImpl(String name, String description) {
         this.name = name;
@@ -165,6 +163,9 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
         if (controller == null) {
             return false;
         }
+        if (amount == 0) {
+            return false;
+        }
         lastAddedTokenIds.clear();
 
         CreateTokenEvent event = new CreateTokenEvent(sourceId, controllerId, amount, this);
@@ -240,6 +241,10 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
         return tokenType;
     }
 
+    /**
+     * Set token index to search in card-pictures-tok.txt (if set have multiple tokens with same name)
+     * Default is 1
+     */
     @Override
     public void setTokenType(int tokenType) {
         this.tokenType = tokenType;
diff --git a/Mage/src/main/java/mage/game/permanent/token/TreasureToken.java b/Mage/src/main/java/mage/game/permanent/token/TreasureToken.java
index dd23d59eda..fee291d404 100644
--- a/Mage/src/main/java/mage/game/permanent/token/TreasureToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/TreasureToken.java
@@ -20,24 +20,11 @@ public final class TreasureToken extends TokenImpl {
 
     static final private List<String> tokenImageSets = new ArrayList<>();
 
-    static {
-        tokenImageSets.addAll(Arrays.asList("XLN", "RNA", "M20", "C19"));
-    }
-
     public TreasureToken() {
-        this(null, 0);
-    }
-
-    public TreasureToken(String setCode) {
-        this(setCode, 0);
-    }
-
-    public TreasureToken(String setCode, int tokenType) {
         super("Treasure", "Treasure token");
-        availableImageSetCodes = tokenImageSets;
-        setOriginalExpansionSetCode(setCode);
         cardType.add(CardType.ARTIFACT);
         subtype.add(SubType.TREASURE);
+        availableImageSetCodes = Arrays.asList("XLN", "RNA", "M20", "C19", "C20");
 
         Ability ability = new SimpleManaAbility(Zone.BATTLEFIELD, new AddManaOfAnyColorEffect(), new TapSourceCost());
         ability.addCost(new SacrificeSourceCost());
diff --git a/Mage/src/main/java/mage/game/permanent/token/WhiteElementalToken.java b/Mage/src/main/java/mage/game/permanent/token/WhiteElementalToken.java
index 52edc3276e..5c84545558 100644
--- a/Mage/src/main/java/mage/game/permanent/token/WhiteElementalToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/WhiteElementalToken.java
@@ -1,13 +1,11 @@
-
-
 package mage.game.permanent.token;
-import mage.constants.CardType;
-import mage.constants.SubType;
+
 import mage.MageInt;
 import mage.abilities.keyword.FlyingAbility;
+import mage.constants.CardType;
+import mage.constants.SubType;
 
 /**
- *
  * @author spjspj
  */
 public final class WhiteElementalToken extends TokenImpl {
@@ -19,8 +17,11 @@ public final class WhiteElementalToken extends TokenImpl {
         subtype.add(SubType.ELEMENTAL);
         power = new MageInt(4);
         toughness = new MageInt(4);
-        setTokenType(2);
         this.addAbility(FlyingAbility.getInstance());
+
+        if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("C20")) {
+            setTokenType(2);
+        }
     }
 
     public WhiteElementalToken(final WhiteElementalToken token) {
diff --git a/Mage/src/main/java/mage/game/permanent/token/ZaxaraTheExemplaryHydraToken.java b/Mage/src/main/java/mage/game/permanent/token/ZaxaraTheExemplaryHydraToken.java
new file mode 100644
index 0000000000..ce9eaee6dd
--- /dev/null
+++ b/Mage/src/main/java/mage/game/permanent/token/ZaxaraTheExemplaryHydraToken.java
@@ -0,0 +1,33 @@
+package mage.game.permanent.token;
+
+import mage.MageInt;
+import mage.constants.CardType;
+import mage.constants.SubType;
+
+import java.util.Arrays;
+
+/**
+ * @author AsterAether
+ */
+public class ZaxaraTheExemplaryHydraToken extends TokenImpl {
+
+    public ZaxaraTheExemplaryHydraToken() {
+        super("Hydra", "0/0 green Hydra creature token");
+        cardType.add(CardType.CREATURE);
+        color.setGreen(true);
+        subtype.add(SubType.HYDRA);
+        power = new MageInt(0);
+        toughness = new MageInt(0);
+
+        availableImageSetCodes = Arrays.asList("C20");
+    }
+
+    private ZaxaraTheExemplaryHydraToken(final ZaxaraTheExemplaryHydraToken token) {
+        super(token);
+    }
+
+    @Override
+    public ZaxaraTheExemplaryHydraToken copy() {
+        return new ZaxaraTheExemplaryHydraToken(this);
+    }
+}
\ No newline at end of file

From f2f1abd0f394c81b5874c9bd55675617a34216e9 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Mon, 15 Jun 2020 20:59:53 +0400
Subject: [PATCH 280/586] * Images: fixed wrong Wurm token image for some sets
 (#5604);

---
 .../sources/ScryfallImageSupportTokens.java   |  3 ++
 .../src/main/resources/card-pictures-tok.txt  | 20 ++++----
 .../src/mage/cards/a/AdventOfTheWurm.java     | 12 ++---
 Mage.Sets/src/mage/cards/a/ArmadaWurm.java    |  9 ++--
 .../src/mage/cards/s/SandwurmConvergence.java |  9 ++--
 .../src/mage/cards/w/WorldspineWurm.java      | 11 ++---
 .../src/mage/cards/w/WurmcoilEngine.java      | 13 +++--
 .../token/GreenAndWhiteElementalToken.java    | 10 +---
 .../mage/game/permanent/token/Wurm2Token.java | 39 ---------------
 .../game/permanent/token/Wurm55Token.java     | 41 ++++++++++++++++
 .../mage/game/permanent/token/WurmToken.java  | 14 +++++-
 .../mage/game/permanent/token/WurmToken3.java | 40 ----------------
 ...oken.java => WurmWithDeathtouchToken.java} | 28 ++++++-----
 .../token/WurmWithLifelinkToken.java          | 47 +++++++++++++++++++
 ...mToken2.java => WurmWithTrampleToken.java} | 30 +++++-------
 15 files changed, 166 insertions(+), 160 deletions(-)
 delete mode 100644 Mage/src/main/java/mage/game/permanent/token/Wurm2Token.java
 create mode 100644 Mage/src/main/java/mage/game/permanent/token/Wurm55Token.java
 delete mode 100644 Mage/src/main/java/mage/game/permanent/token/WurmToken3.java
 rename Mage/src/main/java/mage/game/permanent/token/{Wurm1Token.java => WurmWithDeathtouchToken.java} (54%)
 create mode 100644 Mage/src/main/java/mage/game/permanent/token/WurmWithLifelinkToken.java
 rename Mage/src/main/java/mage/game/permanent/token/{WurmToken2.java => WurmWithTrampleToken.java} (58%)

diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java
index 3046eb508a..15490b5f0e 100644
--- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java
@@ -186,6 +186,9 @@ public class ScryfallImageSupportTokens {
             put("BFZ/Octopus", "https://api.scryfall.com/cards/tbfz/7/en?format=image");
             put("BFZ/Plant", "https://api.scryfall.com/cards/tbfz/10/en?format=image");
 
+            // C17
+            
+
             // WAR
             put("WAR/Angel", "https://api.scryfall.com/cards/twar/2/en?format=image");
             put("WAR/Assassin", "https://api.scryfall.com/cards/twar/6/en?format=image");
diff --git a/Mage.Client/src/main/resources/card-pictures-tok.txt b/Mage.Client/src/main/resources/card-pictures-tok.txt
index e5a153c878..1e86a67b46 100644
--- a/Mage.Client/src/main/resources/card-pictures-tok.txt
+++ b/Mage.Client/src/main/resources/card-pictures-tok.txt
@@ -174,7 +174,7 @@
 |Generate|TOK:AKH|Hippo|||HippoToken2|
 |Generate|TOK:AKH|Snake|||DeathtouchSnakeToken|
 |Generate|TOK:AKH|Warrior|||WarriorVigilantToken|
-|Generate|TOK:AKH|Wurm|||WurmToken3|
+|Generate|TOK:AKH|Wurm|||Wurm55Token|
 |Generate|TOK:AKH|Zombie|||ZombieToken
 #TOK:AKH - some tokens from real cards (see Embalm ability)
 #|Generate|TOK:AKH|Angel of Sanctions||
@@ -319,8 +319,8 @@
 |Generate|TOK:C14|Treefolk|||SylvanOfferingTreefolkToken|
 |Generate|TOK:C14|Whale|||ReefWormWhaleToken|
 |Generate|TOK:C14|Wolf|||WolfToken|
-|Generate|TOK:C14|Wurm|1||Wurm1Token|
-|Generate|TOK:C14|Wurm|2||Wurm2Token|
+|Generate|TOK:C14|Wurm|1||WurmWithDeathtouchToken|
+|Generate|TOK:C14|Wurm|2||WurmWithLifelinkToken|
 |Generate|TOK:C14|Zombie|1||ZombieToken|
 |Generate|TOK:C14|Zombie|2||StitcherGeralfZombieToken|
 |Generate|TOK:C15|Angel|||AngelToken|
@@ -567,8 +567,8 @@
 |Generate|TOK:DGM|Rhino|||RhinoToken|
 |Generate|TOK:DGM|Soldier|||SoldierTokenWithHaste|
 |Generate|TOK:DGM|Spirit|||TeysaEnvoyOfGhostsToken|
-|Generate|TOK:DGM|Wurm|||WurmToken2|
-|Generate|TOK:DGM|Wurm|||WurmToken3|
+|Generate|TOK:DGM|Wurm|1||WurmWithTrampleToken|
+|Generate|TOK:DGM|Wurm|2||Wurm55Token|
 |Generate|TOK:DIS|Bird|||DovescapeToken|
 |Generate|TOK:DIS|Drake|||LeafdrakeRoostDrakeToken|
 |Generate|TOK:DIS|Elemental|||ResearchDevelopmentToken|
@@ -1070,7 +1070,7 @@
 |Generate|TOK:RTR|Rhino|||RhinoToken|
 |Generate|TOK:RTR|Saproling|||SaprolingToken|
 |Generate|TOK:RTR|Soldier|||SoldierToken|
-|Generate|TOK:RTR|Wurm|||WurmToken2|
+|Generate|TOK:RTR|Wurm|||WurmWithTrampleToken|
 |Generate|TOK:SCG|Angel|||AngelToken|
 |Generate|TOK:SCG|Beast|||BeastToken2|
 |Generate|TOK:SCG|Dragon|||DragonToken2|
@@ -1118,8 +1118,8 @@
 |Generate|TOK:SOM|Myr|||MyrToken|
 |Generate|TOK:SOM|Soldier|||SoldierToken|
 |Generate|TOK:SOM|Wolf|||WolfToken|
-|Generate|TOK:SOM|Wurm|1||Wurm1Token|
-|Generate|TOK:SOM|Wurm|2||Wurm2Token|
+|Generate|TOK:SOM|Wurm|1||WurmWithDeathtouchToken|
+|Generate|TOK:SOM|Wurm|2||WurmWithLifelinkToken|
 |Generate|TOK:STH|Goblin|||GoblinToken|
 |Generate|TOK:STH|Insect|||HornetToken|
 |Generate|TOK:STH|Insect|||WaspToken|
@@ -1214,8 +1214,8 @@
 |Generate|TOK:VMA|Spirit|||SpiritWhiteToken|
 |Generate|TOK:VMA|Squirrel|||SquirrelToken|
 |Generate|TOK:VMA|Thopter|||ThopterColorlessToken|
-|Generate|TOK:VMA|Wurm|||PenumbraWurmToken|
-|Generate|TOK:VMA|Wurm|||WurmToken|
+|Generate|TOK:VMA|Wurm|1||PenumbraWurmToken|
+|Generate|TOK:VMA|Wurm|2||WurmToken|
 |Generate|TOK:VMA|Zombie|||ZombieToken|
 |Generate|TOK:WTH|Squirrel|||SquirrelToken|
 |Generate|TOK:WWK|Construct|||StoneTrapIdolToken|
diff --git a/Mage.Sets/src/mage/cards/a/AdventOfTheWurm.java b/Mage.Sets/src/mage/cards/a/AdventOfTheWurm.java
index e00ee2edd2..88295ecad2 100644
--- a/Mage.Sets/src/mage/cards/a/AdventOfTheWurm.java
+++ b/Mage.Sets/src/mage/cards/a/AdventOfTheWurm.java
@@ -1,16 +1,14 @@
-
-
 package mage.cards.a;
 
-import java.util.UUID;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.game.permanent.token.WurmToken2;
+import mage.game.permanent.token.WurmWithTrampleToken;
+
+import java.util.UUID;
 
 /**
- *
  * @author LevelX2
  */
 
@@ -18,10 +16,10 @@ import mage.game.permanent.token.WurmToken2;
 public final class AdventOfTheWurm extends CardImpl {
 
     public AdventOfTheWurm(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{G}{G}{W}");
+        super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}{G}{W}");
 
         // Create a 5/5 green Wurm creature token with trample.
-        this.getSpellAbility().addEffect(new CreateTokenEffect(new WurmToken2()));
+        this.getSpellAbility().addEffect(new CreateTokenEffect(new WurmWithTrampleToken()));
     }
 
     public AdventOfTheWurm(final AdventOfTheWurm card) {
diff --git a/Mage.Sets/src/mage/cards/a/ArmadaWurm.java b/Mage.Sets/src/mage/cards/a/ArmadaWurm.java
index c6fbc3025b..64455089fc 100644
--- a/Mage.Sets/src/mage/cards/a/ArmadaWurm.java
+++ b/Mage.Sets/src/mage/cards/a/ArmadaWurm.java
@@ -1,7 +1,5 @@
-
 package mage.cards.a;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
@@ -10,10 +8,11 @@ import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.SubType;
-import mage.game.permanent.token.WurmToken2;
+import mage.game.permanent.token.WurmWithTrampleToken;
+
+import java.util.UUID;
 
 /**
- *
  * @author LevelX2
  */
 public final class ArmadaWurm extends CardImpl {
@@ -29,7 +28,7 @@ public final class ArmadaWurm extends CardImpl {
         this.addAbility(TrampleAbility.getInstance());
 
         // When Armada Wurm enters the battlefield, create a 5/5 green Wurm creature token with trample.
-        this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new WurmToken2()), false));
+        this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new WurmWithTrampleToken()), false));
 
     }
 
diff --git a/Mage.Sets/src/mage/cards/s/SandwurmConvergence.java b/Mage.Sets/src/mage/cards/s/SandwurmConvergence.java
index 63d0b0374a..ba04fcf1ca 100644
--- a/Mage.Sets/src/mage/cards/s/SandwurmConvergence.java
+++ b/Mage.Sets/src/mage/cards/s/SandwurmConvergence.java
@@ -1,7 +1,5 @@
-
 package mage.cards.s;
 
-import java.util.UUID;
 import mage.abilities.common.BeginningOfEndStepTriggeredAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.Effect;
@@ -16,10 +14,11 @@ import mage.constants.TargetController;
 import mage.constants.Zone;
 import mage.filter.common.FilterCreaturePermanent;
 import mage.filter.predicate.mageobject.AbilityPredicate;
-import mage.game.permanent.token.WurmToken3;
+import mage.game.permanent.token.Wurm55Token;
+
+import java.util.UUID;
 
 /**
- *
  * @author fireshoes
  */
 public final class SandwurmConvergence extends CardImpl {
@@ -39,7 +38,7 @@ public final class SandwurmConvergence extends CardImpl {
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect));
 
         // At the beginning of your end step, create a 5/5 green Wurm creature token.
-        this.addAbility(new BeginningOfEndStepTriggeredAbility(new CreateTokenEffect(new WurmToken3()), TargetController.YOU, false));
+        this.addAbility(new BeginningOfEndStepTriggeredAbility(new CreateTokenEffect(new Wurm55Token()), TargetController.YOU, false));
     }
 
     public SandwurmConvergence(final SandwurmConvergence card) {
diff --git a/Mage.Sets/src/mage/cards/w/WorldspineWurm.java b/Mage.Sets/src/mage/cards/w/WorldspineWurm.java
index 024220338e..db6cafe74a 100644
--- a/Mage.Sets/src/mage/cards/w/WorldspineWurm.java
+++ b/Mage.Sets/src/mage/cards/w/WorldspineWurm.java
@@ -1,7 +1,5 @@
-
 package mage.cards.w;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.common.DiesTriggeredAbility;
 import mage.abilities.common.PutIntoGraveFromAnywhereSourceTriggeredAbility;
@@ -12,16 +10,17 @@ import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.SubType;
-import mage.game.permanent.token.WurmToken2;
+import mage.game.permanent.token.WurmWithTrampleToken;
+
+import java.util.UUID;
 
 /**
- *
  * @author Plopman
  */
 public final class WorldspineWurm extends CardImpl {
 
     public WorldspineWurm(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{8}{G}{G}{G}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{8}{G}{G}{G}");
         this.subtype.add(SubType.WURM);
 
         this.power = new MageInt(15);
@@ -31,7 +30,7 @@ public final class WorldspineWurm extends CardImpl {
         this.addAbility(TrampleAbility.getInstance());
 
         // When Worldspine Wurm dies, create three 5/5 green Wurm creature tokens with trample.
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new WurmToken2(), 3)));
+        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new WurmWithTrampleToken(), 3)));
 
         // When Worldspine Wurm is put into a graveyard from anywhere, shuffle it into its owner's library.
         this.addAbility(new PutIntoGraveFromAnywhereSourceTriggeredAbility(new ShuffleIntoLibrarySourceEffect()));
diff --git a/Mage.Sets/src/mage/cards/w/WurmcoilEngine.java b/Mage.Sets/src/mage/cards/w/WurmcoilEngine.java
index 9526069d68..76e82878d3 100644
--- a/Mage.Sets/src/mage/cards/w/WurmcoilEngine.java
+++ b/Mage.Sets/src/mage/cards/w/WurmcoilEngine.java
@@ -1,7 +1,5 @@
-
 package mage.cards.w;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.DiesTriggeredAbility;
@@ -12,11 +10,12 @@ import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.SubType;
-import mage.game.permanent.token.Wurm1Token;
-import mage.game.permanent.token.Wurm2Token;
+import mage.game.permanent.token.WurmWithDeathtouchToken;
+import mage.game.permanent.token.WurmWithLifelinkToken;
+
+import java.util.UUID;
 
 /**
- *
  * @author Loki
  */
 public final class WurmcoilEngine extends CardImpl {
@@ -32,8 +31,8 @@ public final class WurmcoilEngine extends CardImpl {
         this.addAbility(LifelinkAbility.getInstance());
 
         // When Wurmcoil Engine dies, create a 3/3 colorless Wurm artifact creature token with deathtouch and a 3/3 colorless Wurm artifact creature token with lifelink.
-        Ability ability = new DiesTriggeredAbility(new CreateTokenEffect(new Wurm1Token(expansionSetCode)), false);
-        ability.addEffect(new CreateTokenEffect(new Wurm2Token(expansionSetCode)));
+        Ability ability = new DiesTriggeredAbility(new CreateTokenEffect(new WurmWithDeathtouchToken()), false);
+        ability.addEffect(new CreateTokenEffect(new WurmWithLifelinkToken()));
         this.addAbility(ability);
     }
 
diff --git a/Mage/src/main/java/mage/game/permanent/token/GreenAndWhiteElementalToken.java b/Mage/src/main/java/mage/game/permanent/token/GreenAndWhiteElementalToken.java
index ca444ebbb5..25d37498d9 100644
--- a/Mage/src/main/java/mage/game/permanent/token/GreenAndWhiteElementalToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/GreenAndWhiteElementalToken.java
@@ -5,21 +5,13 @@ import mage.abilities.keyword.VigilanceAbility;
 import mage.constants.CardType;
 import mage.constants.SubType;
 
-import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.List;
 
 /**
  * @author JayDi85
  */
 public final class GreenAndWhiteElementalToken extends TokenImpl {
 
-    static final private List<String> tokenImageSets = new ArrayList<>();
-
-    static {
-        tokenImageSets.addAll(Arrays.asList("GK1", "PTC", "RTR"));
-    }
-
     public GreenAndWhiteElementalToken() {
         super("Elemental", "8/8 green and white Elemental creature token with vigilance");
         cardType.add(CardType.CREATURE);
@@ -29,6 +21,8 @@ public final class GreenAndWhiteElementalToken extends TokenImpl {
         power = new MageInt(8);
         toughness = new MageInt(8);
         this.addAbility(VigilanceAbility.getInstance());
+
+        availableImageSetCodes = Arrays.asList("GK1", "PTC", "RTR");
     }
 
     public GreenAndWhiteElementalToken(final GreenAndWhiteElementalToken token) {
diff --git a/Mage/src/main/java/mage/game/permanent/token/Wurm2Token.java b/Mage/src/main/java/mage/game/permanent/token/Wurm2Token.java
deleted file mode 100644
index 7b313746d8..0000000000
--- a/Mage/src/main/java/mage/game/permanent/token/Wurm2Token.java
+++ /dev/null
@@ -1,39 +0,0 @@
-
-package mage.game.permanent.token;
-
-import mage.constants.CardType;
-import mage.constants.SubType;
-import mage.MageInt;
-import mage.abilities.keyword.LifelinkAbility;
-
-/**
- *
- * @author spjspj
- */
-public final class Wurm2Token extends TokenImpl {
-
-    public Wurm2Token() {
-        this("MBS");
-    }
-
-    public Wurm2Token(String setCode) {
-        super("Wurm", "3/3 colorless Wurm artifact creature token with lifelink");
-        setOriginalExpansionSetCode(setCode);
-        cardType.add(CardType.ARTIFACT);
-        cardType.add(CardType.CREATURE);
-        subtype.add(SubType.WURM);
-        power = new MageInt(3);
-        toughness = new MageInt(3);
-        this.addAbility(LifelinkAbility.getInstance());
-
-        setTokenType(2); // for image
-    }
-
-    public Wurm2Token(final Wurm2Token token) {
-        super(token);
-    }
-
-    public Wurm2Token copy() {
-        return new Wurm2Token(this);
-    }
-}
diff --git a/Mage/src/main/java/mage/game/permanent/token/Wurm55Token.java b/Mage/src/main/java/mage/game/permanent/token/Wurm55Token.java
new file mode 100644
index 0000000000..b327c02263
--- /dev/null
+++ b/Mage/src/main/java/mage/game/permanent/token/Wurm55Token.java
@@ -0,0 +1,41 @@
+package mage.game.permanent.token;
+
+import mage.MageInt;
+import mage.constants.CardType;
+import mage.constants.SubType;
+
+import java.util.Arrays;
+
+/**
+ * @author fireshoes
+ */
+public final class Wurm55Token extends TokenImpl {
+
+    public Wurm55Token() {
+        super("Wurm", "5/5 green Wurm creature token");
+        cardType.add(CardType.CREATURE);
+        color.setGreen(true);
+        subtype.add(SubType.WURM);
+        power = new MageInt(5);
+        toughness = new MageInt(5);
+
+        availableImageSetCodes = Arrays.asList("AKH", "DGM");
+    }
+
+    @Override
+    public void setExpansionSetCodeForImage(String code) {
+        super.setExpansionSetCodeForImage(code);
+
+        if (getOriginalExpansionSetCode().equals("DGM")) {
+            this.setTokenType(2);
+        }
+    }
+
+    public Wurm55Token(final Wurm55Token token) {
+        super(token);
+    }
+
+    public Wurm55Token copy() {
+        return new Wurm55Token(this);
+    }
+}
diff --git a/Mage/src/main/java/mage/game/permanent/token/WurmToken.java b/Mage/src/main/java/mage/game/permanent/token/WurmToken.java
index f6af923e0e..e05fe87b6e 100644
--- a/Mage/src/main/java/mage/game/permanent/token/WurmToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/WurmToken.java
@@ -1,10 +1,11 @@
-
 package mage.game.permanent.token;
 
 import mage.MageInt;
 import mage.constants.CardType;
 import mage.constants.SubType;
 
+import java.util.Arrays;
+
 /**
  * @author magenoxx_at_gmail.com
  */
@@ -17,6 +18,17 @@ public final class WurmToken extends TokenImpl {
         subtype.add(SubType.WURM);
         power = new MageInt(6);
         toughness = new MageInt(6);
+
+        availableImageSetCodes = Arrays.asList("C19", "EMA", "GPT", "JUD", "M12", "M13", "MM3", "ODY", "VMA");
+    }
+
+    @Override
+    public void setExpansionSetCodeForImage(String code) {
+        super.setExpansionSetCodeForImage(code);
+
+        if (getOriginalExpansionSetCode().equals("VMA")) {
+            this.setTokenType(2);
+        }
     }
 
     public WurmToken(final WurmToken token) {
diff --git a/Mage/src/main/java/mage/game/permanent/token/WurmToken3.java b/Mage/src/main/java/mage/game/permanent/token/WurmToken3.java
deleted file mode 100644
index 31425bb2bd..0000000000
--- a/Mage/src/main/java/mage/game/permanent/token/WurmToken3.java
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-package mage.game.permanent.token;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import mage.constants.CardType;
-import mage.constants.SubType;
-import mage.MageInt;
-
-/**
- *
- * @author fireshoes
- */
-public final class WurmToken3 extends TokenImpl {
-
-    static final private List<String> tokenImageSets = new ArrayList<>();
-
-    static {
-        tokenImageSets.addAll(Arrays.asList("AKH"));
-    }
-
-    public WurmToken3() {
-        super("Wurm", "5/5 green Wurm creature token");
-        cardType.add(CardType.CREATURE);
-        color.setGreen(true);
-        subtype.add(SubType.WURM);
-        power = new MageInt(5);
-        toughness = new MageInt(5);
-    }
-
-    public WurmToken3(final WurmToken3 token) {
-        super(token);
-    }
-
-    public WurmToken3 copy() {
-        return new WurmToken3(this);
-    }
-}
diff --git a/Mage/src/main/java/mage/game/permanent/token/Wurm1Token.java b/Mage/src/main/java/mage/game/permanent/token/WurmWithDeathtouchToken.java
similarity index 54%
rename from Mage/src/main/java/mage/game/permanent/token/Wurm1Token.java
rename to Mage/src/main/java/mage/game/permanent/token/WurmWithDeathtouchToken.java
index 85d0116826..4f55a1bcc6 100644
--- a/Mage/src/main/java/mage/game/permanent/token/Wurm1Token.java
+++ b/Mage/src/main/java/mage/game/permanent/token/WurmWithDeathtouchToken.java
@@ -1,22 +1,19 @@
 package mage.game.permanent.token;
 
-import mage.constants.CardType;
-import mage.constants.SubType;
 import mage.MageInt;
 import mage.abilities.keyword.DeathtouchAbility;
+import mage.constants.CardType;
+import mage.constants.SubType;
+
+import java.util.Arrays;
 
 /**
- *
  * @author spjspj
  */
-public final class Wurm1Token extends TokenImpl {
+public final class WurmWithDeathtouchToken extends TokenImpl {
 
-    public Wurm1Token() {
-        this("MBS");
-    }
-    public Wurm1Token(String setCode) {
+    public WurmWithDeathtouchToken() {
         super("Wurm", "3/3 colorless Wurm artifact creature token with deathtouch");
-        setOriginalExpansionSetCode(setCode);
         cardType.add(CardType.ARTIFACT);
         cardType.add(CardType.CREATURE);
         subtype.add(SubType.WURM);
@@ -24,14 +21,19 @@ public final class Wurm1Token extends TokenImpl {
         toughness = new MageInt(3);
         this.addAbility(DeathtouchAbility.getInstance());
 
-        setTokenType(1); // for image
+        availableImageSetCodes = Arrays.asList("C14", "SOM");
     }
 
-    public Wurm1Token(final Wurm1Token token) {
+    @Override
+    public void setExpansionSetCodeForImage(String code) {
+        super.setExpansionSetCodeForImage(code);
+    }
+
+    public WurmWithDeathtouchToken(final WurmWithDeathtouchToken token) {
         super(token);
     }
 
-    public Wurm1Token copy() {
-        return new Wurm1Token(this);
+    public WurmWithDeathtouchToken copy() {
+        return new WurmWithDeathtouchToken(this);
     }
 }
diff --git a/Mage/src/main/java/mage/game/permanent/token/WurmWithLifelinkToken.java b/Mage/src/main/java/mage/game/permanent/token/WurmWithLifelinkToken.java
new file mode 100644
index 0000000000..8ecc9cad32
--- /dev/null
+++ b/Mage/src/main/java/mage/game/permanent/token/WurmWithLifelinkToken.java
@@ -0,0 +1,47 @@
+package mage.game.permanent.token;
+
+import mage.MageInt;
+import mage.abilities.keyword.LifelinkAbility;
+import mage.constants.CardType;
+import mage.constants.SubType;
+
+import java.util.Arrays;
+
+/**
+ * @author spjspj
+ */
+public final class WurmWithLifelinkToken extends TokenImpl {
+
+    public WurmWithLifelinkToken() {
+        super("Wurm", "3/3 colorless Wurm artifact creature token with lifelink");
+        cardType.add(CardType.ARTIFACT);
+        cardType.add(CardType.CREATURE);
+        subtype.add(SubType.WURM);
+        power = new MageInt(3);
+        toughness = new MageInt(3);
+        this.addAbility(LifelinkAbility.getInstance());
+
+        availableImageSetCodes = Arrays.asList("C14", "SOM");
+    }
+
+    @Override
+    public void setExpansionSetCodeForImage(String code) {
+        super.setExpansionSetCodeForImage(code);
+
+        if (getOriginalExpansionSetCode().equals("C14")) {
+            this.setTokenType(2);
+        }
+
+        if (getOriginalExpansionSetCode().equals("SOM")) {
+            this.setTokenType(2);
+        }
+    }
+
+    public WurmWithLifelinkToken(final WurmWithLifelinkToken token) {
+        super(token);
+    }
+
+    public WurmWithLifelinkToken copy() {
+        return new WurmWithLifelinkToken(this);
+    }
+}
diff --git a/Mage/src/main/java/mage/game/permanent/token/WurmToken2.java b/Mage/src/main/java/mage/game/permanent/token/WurmWithTrampleToken.java
similarity index 58%
rename from Mage/src/main/java/mage/game/permanent/token/WurmToken2.java
rename to Mage/src/main/java/mage/game/permanent/token/WurmWithTrampleToken.java
index 598e85a5a4..dad1649b03 100644
--- a/Mage/src/main/java/mage/game/permanent/token/WurmToken2.java
+++ b/Mage/src/main/java/mage/game/permanent/token/WurmWithTrampleToken.java
@@ -1,28 +1,18 @@
-
-
 package mage.game.permanent.token;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import mage.constants.CardType;
-import mage.constants.SubType;
 import mage.MageInt;
 import mage.abilities.keyword.TrampleAbility;
+import mage.constants.CardType;
+import mage.constants.SubType;
+
+import java.util.Arrays;
 
 /**
- *
  * @author LoneFox
  */
-public final class WurmToken2 extends TokenImpl {
+public final class WurmWithTrampleToken extends TokenImpl {
 
-    static final private List<String> tokenImageSets = new ArrayList<>();
-
-    static {
-        tokenImageSets.addAll(Arrays.asList("RTR", "MM3"));
-    }
-
-    public WurmToken2() {
+    public WurmWithTrampleToken() {
         super("Wurm", "5/5 green Wurm creature token with trample");
         cardType.add(CardType.CREATURE);
         color.setGreen(true);
@@ -30,13 +20,15 @@ public final class WurmToken2 extends TokenImpl {
         power = new MageInt(5);
         toughness = new MageInt(5);
         addAbility(TrampleAbility.getInstance());
+
+        availableImageSetCodes = Arrays.asList("DGM", "RTR");
     }
 
-    public WurmToken2(final WurmToken2 token) {
+    public WurmWithTrampleToken(final WurmWithTrampleToken token) {
         super(token);
     }
 
-    public WurmToken2 copy() {
-        return new WurmToken2(this);
+    public WurmWithTrampleToken copy() {
+        return new WurmWithTrampleToken(this);
     }
 }

From 4006e9e9096e3399f6d2eb51bbc70b61ad2ee664 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Mon, 15 Jun 2020 21:30:23 +0400
Subject: [PATCH 281/586] * Images: fixed that some tokens uses wrong images of
 Elemental (#5834);

---
 .../src/main/resources/card-pictures-tok.txt  | 32 +++++++-------
 .../mage/cards/c/ChandraAcolyteOfFlame.java   |  5 +--
 .../src/mage/cards/e/ElementalAppeal.java     | 13 +++---
 .../src/mage/cards/m/MaskOfImmolation.java    |  4 +-
 .../src/mage/cards/s/ScamperingScorcher.java  |  4 +-
 .../src/mage/cards/s/SeasonedPyromancer.java  |  6 +--
 .../src/mage/cards/y/YoungPyromancer.java     |  4 +-
 .../mage/cards/z/ZektarShrineExpedition.java  |  9 ++--
 .../token/AkoumStonewakerElementalToken.java  |  5 +++
 .../CallTheSkyBreakerElementalToken.java      | 14 +++---
 ...entalToken.java => RedElementalToken.java} | 43 +++++++++++--------
 ...a => RedElementalWithTrampleAndHaste.java} | 22 +++++++---
 .../permanent/token/SeedGuardianToken.java    |  5 ++-
 .../TilonallisSummonerElementalToken.java     |  6 +--
 .../token/WalkerOfTheGroveToken.java          | 19 ++++++--
 .../token/WandOfTheElementsFirstToken.java    |  5 +--
 .../token/WandOfTheElementsSecondToken.java   |  5 +--
 .../permanent/token/WhiteElementalToken.java  | 13 ++++++
 .../token/ZektarShrineElementalToken.java     | 34 ---------------
 19 files changed, 129 insertions(+), 119 deletions(-)
 rename Mage/src/main/java/mage/game/permanent/token/{YoungPyromancerElementalToken.java => RedElementalToken.java} (60%)
 rename Mage/src/main/java/mage/game/permanent/token/{ElementalAppealElementalToken.java => RedElementalWithTrampleAndHaste.java} (50%)
 delete mode 100644 Mage/src/main/java/mage/game/permanent/token/ZektarShrineElementalToken.java

diff --git a/Mage.Client/src/main/resources/card-pictures-tok.txt b/Mage.Client/src/main/resources/card-pictures-tok.txt
index 1e86a67b46..2ba1244683 100644
--- a/Mage.Client/src/main/resources/card-pictures-tok.txt
+++ b/Mage.Client/src/main/resources/card-pictures-tok.txt
@@ -270,9 +270,9 @@
 |Generate|TOK:C13|Beast|||OneDozenEyesBeastToken|
 |Generate|TOK:C13|Beast|||SpawningGroundsBeastToken|
 |Generate|TOK:C13|Drake|||LeafdrakeRoostDrakeToken|
-|Generate|TOK:C13|Elemental|||SeedGuardianToken|
-|Generate|TOK:C13|Elemental|||WalkerOfTheGroveToken|
-|Generate|TOK:C13|Elemental|||YoungPyromancerElementalToken|
+|Generate|TOK:C13|Elemental|1||SeedGuardianToken|
+|Generate|TOK:C13|Elemental|2||WalkerOfTheGroveToken|
+|Generate|TOK:C13|Elemental|3||RedElementalToken|
 |Generate|TOK:C13|Elephant|||ElephantToken|
 |Generate|TOK:C13|Elf Warrior|||ElfToken|
 |Generate|TOK:C13|Goat|||GoatToken|
@@ -351,7 +351,7 @@
 |Generate|TOK:C16|Beast||
 |Generate|TOK:C16|Bird|1|
 |Generate|TOK:C16|Bird|2|
-|Generate|TOK:C16|Elemental||
+|Generate|TOK:C16|Elemental|||WhiteElementalToken|
 |Generate|TOK:C16|Elf Warrior||
 |Generate|TOK:C16|Germ||
 |Generate|TOK:C16|Goat||
@@ -619,7 +619,7 @@
 |Generate|TOK:EMA|Beast|||CarnivoreToken|
 |Generate|TOK:EMA|Carnivore||
 |Generate|TOK:EMA|Dragon|||DragonEggDragonToken|
-|Generate|TOK:EMA|Elemental|1||YoungPyromancerElementalToken|
+|Generate|TOK:EMA|Elemental|1||RedElementalToken|
 |Generate|TOK:EMA|Elemental|2||CallTheSkyBreakerElementalToken|
 |Generate|TOK:EMA|Elephant|||ElephantToken|
 |Generate|TOK:EMA|Elf Warrior|||ElfToken|
@@ -655,7 +655,7 @@
 |Generate|TOK:EVE|Spirit|||BeckonApparitionToken|
 |Generate|TOK:EVE|Wolf|||WolfToken|
 |Generate|TOK:EVE|Worm|||WormHarvestToken|
-|Generate|TOK:EVG|Elemental||
+|Generate|TOK:EVG|Elemental|||VoiceOfTheWoodsElementalToken|
 |Generate|TOK:EVG|Elf Warrior|||ElfToken|
 |Generate|TOK:EVG|Goblin|||GoblinToken|
 |Generate|TOK:EXO|Pegasus|||PegasusToken|
@@ -791,8 +791,8 @@
 |Generate|TOK:LRW|Avatar|||AvatarToken|
 |Generate|TOK:LRW|Beast|||BeastToken|
 |Generate|TOK:LRW|Elemental Shaman|||ElementalShamanToken|
-|Generate|TOK:LRW|Elemental|||WalkerOfTheGroveToken|
-|Generate|TOK:LRW|Elemental|||WhiteElementalToken|
+|Generate|TOK:LRW|Elemental|1||WalkerOfTheGroveToken|
+|Generate|TOK:LRW|Elemental|2||WhiteElementalToken|
 |Generate|TOK:LRW|Elf Warrior|||ElfToken|
 |Generate|TOK:LRW|Goblin Rogue|||GoblinRogueToken|
 |Generate|TOK:LRW|Kithkin Soldier|||KithkinToken|
@@ -834,8 +834,8 @@
 |Generate|TOK:M14|Beast|||BeastToken|
 |Generate|TOK:M14|Cat|||CatToken|
 |Generate|TOK:M14|Dragon|||DragonEggDragonToken|
-|Generate|TOK:M14|Elemental|1||YoungPyromancerElementalToken|
-|Generate|TOK:M14|Elemental|2||YoungPyromancerElementalToken|
+|Generate|TOK:M14|Elemental|1||RedElementalToken|
+|Generate|TOK:M14|Elemental|2||RedElementalToken|
 |Generate|TOK:M14|Goat|||GoatToken|
 |Generate|TOK:M14|Saproling|||SaprolingToken|
 |Generate|TOK:M14|Sliver||
@@ -909,7 +909,7 @@
 |Generate|TOK:MM3|Bird||
 |Generate|TOK:MM3|Centaur||
 |Generate|TOK:MM3|Dragon||
-|Generate|TOK:MM3|Elemental||
+|Generate|TOK:MM3|Elemental|||VoiceOfResurgenceToken|
 |Generate|TOK:MM3|Elephant||
 |Generate|TOK:MM3|Giant Warrior||
 |Generate|TOK:MM3|Goblin Warrior||
@@ -1077,7 +1077,7 @@
 |Generate|TOK:SCG|Goblin|||GoblinToken|
 |Generate|TOK:SCG|Soldier|||SoldierToken|
 |Generate|TOK:SHM|Elemental|1||DinOfTheFireherdToken|
-|Generate|TOK:SHM|Elemental|2||YoungPyromancerElementalToken|
+|Generate|TOK:SHM|Elemental|2||RedElementalToken|
 |Generate|TOK:SHM|Elf Warrior|1|
 |Generate|TOK:SHM|Elf Warrior|2|
 |Generate|TOK:SHM|Elf Warrior|||ElfToken|
@@ -1239,8 +1239,8 @@
 |Generate|TOK:ZEN|Angel|||AngelToken|
 |Generate|TOK:ZEN|Beast|||BeastToken2|
 |Generate|TOK:ZEN|Bird|||BirdToken|
-|Generate|TOK:ZEN|Elemental||
-|Generate|TOK:ZEN|Elemental|||ZektarShrineElementalToken|
+|Generate|TOK:ZEN|Elemental|1||RedElementalWithTrampleAndHaste|
+|Generate|TOK:ZEN|Elemental|2||RedElementalWithTrampleAndHaste|
 |Generate|TOK:ZEN|Illusion|||IllusionToken|
 |Generate|TOK:ZEN|Kor Soldier|||KorSoldierToken|
 |Generate|TOK:ZEN|Merfolk||
@@ -1290,7 +1290,7 @@
 |Generate|TOK:MH1|Bear|||BearToken|
 |Generate|TOK:MH1|Bird|||BirdToken|
 |Generate|TOK:MH1|Construct|||KarnConstructToken|
-|Generate|TOK:MH1|Elemental|1||YoungPyromancerElementalToken|
+|Generate|TOK:MH1|Elemental|1||RedElementalToken|
 |Generate|TOK:MH1|Elemental|2||AkoumStonewakerElementalToken|
 |Generate|TOK:MH1|Elephant|||ElephantToken|
 |Generate|TOK:MH1|Goblin|||GoblinToken|
@@ -1320,7 +1320,7 @@
 |Generate|TOK:M19|Zombie|||ZombieToken|
 |Generate|TOK:M20|Ajani's Pridemate|||AjanisPridemateToken|
 |Generate|TOK:M20|Demon|||DemonToken|
-|Generate|TOK:M20|Elemental|||YoungPyromancerElementalToken|
+|Generate|TOK:M20|Elemental|||RedElementalToken|
 |Generate|TOK:M20|Elemental Bird|||MuYanlingSkyDancerToken|
 |Generate|TOK:M20|Golem|||GolemToken|
 |Generate|TOK:M20|Soldier|||SoldierToken|
diff --git a/Mage.Sets/src/mage/cards/c/ChandraAcolyteOfFlame.java b/Mage.Sets/src/mage/cards/c/ChandraAcolyteOfFlame.java
index 635252fbf3..131bc23a7e 100644
--- a/Mage.Sets/src/mage/cards/c/ChandraAcolyteOfFlame.java
+++ b/Mage.Sets/src/mage/cards/c/ChandraAcolyteOfFlame.java
@@ -19,7 +19,6 @@ import mage.counters.CounterType;
 import mage.filter.FilterCard;
 import mage.filter.FilterPermanent;
 import mage.filter.common.FilterControlledPlaneswalkerPermanent;
-import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent;
 import mage.filter.common.FilterInstantOrSorceryCard;
 import mage.filter.predicate.mageobject.ColorPredicate;
 import mage.filter.predicate.mageobject.ConvertedManaCostPredicate;
@@ -27,8 +26,8 @@ import mage.game.Game;
 import mage.game.events.GameEvent;
 import mage.game.events.ZoneChangeEvent;
 import mage.game.permanent.Permanent;
+import mage.game.permanent.token.RedElementalToken;
 import mage.game.permanent.token.Token;
-import mage.game.permanent.token.YoungPyromancerElementalToken;
 import mage.players.Player;
 import mage.target.common.TargetCardInYourGraveyard;
 import mage.target.targetpointer.FixedTarget;
@@ -100,7 +99,7 @@ class ChandraAcolyteOfFlameEffect extends OneShotEffect {
 
     @Override
     public boolean apply(Game game, Ability source) {
-        Token token = new YoungPyromancerElementalToken();
+        Token token = new RedElementalToken();
         token.putOntoBattlefield(2, game, source.getSourceId(), source.getControllerId());
 
         token.getLastAddedTokenIds().stream().forEach(permId -> {
diff --git a/Mage.Sets/src/mage/cards/e/ElementalAppeal.java b/Mage.Sets/src/mage/cards/e/ElementalAppeal.java
index d7346d75ce..bfffb2e6b2 100644
--- a/Mage.Sets/src/mage/cards/e/ElementalAppeal.java
+++ b/Mage.Sets/src/mage/cards/e/ElementalAppeal.java
@@ -1,9 +1,5 @@
-
 package mage.cards.e;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
 import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.condition.common.KickedCondition;
@@ -21,10 +17,13 @@ import mage.filter.predicate.Predicate;
 import mage.filter.predicate.Predicates;
 import mage.filter.predicate.mageobject.CardIdPredicate;
 import mage.game.Game;
-import mage.game.permanent.token.ElementalAppealElementalToken;
+import mage.game.permanent.token.RedElementalWithTrampleAndHaste;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
 
 /**
- *
  * @author North
  */
 public final class ElementalAppeal extends CardImpl {
@@ -69,7 +68,7 @@ class ElementalAppealEffect extends OneShotEffect {
 
     @Override
     public boolean apply(Game game, Ability source) {
-        CreateTokenEffect effect = new CreateTokenEffect(new ElementalAppealElementalToken());
+        CreateTokenEffect effect = new CreateTokenEffect(new RedElementalWithTrampleAndHaste());
         if (effect.apply(game, source)) {
             effect.exileTokensCreatedAtNextEndStep(game, source);
             if (KickedCondition.instance.apply(game, source)) {
diff --git a/Mage.Sets/src/mage/cards/m/MaskOfImmolation.java b/Mage.Sets/src/mage/cards/m/MaskOfImmolation.java
index 97688431cd..60cb9da3b8 100644
--- a/Mage.Sets/src/mage/cards/m/MaskOfImmolation.java
+++ b/Mage.Sets/src/mage/cards/m/MaskOfImmolation.java
@@ -16,7 +16,7 @@ import mage.constants.CardType;
 import mage.constants.SubType;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
-import mage.game.permanent.token.YoungPyromancerElementalToken;
+import mage.game.permanent.token.RedElementalToken;
 import mage.players.Player;
 import mage.target.common.TargetAnyTarget;
 
@@ -59,7 +59,7 @@ public final class MaskOfImmolation extends CardImpl {
 class MaskOfImmolationEffect extends CreateTokenEffect {
 
     MaskOfImmolationEffect() {
-        super(new YoungPyromancerElementalToken());
+        super(new RedElementalToken());
         staticText = "create a 1/1 red Elemental creature token, then attach {this} to it.";
     }
 
diff --git a/Mage.Sets/src/mage/cards/s/ScamperingScorcher.java b/Mage.Sets/src/mage/cards/s/ScamperingScorcher.java
index fbbbc088bf..9561b437f8 100644
--- a/Mage.Sets/src/mage/cards/s/ScamperingScorcher.java
+++ b/Mage.Sets/src/mage/cards/s/ScamperingScorcher.java
@@ -13,7 +13,7 @@ import mage.constants.Duration;
 import mage.constants.SubType;
 import mage.filter.FilterPermanent;
 import mage.filter.common.FilterControlledCreaturePermanent;
-import mage.game.permanent.token.YoungPyromancerElementalToken;
+import mage.game.permanent.token.RedElementalToken;
 
 import java.util.UUID;
 
@@ -34,7 +34,7 @@ public final class ScamperingScorcher extends CardImpl {
 
         // When Scampering Scorcher enters the battlefield, create two 1/1 red Elemental creature tokens. Elementals you control gain haste until end of turn.
         Ability ability = new EntersBattlefieldTriggeredAbility(
-                new CreateTokenEffect(new YoungPyromancerElementalToken(), 2)
+                new CreateTokenEffect(new RedElementalToken(), 2)
         );
         ability.addEffect(new GainAbilityControlledEffect(
                 HasteAbility.getInstance(), Duration.EndOfTurn, filter
diff --git a/Mage.Sets/src/mage/cards/s/SeasonedPyromancer.java b/Mage.Sets/src/mage/cards/s/SeasonedPyromancer.java
index 5aed1a1767..f1e705b89d 100644
--- a/Mage.Sets/src/mage/cards/s/SeasonedPyromancer.java
+++ b/Mage.Sets/src/mage/cards/s/SeasonedPyromancer.java
@@ -16,7 +16,7 @@ import mage.constants.SubType;
 import mage.constants.Zone;
 import mage.filter.StaticFilters;
 import mage.game.Game;
-import mage.game.permanent.token.YoungPyromancerElementalToken;
+import mage.game.permanent.token.RedElementalToken;
 import mage.players.Player;
 
 import java.util.UUID;
@@ -40,7 +40,7 @@ public final class SeasonedPyromancer extends CardImpl {
         // {3}{R}{R}, Exile Seasoned Pyromancer from your graveyard: Create two 1/1 red Elemental creature tokens.
         Ability ability = new SimpleActivatedAbility(
                 Zone.GRAVEYARD,
-                new CreateTokenEffect(new YoungPyromancerElementalToken(), 2),
+                new CreateTokenEffect(new RedElementalToken(), 2),
                 new ManaCostsImpl("{3}{R}{R}")
         );
         ability.addCost(new ExileSourceFromGraveCost());
@@ -88,7 +88,7 @@ class SeasonedPyromancerEffect extends OneShotEffect {
         if (nonlands == 0) {
             return true;
         }
-        new YoungPyromancerElementalToken().putOntoBattlefield(
+        new RedElementalToken().putOntoBattlefield(
                 nonlands, game, source.getSourceId(), source.getControllerId()
         );
         return true;
diff --git a/Mage.Sets/src/mage/cards/y/YoungPyromancer.java b/Mage.Sets/src/mage/cards/y/YoungPyromancer.java
index d5e647087f..6c5f1f6f24 100644
--- a/Mage.Sets/src/mage/cards/y/YoungPyromancer.java
+++ b/Mage.Sets/src/mage/cards/y/YoungPyromancer.java
@@ -8,7 +8,7 @@ import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.SubType;
 import mage.filter.StaticFilters;
-import mage.game.permanent.token.YoungPyromancerElementalToken;
+import mage.game.permanent.token.RedElementalToken;
 
 import java.util.UUID;
 
@@ -27,7 +27,7 @@ public final class YoungPyromancer extends CardImpl {
 
         // Whenever you cast an instant or sorcery spell, create a 1/1 red Elemental creature token.
         this.addAbility(new SpellCastControllerTriggeredAbility(
-                new CreateTokenEffect(new YoungPyromancerElementalToken()),
+                new CreateTokenEffect(new RedElementalToken()),
                 StaticFilters.FILTER_SPELL_AN_INSTANT_OR_SORCERY, false
         ));
 
diff --git a/Mage.Sets/src/mage/cards/z/ZektarShrineExpedition.java b/Mage.Sets/src/mage/cards/z/ZektarShrineExpedition.java
index 49c3e041c7..58e317fe83 100644
--- a/Mage.Sets/src/mage/cards/z/ZektarShrineExpedition.java
+++ b/Mage.Sets/src/mage/cards/z/ZektarShrineExpedition.java
@@ -1,7 +1,5 @@
-
 package mage.cards.z;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.common.LandfallAbility;
 import mage.abilities.common.SimpleActivatedAbility;
@@ -17,10 +15,11 @@ import mage.constants.Outcome;
 import mage.constants.Zone;
 import mage.counters.CounterType;
 import mage.game.Game;
-import mage.game.permanent.token.ZektarShrineElementalToken;
+import mage.game.permanent.token.RedElementalWithTrampleAndHaste;
+
+import java.util.UUID;
 
 /**
- *
  * @author North
  */
 public final class ZektarShrineExpedition extends CardImpl {
@@ -65,7 +64,7 @@ class ZektarShrineExpeditionEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
 
-        CreateTokenEffect effect = new CreateTokenEffect(new ZektarShrineElementalToken());
+        CreateTokenEffect effect = new CreateTokenEffect(new RedElementalWithTrampleAndHaste());
         if (effect.apply(game, source)) {
             effect.exileTokensCreatedAtNextEndStep(game, source);
             return true;
diff --git a/Mage/src/main/java/mage/game/permanent/token/AkoumStonewakerElementalToken.java b/Mage/src/main/java/mage/game/permanent/token/AkoumStonewakerElementalToken.java
index d992ba44f4..b4137682d7 100644
--- a/Mage/src/main/java/mage/game/permanent/token/AkoumStonewakerElementalToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/AkoumStonewakerElementalToken.java
@@ -23,6 +23,11 @@ public final class AkoumStonewakerElementalToken extends TokenImpl {
         this.addAbility(TrampleAbility.getInstance());
         this.addAbility(HasteAbility.getInstance());
         availableImageSetCodes.addAll(Arrays.asList("BFZ", "MH1"));
+    }
+
+    @Override
+    public void setExpansionSetCodeForImage(String code) {
+        super.setExpansionSetCodeForImage(code);
 
         if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("BFZ")) {
             setTokenType(2);
diff --git a/Mage/src/main/java/mage/game/permanent/token/CallTheSkyBreakerElementalToken.java b/Mage/src/main/java/mage/game/permanent/token/CallTheSkyBreakerElementalToken.java
index 1e43effc15..3b6ef19b38 100644
--- a/Mage/src/main/java/mage/game/permanent/token/CallTheSkyBreakerElementalToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/CallTheSkyBreakerElementalToken.java
@@ -1,4 +1,3 @@
-
 package mage.game.permanent.token;
 
 import mage.MageInt;
@@ -7,7 +6,6 @@ import mage.constants.CardType;
 import mage.constants.SubType;
 
 /**
- *
  * @author spjspj
  */
 public final class CallTheSkyBreakerElementalToken extends TokenImpl {
@@ -18,14 +16,20 @@ public final class CallTheSkyBreakerElementalToken extends TokenImpl {
         color.setBlue(true);
         color.setRed(true);
         subtype.add(SubType.ELEMENTAL);
-        if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("EMA")) {
-            setTokenType(2);
-        }
         power = new MageInt(5);
         toughness = new MageInt(5);
         this.addAbility(FlyingAbility.getInstance());
     }
 
+    @Override
+    public void setExpansionSetCodeForImage(String code) {
+        super.setExpansionSetCodeForImage(code);
+
+        if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("EMA")) {
+            setTokenType(2);
+        }
+    }
+
     public CallTheSkyBreakerElementalToken(final CallTheSkyBreakerElementalToken token) {
         super(token);
     }
diff --git a/Mage/src/main/java/mage/game/permanent/token/YoungPyromancerElementalToken.java b/Mage/src/main/java/mage/game/permanent/token/RedElementalToken.java
similarity index 60%
rename from Mage/src/main/java/mage/game/permanent/token/YoungPyromancerElementalToken.java
rename to Mage/src/main/java/mage/game/permanent/token/RedElementalToken.java
index e9d7a704c0..9b8fc4ce8c 100644
--- a/Mage/src/main/java/mage/game/permanent/token/YoungPyromancerElementalToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/RedElementalToken.java
@@ -5,37 +5,46 @@ import mage.constants.CardType;
 import mage.constants.SubType;
 import mage.util.RandomUtil;
 
+import java.util.Arrays;
+
 /**
  * @author spjspj
  */
-public final class YoungPyromancerElementalToken extends TokenImpl {
+public final class RedElementalToken extends TokenImpl {
 
-    public YoungPyromancerElementalToken() {
+    public RedElementalToken() {
         super("Elemental", "1/1 red Elemental creature token");
-        if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("M14")) {
-            setTokenType(RandomUtil.nextInt(2) + 1);
-        }
-        if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("EMA")) {
-            setTokenType(1);
-        }
-        if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("SHM")) {
-            setTokenType(2);
-        }
-        if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("MH1")) {
-            setTokenType(1);
-        }
         cardType.add(CardType.CREATURE);
         color.setRed(true);
         subtype.add(SubType.ELEMENTAL);
         power = new MageInt(1);
         toughness = new MageInt(1);
+
+        availableImageSetCodes = Arrays.asList("C13", "EMA", "M14", "SHM", "MH1", "M20");
     }
 
-    public YoungPyromancerElementalToken(final YoungPyromancerElementalToken token) {
+    @Override
+    public void setExpansionSetCodeForImage(String code) {
+        super.setExpansionSetCodeForImage(code);
+
+        if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("C13")) {
+            setTokenType(2);
+        }
+
+        if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("M14")) {
+            setTokenType(RandomUtil.nextInt(2) + 1);
+        }
+
+        if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("SHM")) {
+            setTokenType(2);
+        }
+    }
+
+    public RedElementalToken(final RedElementalToken token) {
         super(token);
     }
 
-    public YoungPyromancerElementalToken copy() {
-        return new YoungPyromancerElementalToken(this);
+    public RedElementalToken copy() {
+        return new RedElementalToken(this);
     }
 }
diff --git a/Mage/src/main/java/mage/game/permanent/token/ElementalAppealElementalToken.java b/Mage/src/main/java/mage/game/permanent/token/RedElementalWithTrampleAndHaste.java
similarity index 50%
rename from Mage/src/main/java/mage/game/permanent/token/ElementalAppealElementalToken.java
rename to Mage/src/main/java/mage/game/permanent/token/RedElementalWithTrampleAndHaste.java
index 0a54bf078f..30c9a7b197 100644
--- a/Mage/src/main/java/mage/game/permanent/token/ElementalAppealElementalToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/RedElementalWithTrampleAndHaste.java
@@ -1,4 +1,3 @@
-
 package mage.game.permanent.token;
 
 import mage.MageInt;
@@ -6,14 +5,14 @@ import mage.abilities.keyword.HasteAbility;
 import mage.abilities.keyword.TrampleAbility;
 import mage.constants.CardType;
 import mage.constants.SubType;
+import mage.util.RandomUtil;
 
 /**
- *
  * @author spjspj
  */
-public final class ElementalAppealElementalToken extends TokenImpl {
+public final class RedElementalWithTrampleAndHaste extends TokenImpl {
 
-    public ElementalAppealElementalToken() {
+    public RedElementalWithTrampleAndHaste() {
         super("Elemental", "7/1 red Elemental creature token with trample and haste");
         cardType.add(CardType.CREATURE);
         color.setRed(true);
@@ -24,11 +23,20 @@ public final class ElementalAppealElementalToken extends TokenImpl {
         addAbility(HasteAbility.getInstance());
     }
 
-    public ElementalAppealElementalToken(final ElementalAppealElementalToken token) {
+    @Override
+    public void setExpansionSetCodeForImage(String code) {
+        super.setExpansionSetCodeForImage(code);
+
+        if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("ZEN")) {
+            setTokenType(RandomUtil.nextInt(2) + 1);
+        }
+    }
+
+    public RedElementalWithTrampleAndHaste(final RedElementalWithTrampleAndHaste token) {
         super(token);
     }
 
-    public ElementalAppealElementalToken copy() {
-        return new ElementalAppealElementalToken(this);
+    public RedElementalWithTrampleAndHaste copy() {
+        return new RedElementalWithTrampleAndHaste(this);
     }
 }
diff --git a/Mage/src/main/java/mage/game/permanent/token/SeedGuardianToken.java b/Mage/src/main/java/mage/game/permanent/token/SeedGuardianToken.java
index ddf2bbefa6..1be69e9ca0 100644
--- a/Mage/src/main/java/mage/game/permanent/token/SeedGuardianToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/SeedGuardianToken.java
@@ -4,6 +4,8 @@ import mage.MageInt;
 import mage.constants.CardType;
 import mage.constants.SubType;
 
+import java.util.Arrays;
+
 /**
  * @author spjspj
  */
@@ -15,14 +17,13 @@ public final class SeedGuardianToken extends TokenImpl {
 
     public SeedGuardianToken(int xValue) {
         super("Elemental", "X/X green Elemental creature token");
-        setTokenType(1);
-        setOriginalExpansionSetCode("OGW");
         cardType.add(CardType.CREATURE);
         color.setGreen(true);
         subtype.add(SubType.ELEMENTAL);
         power = new MageInt(xValue);
         toughness = new MageInt(xValue);
 
+        availableImageSetCodes = Arrays.asList("C13", "CHK", "OGW");
     }
 
     public SeedGuardianToken(final SeedGuardianToken token) {
diff --git a/Mage/src/main/java/mage/game/permanent/token/TilonallisSummonerElementalToken.java b/Mage/src/main/java/mage/game/permanent/token/TilonallisSummonerElementalToken.java
index d2dcf64ce5..50eeebc52a 100644
--- a/Mage/src/main/java/mage/game/permanent/token/TilonallisSummonerElementalToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/TilonallisSummonerElementalToken.java
@@ -1,4 +1,3 @@
-
 package mage.game.permanent.token;
 
 import mage.MageInt;
@@ -6,20 +5,19 @@ import mage.constants.CardType;
 import mage.constants.SubType;
 
 /**
- *
  * @author LevelX2
  */
 public final class TilonallisSummonerElementalToken extends TokenImpl {
 
     public TilonallisSummonerElementalToken() {
         super("Elemental", "1/1 red Elemental creature tokens");
-        setTokenType(2);
         cardType.add(CardType.CREATURE);
         subtype.add(SubType.ELEMENTAL);
-
         color.setRed(true);
         power = new MageInt(1);
         toughness = new MageInt(1);
+
+        setTokenType(2);
     }
 
     public TilonallisSummonerElementalToken(final TilonallisSummonerElementalToken token) {
diff --git a/Mage/src/main/java/mage/game/permanent/token/WalkerOfTheGroveToken.java b/Mage/src/main/java/mage/game/permanent/token/WalkerOfTheGroveToken.java
index b07977696f..0a31fce579 100644
--- a/Mage/src/main/java/mage/game/permanent/token/WalkerOfTheGroveToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/WalkerOfTheGroveToken.java
@@ -1,12 +1,12 @@
-
-
 package mage.game.permanent.token;
+
+import mage.MageInt;
 import mage.constants.CardType;
 import mage.constants.SubType;
-import mage.MageInt;
+
+import java.util.Arrays;
 
 /**
- *
  * @author spjspj
  */
 public final class WalkerOfTheGroveToken extends TokenImpl {
@@ -18,6 +18,17 @@ public final class WalkerOfTheGroveToken extends TokenImpl {
         this.color.setGreen(true);
         power = new MageInt(4);
         toughness = new MageInt(4);
+
+        availableImageSetCodes = Arrays.asList("C13", "LRW", "MMA", "MOR");
+    }
+
+    @Override
+    public void setExpansionSetCodeForImage(String code) {
+        super.setExpansionSetCodeForImage(code);
+
+        if (getOriginalExpansionSetCode().equals("C13")) {
+            this.setTokenType(2);
+        }
     }
 
     public WalkerOfTheGroveToken(final WalkerOfTheGroveToken token) {
diff --git a/Mage/src/main/java/mage/game/permanent/token/WandOfTheElementsFirstToken.java b/Mage/src/main/java/mage/game/permanent/token/WandOfTheElementsFirstToken.java
index 40f6f42503..13fc1c12e9 100644
--- a/Mage/src/main/java/mage/game/permanent/token/WandOfTheElementsFirstToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/WandOfTheElementsFirstToken.java
@@ -1,4 +1,3 @@
-
 package mage.game.permanent.token;
 
 import mage.MageInt;
@@ -7,20 +6,20 @@ import mage.constants.CardType;
 import mage.constants.SubType;
 
 /**
- *
  * @author spjspj
  */
 public final class WandOfTheElementsFirstToken extends TokenImpl {
 
     public WandOfTheElementsFirstToken() {
         super("Elemental", "2/2 blue Elemental creature token with flying");
-        setTokenType(1);
         cardType.add(CardType.CREATURE);
         this.subtype.add(SubType.ELEMENTAL);
         this.color.setBlue(true);
         power = new MageInt(2);
         toughness = new MageInt(2);
         this.addAbility(FlyingAbility.getInstance());
+
+        setTokenType(1);
     }
 
     public WandOfTheElementsFirstToken(final WandOfTheElementsFirstToken token) {
diff --git a/Mage/src/main/java/mage/game/permanent/token/WandOfTheElementsSecondToken.java b/Mage/src/main/java/mage/game/permanent/token/WandOfTheElementsSecondToken.java
index 12e38624bd..43baf70390 100644
--- a/Mage/src/main/java/mage/game/permanent/token/WandOfTheElementsSecondToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/WandOfTheElementsSecondToken.java
@@ -1,4 +1,3 @@
-
 package mage.game.permanent.token;
 
 import mage.MageInt;
@@ -6,19 +5,19 @@ import mage.constants.CardType;
 import mage.constants.SubType;
 
 /**
- *
  * @author spjspj
  */
 public final class WandOfTheElementsSecondToken extends TokenImpl {
 
     public WandOfTheElementsSecondToken() {
         super("Elemental", "3/3 red Elemental creature token");
-        setTokenType(2);
         cardType.add(CardType.CREATURE);
         this.subtype.add(SubType.ELEMENTAL);
         this.color.setRed(true);
         power = new MageInt(3);
         toughness = new MageInt(3);
+
+        setTokenType(2);
     }
 
     public WandOfTheElementsSecondToken(final WandOfTheElementsSecondToken token) {
diff --git a/Mage/src/main/java/mage/game/permanent/token/WhiteElementalToken.java b/Mage/src/main/java/mage/game/permanent/token/WhiteElementalToken.java
index 5c84545558..59ea1d700e 100644
--- a/Mage/src/main/java/mage/game/permanent/token/WhiteElementalToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/WhiteElementalToken.java
@@ -5,6 +5,8 @@ import mage.abilities.keyword.FlyingAbility;
 import mage.constants.CardType;
 import mage.constants.SubType;
 
+import java.util.Arrays;
+
 /**
  * @author spjspj
  */
@@ -19,9 +21,20 @@ public final class WhiteElementalToken extends TokenImpl {
         toughness = new MageInt(4);
         this.addAbility(FlyingAbility.getInstance());
 
+        availableImageSetCodes = Arrays.asList("LRW", "C16", "C20", "RTR");
+    }
+
+    @Override
+    public void setExpansionSetCodeForImage(String code) {
+        super.setExpansionSetCodeForImage(code);
+
         if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("C20")) {
             setTokenType(2);
         }
+
+        if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("LRW")) {
+            setTokenType(2);
+        }
     }
 
     public WhiteElementalToken(final WhiteElementalToken token) {
diff --git a/Mage/src/main/java/mage/game/permanent/token/ZektarShrineElementalToken.java b/Mage/src/main/java/mage/game/permanent/token/ZektarShrineElementalToken.java
deleted file mode 100644
index 1d836890a8..0000000000
--- a/Mage/src/main/java/mage/game/permanent/token/ZektarShrineElementalToken.java
+++ /dev/null
@@ -1,34 +0,0 @@
-
-package mage.game.permanent.token;
-
-import mage.MageInt;
-import mage.abilities.keyword.HasteAbility;
-import mage.abilities.keyword.TrampleAbility;
-import mage.constants.CardType;
-import mage.constants.SubType;
-
-/**
- *
- * @author spjspj
- */
-public final class ZektarShrineElementalToken extends TokenImpl {
-
-    public ZektarShrineElementalToken() {
-        super("Elemental", "7/1 red Elemental creature token with trample and haste");
-        cardType.add(CardType.CREATURE);
-        color.setRed(true);
-        subtype.add(SubType.ELEMENTAL);
-        power = new MageInt(7);
-        toughness = new MageInt(1);
-        addAbility(TrampleAbility.getInstance());
-        addAbility(HasteAbility.getInstance());
-    }
-
-    public ZektarShrineElementalToken(final ZektarShrineElementalToken token) {
-        super(token);
-    }
-
-    public ZektarShrineElementalToken copy() {
-        return new ZektarShrineElementalToken(this);
-    }
-}

From 9df32a571be84079777ad9fa584ff369cec0757c Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Mon, 15 Jun 2020 22:57:29 +0400
Subject: [PATCH 282/586] * Images: added embalm ability tokens and missed
 Insect token from Amonkhet (see #3201);

---
 .../sources/ScryfallImageSupportTokens.java   | 22 ++++++++++--
 .../mage/plugins/card/images/ImageCache.java  | 30 ++++++++++++----
 .../src/main/resources/card-pictures-tok.txt  | 36 ++++++++++---------
 3 files changed, 63 insertions(+), 25 deletions(-)

diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java
index 15490b5f0e..908dd265ff 100644
--- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java
@@ -94,16 +94,34 @@ public class ScryfallImageSupportTokens {
             put("HOU/Insect", "https://api.scryfall.com/cards/thou/12/en?format=image");
             put("HOU/Snake", "https://api.scryfall.com/cards/thou/11/en?format=image");
 
-            //AKH
+            //AKH - tokens
             put("AKH/Beast", "https://api.scryfall.com/cards/takh/21/en?format=image");
             put("AKH/Cat", "https://api.scryfall.com/cards/takh/16/en?format=image");
             put("AKH/Drake", "https://api.scryfall.com/cards/takh/18/en?format=image");
             put("AKH/Emblem Gideon", "https://api.scryfall.com/cards/takh/25/en?format=image");
             put("AKH/Hippo", "https://api.scryfall.com/cards/takh/22/en?format=image");
+            put("AKH/Insect", "https://api.scryfall.com/cards/takh/19/en?format=image");
             put("AKH/Snake", "https://api.scryfall.com/cards/takh/23/en?format=image");
             put("AKH/Warrior", "https://api.scryfall.com/cards/takh/17/en?format=image");
             put("AKH/Wurm", "https://api.scryfall.com/cards/takh/24/en?format=image");
             put("AKH/Zombie", "https://api.scryfall.com/cards/takh/20/en?format=image");
+            //AKH - embalm ability (token from card)
+            put("AKH/Angel of Sanctions", "https://api.scryfall.com/cards/takh/1/en?format=image");
+            put("AKH/Anointer Priest", "https://api.scryfall.com/cards/takh/2/en?format=image");
+            put("AKH/Aven Initiate", "https://api.scryfall.com/cards/takh/3/en?format=image");
+            put("AKH/Aven Wind Guide", "https://api.scryfall.com/cards/takh/4/en?format=image");
+            put("AKH/Gideon of the Trials Emblem", "https://api.scryfall.com/cards/takh/25/en?format=image");
+            put("AKH/Glyph Keeper", "https://api.scryfall.com/cards/takh/5/en?format=image");
+            put("AKH/Heart-Piercer Manticore", "https://api.scryfall.com/cards/takh/6/en?format=image");
+            put("AKH/Honored Hydra", "https://api.scryfall.com/cards/takh/7/en?format=image");
+            put("AKH/Labyrinth Guardian", "https://api.scryfall.com/cards/takh/8/en?format=image");
+            put("AKH/Oketra's Attendant", "https://api.scryfall.com/cards/takh/9/en?format=image");
+            put("AKH/Sacred Cat", "https://api.scryfall.com/cards/takh/10/en?format=image");
+            put("AKH/Tah-Crop Skirmisher", "https://api.scryfall.com/cards/takh/11/en?format=image");
+            put("AKH/Temmet, Vizier of Naktamun", "https://api.scryfall.com/cards/takh/12/en?format=image");
+            put("AKH/Trueheart Duelist", "https://api.scryfall.com/cards/takh/13/en?format=image");
+            put("AKH/Unwavering Initiate", "https://api.scryfall.com/cards/takh/14/en?format=image");
+            put("AKH/Vizier of Many Faces", "https://api.scryfall.com/cards/takh/15/en?format=image");
 
             //AER
             put("AER/Etherium Cell", "https://api.scryfall.com/cards/taer/3/en?format=image");
@@ -187,7 +205,7 @@ public class ScryfallImageSupportTokens {
             put("BFZ/Plant", "https://api.scryfall.com/cards/tbfz/10/en?format=image");
 
             // C17
-            
+
 
             // WAR
             put("WAR/Angel", "https://api.scryfall.com/cards/twar/2/en?format=image");
diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java b/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java
index ae54361ff4..0b3b528436 100644
--- a/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java
@@ -84,13 +84,21 @@ public final class ImageCache {
 
                         boolean cardback = false;
                         String path;
-                        if (collectorId.isEmpty() || "0".equals(collectorId)) {
+                        if (collectorId.isEmpty() || "0".equals(collectorId) || !tokenDescriptor.isEmpty()) { // tokenDescriptor for embalm ability
                             info.setToken(true);
                             path = CardImageUtils.generateTokenImagePath(info);
                             if (path == null) {
                                 cardback = true;
-                                // TODO: replace empty token by other default card, not cardback
-                                path = CardImageUtils.buildImagePathToDefault(DirectLinksForDownload.cardbackFilename);
+                                // try token image from card
+                                CardDownloadData newInfo = new CardDownloadData(info);
+                                newInfo.setToken(false);
+                                path = CardImageUtils.buildImagePathToCard(newInfo);
+                                TFile tokenFile = getTFile(path);
+                                if (tokenFile == null || !tokenFile.exists()) {
+                                    // token empty token image
+                                    // TODO: replace empty token by other default card, not cardback
+                                    path = CardImageUtils.buildImagePathToDefault(DirectLinksForDownload.cardbackFilename);
+                                }
                             }
                         } else {
                             path = CardImageUtils.buildImagePathToCard(info);
@@ -245,12 +253,20 @@ public final class ImageCache {
             CardDownloadData info = new CardDownloadData(name, set, collectorId, usesVariousArt, type, tokenSetCode, tokenDescriptor);
 
             String path;
-            if (collectorId.isEmpty() || "0".equals(collectorId)) {
+            if (collectorId.isEmpty() || "0".equals(collectorId) || !tokenDescriptor.isEmpty()) { // tokenDescriptor for embalm ability
                 info.setToken(true);
-                path = CardImageUtils.generateFullTokenImagePath(info);
+                path = CardImageUtils.generateTokenImagePath(info);
                 if (path == null) {
-                    // TODO: replace empty token by other default card, not cardback
-                    path = CardImageUtils.buildImagePathToDefault(DirectLinksForDownload.cardbackFilename);
+                    // try token image from card
+                    CardDownloadData newInfo = new CardDownloadData(info);
+                    newInfo.setToken(false);
+                    path = CardImageUtils.buildImagePathToCard(newInfo);
+                    TFile tokenFile = getTFile(path);
+                    if (tokenFile == null || !tokenFile.exists()) {
+                        // token empty token image
+                        // TODO: replace empty token by other default card, not cardback
+                        path = CardImageUtils.buildImagePathToDefault(DirectLinksForDownload.cardbackFilename);
+                    }
                 }
             } else {
                 path = CardImageUtils.buildImagePathToCard(info);
diff --git a/Mage.Client/src/main/resources/card-pictures-tok.txt b/Mage.Client/src/main/resources/card-pictures-tok.txt
index 2ba1244683..d259a41e0a 100644
--- a/Mage.Client/src/main/resources/card-pictures-tok.txt
+++ b/Mage.Client/src/main/resources/card-pictures-tok.txt
@@ -168,30 +168,34 @@
 |Generate|TOK:AER|Etherium Cell|||EtheriumCellToken|
 |Generate|TOK:AER|Gremlin|||GremlinToken|
 |Generate|TOK:AER|Ragavan|||RagavanToken|
+
+# AKH
 |Generate|TOK:AKH|Beast|||BeastToken3|
 |Generate|TOK:AKH|Cat|||CatToken2|
 |Generate|TOK:AKH|Drake|||DrakeToken|
 |Generate|TOK:AKH|Hippo|||HippoToken2|
+|Generate|TOK:AKH|Insect|||NestOfScarabsBlackInsectToken|
 |Generate|TOK:AKH|Snake|||DeathtouchSnakeToken|
 |Generate|TOK:AKH|Warrior|||WarriorVigilantToken|
 |Generate|TOK:AKH|Wurm|||Wurm55Token|
-|Generate|TOK:AKH|Zombie|||ZombieToken
+|Generate|TOK:AKH|Zombie|||ZombieToken|
 #TOK:AKH - some tokens from real cards (see Embalm ability)
-#|Generate|TOK:AKH|Angel of Sanctions||
-#|Generate|TOK:AKH|Anointer Priest||
-#|Generate|TOK:AKH|Aven Initiate||
-#|Generate|TOK:AKH|Aven Wind Guide||
-#|Generate|TOK:AKH|Glyph Keeper||
-#|Generate|TOK:AKH|Heart-Piercer Manticore||
-#|Generate|TOK:AKH|Honored Hydra||
-#|Generate|TOK:AKH|Labyrinth Guardian||
-#|Generate|TOK:AKH|Oketra's Attendant||
-#|Generate|TOK:AKH|Sacred Cat||
-#|Generate|TOK:AKH|Tah-Crop Skirmisher||
-#|Generate|TOK:AKH|Temmet, Vizier of Naktamun||
-#|Generate|TOK:AKH|Trueheart Duelist||
-#|Generate|TOK:AKH|Unwavering Initiate||
-#|Generate|TOK:AKH|Vizier of Many Faces||
+|Generate|TOK:AKH|Angel of Sanctions||
+|Generate|TOK:AKH|Anointer Priest||
+|Generate|TOK:AKH|Aven Initiate||
+|Generate|TOK:AKH|Aven Wind Guide||
+|Generate|TOK:AKH|Glyph Keeper||
+|Generate|TOK:AKH|Heart-Piercer Manticore||
+|Generate|TOK:AKH|Honored Hydra||
+|Generate|TOK:AKH|Labyrinth Guardian||
+|Generate|TOK:AKH|Oketra's Attendant||
+|Generate|TOK:AKH|Sacred Cat||
+|Generate|TOK:AKH|Tah-Crop Skirmisher||
+|Generate|TOK:AKH|Temmet, Vizier of Naktamun||
+|Generate|TOK:AKH|Trueheart Duelist||
+|Generate|TOK:AKH|Unwavering Initiate||
+|Generate|TOK:AKH|Vizier of Many Faces||
+
 |Generate|TOK:ALA|Beast|||GodSireBeastToken|
 |Generate|TOK:ALA|Dragon|||DragonToken|
 |Generate|TOK:ALA|Goblin|||GoblinTokenWithHaste|

From 2788eab082ef97bf83995f0384d24286926b7567 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Mon, 15 Jun 2020 21:11:54 +0200
Subject: [PATCH 283/586] * Fixed card type displaying order (fixes #6055).

---
 .../client/dialog/CardInfoWindowDialog.java   | 30 +++++++++++--------
 .../java/mage/client/game/PlayerPanelExt.java | 25 +++++++---------
 .../src/main/java/mage/view/AbilityView.java  |  8 ++---
 .../src/main/java/mage/view/CardView.java     | 27 ++++++++---------
 .../src/mage/cards/c/CrawlingSensation.java   |  6 ----
 .../src/mage/cards/d/DesecratedTomb.java      |  5 ----
 Mage.Sets/src/mage/cards/m/ManaClash.java     |  6 ++--
 .../src/mage/cards/p/PedanticLearning.java    |  4 ---
 .../src/mage/cards/p/PossibilityStorm.java    |  6 ++--
 .../src/mage/cards/s/SidisiBroodTyrant.java   |  6 ----
 .../src/mage/cards/t/TheGitrogMonster.java    |  8 ++---
 .../src/mage/cards/t/TurntimberSower.java     |  5 +---
 Mage/src/main/java/mage/MageObject.java       |  7 +++--
 Mage/src/main/java/mage/MageObjectImpl.java   |  9 +++---
 .../common/CardTypesInGraveyardCount.java     |  5 ++--
 .../java/mage/cards/repository/CardInfo.java  | 11 ++++---
 .../main/java/mage/constants/CardType.java    |  7 ++---
 .../java/mage/designations/Designation.java   | 13 ++++----
 .../java/mage/game/command/Commander.java     | 10 +++----
 .../main/java/mage/game/command/Emblem.java   | 12 ++++----
 .../main/java/mage/game/command/Plane.java    | 14 ++++-----
 .../TilonallisSummonerElementalToken.java     |  1 +
 Mage/src/main/java/mage/game/stack/Spell.java |  9 +++---
 .../java/mage/game/stack/StackAbility.java    | 13 ++++----
 24 files changed, 106 insertions(+), 141 deletions(-)

diff --git a/Mage.Client/src/main/java/mage/client/dialog/CardInfoWindowDialog.java b/Mage.Client/src/main/java/mage/client/dialog/CardInfoWindowDialog.java
index 8b4d90ef0d..4a133d4fdc 100644
--- a/Mage.Client/src/main/java/mage/client/dialog/CardInfoWindowDialog.java
+++ b/Mage.Client/src/main/java/mage/client/dialog/CardInfoWindowDialog.java
@@ -1,5 +1,14 @@
 package mage.client.dialog;
 
+import java.awt.*;
+import java.beans.PropertyVetoException;
+import java.util.EnumSet;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.util.UUID;
+import javax.swing.*;
+import javax.swing.event.InternalFrameAdapter;
+import javax.swing.event.InternalFrameEvent;
 import mage.client.cards.BigCard;
 import mage.client.util.GUISizeHelper;
 import mage.client.util.ImageHelper;
@@ -13,15 +22,6 @@ import mage.view.SimpleCardsView;
 import org.apache.log4j.Logger;
 import org.mage.plugins.card.utils.impl.ImageManagerImpl;
 
-import javax.swing.*;
-import javax.swing.event.InternalFrameAdapter;
-import javax.swing.event.InternalFrameEvent;
-import java.awt.*;
-import java.beans.PropertyVetoException;
-import java.util.LinkedHashSet;
-import java.util.Set;
-import java.util.UUID;
-
 /**
  * @author BetaSteward_at_googlemail.com, JayDi85
  */
@@ -77,7 +77,7 @@ public class CardInfoWindowDialog extends MageDialog {
                 this.setClosable(false);
                 break;
             default:
-                // no icon yet
+            // no icon yet
         }
         this.setTitelBarToolTip(name);
         setGUISize();
@@ -174,13 +174,17 @@ public class CardInfoWindowDialog extends MageDialog {
         Set<String> cardTypesPresent = new LinkedHashSet<String>() {
         };
         for (CardView card : cardsView.values()) {
-            Set<CardType> cardTypes = card.getCardTypes();
+            Set<CardType> cardTypes = EnumSet.noneOf(CardType.class);
+            cardTypes.addAll(card.getCardTypes());
             for (CardType cardType : cardTypes) {
                 cardTypesPresent.add(cardType.toString());
             }
         }
-        if (cardTypesPresent.isEmpty()) return 0;
-        else return cardTypesPresent.size();
+        if (cardTypesPresent.isEmpty()) {
+            return 0;
+        } else {
+            return cardTypesPresent.size();
+        }
     }
 
     /**
diff --git a/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java b/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java
index f0c8085cc6..c7d7ac32d0 100644
--- a/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java
+++ b/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java
@@ -1,5 +1,13 @@
 package mage.client.game;
 
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.util.*;
+import javax.swing.*;
+import javax.swing.GroupLayout.Alignment;
+import javax.swing.LayoutStyle.ComponentPlacement;
+import javax.swing.border.Border;
+import javax.swing.border.LineBorder;
 import mage.cards.decks.importer.DckDeckImporter;
 import mage.client.MageFrame;
 import mage.client.SessionHandler;
@@ -15,6 +23,7 @@ import mage.client.util.gui.countryBox.CountryUtil;
 import mage.components.ImagePanel;
 import mage.components.ImagePanelStyle;
 import mage.constants.CardType;
+import static mage.constants.Constants.*;
 import mage.constants.ManaType;
 import mage.counters.Counter;
 import mage.counters.CounterType;
@@ -23,17 +32,6 @@ import mage.utils.timer.PriorityTimer;
 import mage.view.*;
 import org.mage.card.arcane.ManaSymbols;
 
-import javax.swing.*;
-import javax.swing.GroupLayout.Alignment;
-import javax.swing.LayoutStyle.ComponentPlacement;
-import javax.swing.border.Border;
-import javax.swing.border.LineBorder;
-import java.awt.*;
-import java.awt.image.BufferedImage;
-import java.util.*;
-
-import static mage.constants.Constants.*;
-
 /**
  * Enhanced player pane.
  *
@@ -42,7 +40,6 @@ import static mage.constants.Constants.*;
 public class PlayerPanelExt extends javax.swing.JPanel {
 
     // TODO: *.form file was lost, panel must be reworks in designer
-
     private UUID playerId;
     private UUID gameId;
     private PlayerView player;
@@ -612,7 +609,6 @@ public class PlayerPanelExt extends javax.swing.JPanel {
         manaCountLabelW.addMouseListener(manaMouseAdapter);
         manaLabels.put(manaCountLabelW, ManaType.WHITE);l
         //*/
-
         ///*
         JLabel manaCountLabelW = new JLabel();
         manaCountLabelW.setToolTipText("White mana");
@@ -944,7 +940,8 @@ public class PlayerPanelExt extends javax.swing.JPanel {
         Set<String> cardTypesPresent = new LinkedHashSet<String>() {
         };
         for (CardView card : cardsView.values()) {
-            Set<CardType> cardTypes = card.getCardTypes();
+            Set<CardType> cardTypes = EnumSet.noneOf(CardType.class);
+            cardTypes.addAll(card.getCardTypes());
             for (CardType cardType : cardTypes) {
                 cardTypesPresent.add(cardType.toString());
             }
diff --git a/Mage.Common/src/main/java/mage/view/AbilityView.java b/Mage.Common/src/main/java/mage/view/AbilityView.java
index 77e767d7af..e01a7d5404 100644
--- a/Mage.Common/src/main/java/mage/view/AbilityView.java
+++ b/Mage.Common/src/main/java/mage/view/AbilityView.java
@@ -1,14 +1,13 @@
 package mage.view;
 
+import java.util.ArrayList;
+import java.util.EnumSet;
 import mage.ObjectColor;
 import mage.abilities.Ability;
 import mage.constants.CardType;
 import mage.constants.SuperType;
 import mage.util.SubTypeList;
 
-import java.util.ArrayList;
-import java.util.EnumSet;
-
 /**
  * @author BetaSteward_at_googlemail.com
  */
@@ -29,7 +28,7 @@ public class AbilityView extends CardView {
         this.power = "";
         this.toughness = "";
         this.loyalty = "";
-        this.cardTypes = EnumSet.noneOf(CardType.class);
+        this.cardTypes = new ArrayList<CardType>();
         this.subTypes = new SubTypeList();
         this.superTypes = EnumSet.noneOf(SuperType.class);
         this.color = new ObjectColor();
@@ -45,5 +44,4 @@ public class AbilityView extends CardView {
         this.name = name;
     }
 
-
 }
diff --git a/Mage.Common/src/main/java/mage/view/CardView.java b/Mage.Common/src/main/java/mage/view/CardView.java
index 5dc0de29e3..2ea469997a 100644
--- a/Mage.Common/src/main/java/mage/view/CardView.java
+++ b/Mage.Common/src/main/java/mage/view/CardView.java
@@ -1,6 +1,8 @@
 package mage.view;
 
 import com.google.gson.annotations.Expose;
+import java.util.*;
+import java.util.stream.Collectors;
 import mage.MageObject;
 import mage.ObjectColor;
 import mage.abilities.Abilities;
@@ -29,9 +31,6 @@ import mage.target.Targets;
 import mage.util.CardUtil;
 import mage.util.SubTypeList;
 
-import java.util.*;
-import java.util.stream.Collectors;
-
 /**
  * @author BetaSteward_at_googlemail.com
  */
@@ -55,7 +54,7 @@ public class CardView extends SimpleCardView {
     @Expose
     protected String loyalty = "";
     protected String startingLoyalty;
-    protected Set<CardType> cardTypes;
+    protected ArrayList<CardType> cardTypes;
     protected SubTypeList subTypes;
     protected Set<SuperType> superTypes;
     protected ObjectColor color;
@@ -151,7 +150,7 @@ public class CardView extends SimpleCardView {
         this.toughness = cardView.toughness;
         this.loyalty = cardView.loyalty;
         this.startingLoyalty = cardView.startingLoyalty;
-        this.cardTypes = new HashSet<>(cardView.cardTypes);
+        this.cardTypes = new ArrayList<>(cardView.cardTypes);
         this.subTypes = new SubTypeList(cardView.subTypes);
         this.superTypes = cardView.superTypes;
 
@@ -213,8 +212,8 @@ public class CardView extends SimpleCardView {
      * @param card
      * @param game
      * @param controlled is the card view created for the card controller - used
-     *                   for morph / face down cards to know which player may see information for
-     *                   the card
+     * for morph / face down cards to know which player may see information for
+     * the card
      */
     public CardView(Card card, Game game, boolean controlled) {
         this(card, game, controlled, false, false);
@@ -240,12 +239,12 @@ public class CardView extends SimpleCardView {
     /**
      * @param card
      * @param game
-     * @param controlled       is the card view created for the card controller - used
-     *                         for morph / face down cards to know which player may see information for
-     *                         the card
+     * @param controlled is the card view created for the card controller - used
+     * for morph / face down cards to know which player may see information for
+     * the card
      * @param showFaceDownCard if true and the card is not on the battlefield,
-     *                         also a face down card is shown in the view, face down cards will be shown
-     * @param storeZone        if true the card zone will be set in the zone attribute.
+     * also a face down card is shown in the view, face down cards will be shown
+     * @param storeZone if true the card zone will be set in the zone attribute.
      */
     public CardView(Card card, Game game, boolean controlled, boolean showFaceDownCard, boolean storeZone) {
         super(card.getId(), card.getExpansionSetCode(), card.getCardNumber(), card.getUsesVariousArt(), card.getTokenSetCode(), game != null, card.getTokenDescriptor());
@@ -636,7 +635,7 @@ public class CardView extends SimpleCardView {
         this.toughness = "";
         this.loyalty = "";
         this.startingLoyalty = "";
-        this.cardTypes = EnumSet.noneOf(CardType.class);
+        this.cardTypes = new ArrayList<>();
         this.subTypes = new SubTypeList();
         this.superTypes = EnumSet.noneOf(SuperType.class);
         this.color = new ObjectColor();
@@ -764,7 +763,7 @@ public class CardView extends SimpleCardView {
         return startingLoyalty;
     }
 
-    public Set<CardType> getCardTypes() {
+    public ArrayList<CardType> getCardTypes() {
         return cardTypes;
     }
 
diff --git a/Mage.Sets/src/mage/cards/c/CrawlingSensation.java b/Mage.Sets/src/mage/cards/c/CrawlingSensation.java
index f3c2788771..83f3cd4b74 100644
--- a/Mage.Sets/src/mage/cards/c/CrawlingSensation.java
+++ b/Mage.Sets/src/mage/cards/c/CrawlingSensation.java
@@ -1,8 +1,5 @@
-
-
 package mage.cards.c;
 
-import java.util.Set;
 import java.util.UUID;
 import mage.abilities.TriggeredAbilityImpl;
 import mage.abilities.common.OnEventTriggeredAbility;
@@ -69,11 +66,8 @@ class CrawlingSensationTriggeredAbility extends TriggeredAbilityImpl {
                 for (Card card : zEvent.getCards()) {
                     if (card != null) {
                         UUID cardOwnerId = card.getOwnerId();
-                        Set<CardType> cardType = card.getCardType();
-
                         if (cardOwnerId != null
                                 && card.isOwnedBy(getControllerId())
-                                && cardType != null
                                 && card.isLand()) {
                             game.getState().setValue("usedOnTurn" + getControllerId() + getOriginalId(), game.getTurnNum());
                             return true;
diff --git a/Mage.Sets/src/mage/cards/d/DesecratedTomb.java b/Mage.Sets/src/mage/cards/d/DesecratedTomb.java
index b597b3e36d..0ca91ef33b 100644
--- a/Mage.Sets/src/mage/cards/d/DesecratedTomb.java
+++ b/Mage.Sets/src/mage/cards/d/DesecratedTomb.java
@@ -1,6 +1,5 @@
 package mage.cards.d;
 
-import java.util.Set;
 import java.util.UUID;
 import mage.abilities.TriggeredAbilityImpl;
 import mage.abilities.effects.common.CreateTokenEffect;
@@ -60,13 +59,9 @@ class DesecratedTombTriggeredAbility extends TriggeredAbilityImpl {
                 && zEvent.getCards() != null) {
             for (Card card : zEvent.getCards()) {
                 if (card != null) {
-
                     UUID cardOwnerId = card.getOwnerId();
-                    Set<CardType> cardType = card.getCardType();
-
                     if (cardOwnerId != null
                             && card.isOwnedBy(getControllerId())
-                            && cardType != null
                             && card.isCreature()) {
                         return true;
                     }
diff --git a/Mage.Sets/src/mage/cards/m/ManaClash.java b/Mage.Sets/src/mage/cards/m/ManaClash.java
index ef946eb397..1232a402f8 100644
--- a/Mage.Sets/src/mage/cards/m/ManaClash.java
+++ b/Mage.Sets/src/mage/cards/m/ManaClash.java
@@ -1,5 +1,6 @@
 package mage.cards.m;
 
+import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
 import mage.cards.CardImpl;
@@ -10,8 +11,6 @@ import mage.game.Game;
 import mage.players.Player;
 import mage.target.common.TargetOpponent;
 
-import java.util.UUID;
-
 /**
  * @author LevelX2
  */
@@ -20,7 +19,8 @@ public final class ManaClash extends CardImpl {
     public ManaClash(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{R}");
 
-        // You and target opponent each flip a coin. Mana Clash deals 1 damage to each player whose coin comes up tails. Repeat this process until both players' coins come up heads on the same flip.
+        // You and target opponent each flip a coin. Mana Clash deals 1 damage to each player whose coin comes up tails.
+        // Repeat this process until both players' coins come up heads on the same flip.
         this.getSpellAbility().addEffect(new ManaClashEffect());
         this.getSpellAbility().addTarget(new TargetOpponent());
     }
diff --git a/Mage.Sets/src/mage/cards/p/PedanticLearning.java b/Mage.Sets/src/mage/cards/p/PedanticLearning.java
index b72a54652c..369e68327b 100644
--- a/Mage.Sets/src/mage/cards/p/PedanticLearning.java
+++ b/Mage.Sets/src/mage/cards/p/PedanticLearning.java
@@ -1,7 +1,5 @@
-
 package mage.cards.p;
 
-import java.util.Set;
 import java.util.UUID;
 import mage.abilities.TriggeredAbilityImpl;
 import mage.abilities.costs.mana.ManaCostsImpl;
@@ -61,10 +59,8 @@ class PedanticLearningTriggeredAbility extends TriggeredAbilityImpl {
             Card card = game.getCard(event.getTargetId());
             if (card != null) {
                 UUID cardOwnerId = card.getOwnerId();
-                Set<CardType> cardType = card.getCardType();
                 if (cardOwnerId != null
                         && card.isOwnedBy(getControllerId())
-                        && cardType != null
                         && card.isLand()) {
                     return true;
                 }
diff --git a/Mage.Sets/src/mage/cards/p/PossibilityStorm.java b/Mage.Sets/src/mage/cards/p/PossibilityStorm.java
index 998707e955..134108fb51 100644
--- a/Mage.Sets/src/mage/cards/p/PossibilityStorm.java
+++ b/Mage.Sets/src/mage/cards/p/PossibilityStorm.java
@@ -1,8 +1,6 @@
-
 package mage.cards.p;
 
-import java.util.EnumSet;
-import java.util.Set;
+import java.util.ArrayList;
 import java.util.UUID;
 import mage.MageObject;
 import mage.MageObjectReference;
@@ -147,7 +145,7 @@ class PossibilityStormEffect extends OneShotEffect {
         return false;
     }
 
-    private boolean sharesType(Card card, Set<CardType> cardTypes) {
+    private boolean sharesType(Card card, ArrayList<CardType> cardTypes) {
         for (CardType type : card.getCardType()) {
             if (cardTypes.contains(type)) {
                 return true;
diff --git a/Mage.Sets/src/mage/cards/s/SidisiBroodTyrant.java b/Mage.Sets/src/mage/cards/s/SidisiBroodTyrant.java
index 933cbb20ce..b4bca16a13 100644
--- a/Mage.Sets/src/mage/cards/s/SidisiBroodTyrant.java
+++ b/Mage.Sets/src/mage/cards/s/SidisiBroodTyrant.java
@@ -1,7 +1,5 @@
-
 package mage.cards.s;
 
-import java.util.Set;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.TriggeredAbilityImpl;
@@ -73,13 +71,9 @@ class SidisiBroodTyrantTriggeredAbility extends TriggeredAbilityImpl {
         if (zEvent != null && Zone.LIBRARY == zEvent.getFromZone() && Zone.GRAVEYARD == zEvent.getToZone() && zEvent.getCards() != null) {
             for (Card card : zEvent.getCards()) {
                 if (card != null) {
-
                     UUID cardOwnerId = card.getOwnerId();
-                    Set<CardType> cardType = card.getCardType();
-
                     if (cardOwnerId != null
                             && card.isOwnedBy(getControllerId())
-                            && cardType != null
                             && card.isCreature()) {
                         return true;
                     }
diff --git a/Mage.Sets/src/mage/cards/t/TheGitrogMonster.java b/Mage.Sets/src/mage/cards/t/TheGitrogMonster.java
index 8fc2ee9ce1..a4073f519e 100644
--- a/Mage.Sets/src/mage/cards/t/TheGitrogMonster.java
+++ b/Mage.Sets/src/mage/cards/t/TheGitrogMonster.java
@@ -1,7 +1,5 @@
-
 package mage.cards.t;
 
-import java.util.Set;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.TriggeredAbilityImpl;
@@ -29,7 +27,7 @@ import mage.target.common.TargetControlledPermanent;
 public final class TheGitrogMonster extends CardImpl {
 
     public TheGitrogMonster(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}{G}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{G}");
         addSuperType(SuperType.LEGENDARY);
         this.subtype.add(SubType.FROG);
         this.subtype.add(SubType.HORROR);
@@ -79,12 +77,10 @@ class TheGitrogMonsterTriggeredAbility extends TriggeredAbilityImpl {
         ZoneChangeGroupEvent zEvent = (ZoneChangeGroupEvent) event;
         if (zEvent != null && Zone.GRAVEYARD == zEvent.getToZone() && zEvent.getCards() != null) {
             for (Card card : zEvent.getCards()) {
-                if (card != null) {                    
+                if (card != null) {
                     UUID cardOwnerId = card.getOwnerId();
-                    Set<CardType> cardType = card.getCardType();
                     if (cardOwnerId != null
                             && card.isOwnedBy(getControllerId())
-                            && cardType != null
                             && card.isLand()) {
                         return true;
                     }
diff --git a/Mage.Sets/src/mage/cards/t/TurntimberSower.java b/Mage.Sets/src/mage/cards/t/TurntimberSower.java
index 2db016a36c..ed47b60134 100644
--- a/Mage.Sets/src/mage/cards/t/TurntimberSower.java
+++ b/Mage.Sets/src/mage/cards/t/TurntimberSower.java
@@ -1,6 +1,5 @@
 package mage.cards.t;
 
-import java.util.Set;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
@@ -11,10 +10,10 @@ import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.abilities.effects.common.ReturnToHandTargetEffect;
 import mage.cards.Card;
-import mage.constants.SubType;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
+import mage.constants.SubType;
 import mage.constants.Zone;
 import mage.filter.StaticFilters;
 import mage.filter.common.FilterControlledCreaturePermanent;
@@ -94,10 +93,8 @@ class TurntimberSowerTriggeredAbility extends TriggeredAbilityImpl {
             for (Card card : zEvent.getCards()) {
                 if (card != null) {
                     UUID cardOwnerId = card.getOwnerId();
-                    Set<CardType> cardType = card.getCardType();
                     if (cardOwnerId != null
                             && card.isOwnedBy(getControllerId())
-                            && cardType != null
                             && card.isLand()) {
                         return true;
                     }
diff --git a/Mage/src/main/java/mage/MageObject.java b/Mage/src/main/java/mage/MageObject.java
index d25a2f3e5d..53bb7d9cef 100644
--- a/Mage/src/main/java/mage/MageObject.java
+++ b/Mage/src/main/java/mage/MageObject.java
@@ -1,6 +1,7 @@
 package mage;
 
 import java.io.Serializable;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
 import mage.abilities.Abilities;
@@ -30,7 +31,7 @@ public interface MageObject extends MageItem, Serializable {
 
     void setName(String name);
 
-    Set<CardType> getCardType();
+    ArrayList<CardType> getCardType();
 
     SubTypeList getSubtype(Game game);
 
@@ -41,6 +42,8 @@ public interface MageObject extends MageItem, Serializable {
     /**
      * For cards: return basic abilities (without dynamic added) For permanents:
      * return all abilities (dynamic ability inserts into permanent)
+     *
+     * @return
      */
     Abilities<Ability> getAbilities();
 
@@ -201,7 +204,7 @@ public interface MageObject extends MageItem, Serializable {
 
     void setIsAllCreatureTypes(boolean value);
 
-    default void addCardTypes(Set<CardType> cardType) {
+    default void addCardTypes(ArrayList<CardType> cardType) {
         getCardType().addAll(cardType);
     }
 
diff --git a/Mage/src/main/java/mage/MageObjectImpl.java b/Mage/src/main/java/mage/MageObjectImpl.java
index f25762695f..17e9bd22d3 100644
--- a/Mage/src/main/java/mage/MageObjectImpl.java
+++ b/Mage/src/main/java/mage/MageObjectImpl.java
@@ -1,5 +1,6 @@
 package mage;
 
+import java.util.*;
 import mage.abilities.Abilities;
 import mage.abilities.AbilitiesImpl;
 import mage.abilities.Ability;
@@ -21,8 +22,6 @@ import mage.game.permanent.Permanent;
 import mage.util.GameLog;
 import mage.util.SubTypeList;
 
-import java.util.*;
-
 public abstract class MageObjectImpl implements MageObject {
 
     protected UUID objectId;
@@ -32,7 +31,7 @@ public abstract class MageObjectImpl implements MageObject {
     protected ObjectColor color;
     protected ObjectColor frameColor;
     protected FrameStyle frameStyle;
-    protected Set<CardType> cardType = EnumSet.noneOf(CardType.class);
+    protected ArrayList<CardType> cardType = new ArrayList<>();
     protected SubTypeList subtype = new SubTypeList();
     protected boolean isAllCreatureTypes;
     protected Set<SuperType> supertype = EnumSet.noneOf(SuperType.class);
@@ -112,7 +111,7 @@ public abstract class MageObjectImpl implements MageObject {
     }
 
     @Override
-    public Set<CardType> getCardType() {
+    public ArrayList<CardType> getCardType() {
         return cardType;
     }
 
@@ -329,7 +328,7 @@ public abstract class MageObjectImpl implements MageObject {
      */
     @Override
     public void removePTCDA() {
-        for (Iterator<Ability> iter = this.getAbilities().iterator(); iter.hasNext(); ) {
+        for (Iterator<Ability> iter = this.getAbilities().iterator(); iter.hasNext();) {
             Ability ability = iter.next();
             for (Effect effect : ability.getEffects()) {
                 if (effect instanceof ContinuousEffect && ((ContinuousEffect) effect).getSublayer() == SubLayer.CharacteristicDefining_7a) {
diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/CardTypesInGraveyardCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/CardTypesInGraveyardCount.java
index 32b2d55318..4bdc931e36 100644
--- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/CardTypesInGraveyardCount.java
+++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/CardTypesInGraveyardCount.java
@@ -1,5 +1,6 @@
 package mage.abilities.dynamicvalue.common;
 
+import java.util.ArrayList;
 import mage.abilities.Ability;
 import mage.abilities.dynamicvalue.DynamicValue;
 import mage.abilities.effects.Effect;
@@ -8,8 +9,6 @@ import mage.constants.CardType;
 import mage.game.Game;
 import mage.players.Player;
 
-import java.util.EnumSet;
-
 /**
  * @author JayDi85
  */
@@ -21,7 +20,7 @@ public enum CardTypesInGraveyardCount implements DynamicValue {
     public int calculate(Game game, Ability sourceAbility, Effect effect) {
         Player controller = game.getPlayer(sourceAbility.getControllerId());
         if (controller != null) {
-            EnumSet<CardType> foundCardTypes = EnumSet.noneOf(CardType.class);
+            ArrayList<CardType> foundCardTypes = new ArrayList<>();
             for (Card card : controller.getGraveyard().getCards(game)) {
                 foundCardTypes.addAll(card.getCardType());
             }
diff --git a/Mage/src/main/java/mage/cards/repository/CardInfo.java b/Mage/src/main/java/mage/cards/repository/CardInfo.java
index 28921fc116..7b9b62305f 100644
--- a/Mage/src/main/java/mage/cards/repository/CardInfo.java
+++ b/Mage/src/main/java/mage/cards/repository/CardInfo.java
@@ -3,6 +3,8 @@ package mage.cards.repository;
 import com.j256.ormlite.field.DataType;
 import com.j256.ormlite.field.DatabaseField;
 import com.j256.ormlite.table.DatabaseTable;
+import java.util.*;
+import java.util.stream.Collectors;
 import mage.ObjectColor;
 import mage.abilities.Ability;
 import mage.abilities.SpellAbility;
@@ -15,9 +17,6 @@ import mage.util.CardUtil;
 import mage.util.SubTypeList;
 import org.apache.log4j.Logger;
 
-import java.util.*;
-import java.util.stream.Collectors;
-
 /**
  * @author North
  */
@@ -289,8 +288,8 @@ public class CardInfo {
         return res;
     }
 
-    public final Set<CardType> getTypes() {
-        Set<CardType> list = EnumSet.noneOf(CardType.class);
+    public final ArrayList<CardType> getTypes() {
+        ArrayList<CardType> list = new ArrayList<>();
         for (String type : this.types.split(SEPARATOR)) {
             try {
                 list.add(CardType.valueOf(type));
@@ -300,7 +299,7 @@ public class CardInfo {
         return list;
     }
 
-    public final void setTypes(Set<CardType> types) {
+    public final void setTypes(ArrayList<CardType> types) {
         StringBuilder sb = new StringBuilder();
         for (CardType item : types) {
             sb.append(item.name()).append(SEPARATOR);
diff --git a/Mage/src/main/java/mage/constants/CardType.java b/Mage/src/main/java/mage/constants/CardType.java
index e316543006..60a5378121 100644
--- a/Mage/src/main/java/mage/constants/CardType.java
+++ b/Mage/src/main/java/mage/constants/CardType.java
@@ -1,12 +1,11 @@
 package mage.constants;
 
+import java.util.ArrayList;
+import java.util.Arrays;
 import mage.MageObject;
 import mage.filter.predicate.Predicate;
 import mage.game.Game;
 
-import java.util.Arrays;
-import java.util.EnumSet;
-
 /**
  * @author North
  */
@@ -63,7 +62,7 @@ public enum CardType {
      * @return
      */
     public static CardType[] mergeTypes(CardType[] a, CardType[] b) {
-        EnumSet<CardType> cardTypes = EnumSet.noneOf(CardType.class);
+        ArrayList<CardType> cardTypes = new ArrayList<>();
         cardTypes.addAll(Arrays.asList(a));
         cardTypes.addAll(Arrays.asList(b));
         return cardTypes.toArray(new CardType[0]);
diff --git a/Mage/src/main/java/mage/designations/Designation.java b/Mage/src/main/java/mage/designations/Designation.java
index 4537abe3b0..e8e633c56f 100644
--- a/Mage/src/main/java/mage/designations/Designation.java
+++ b/Mage/src/main/java/mage/designations/Designation.java
@@ -1,5 +1,9 @@
 package mage.designations;
 
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.UUID;
 import mage.MageInt;
 import mage.MageObject;
 import mage.ObjectColor;
@@ -19,11 +23,6 @@ import mage.game.events.ZoneChangeEvent;
 import mage.util.GameLog;
 import mage.util.SubTypeList;
 
-import java.util.ArrayList;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.UUID;
-
 /**
  * @author LevelX2
  */
@@ -151,8 +150,8 @@ public abstract class Designation implements MageObject {
     }
 
     @Override
-    public EnumSet<CardType> getCardType() {
-        return emptySet;
+    public ArrayList<CardType> getCardType() {
+        return new ArrayList<>();
     }
 
     @Override
diff --git a/Mage/src/main/java/mage/game/command/Commander.java b/Mage/src/main/java/mage/game/command/Commander.java
index c2cd0491bf..8186c14ca5 100644
--- a/Mage/src/main/java/mage/game/command/Commander.java
+++ b/Mage/src/main/java/mage/game/command/Commander.java
@@ -1,5 +1,9 @@
 package mage.game.command;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
 import mage.MageInt;
 import mage.MageObject;
 import mage.ObjectColor;
@@ -20,10 +24,6 @@ import mage.game.events.ZoneChangeEvent;
 import mage.util.GameLog;
 import mage.util.SubTypeList;
 
-import java.util.List;
-import java.util.Set;
-import java.util.UUID;
-
 public class Commander implements CommandObject {
 
     private final Card sourceObject;
@@ -133,7 +133,7 @@ public class Commander implements CommandObject {
     }
 
     @Override
-    public Set<CardType> getCardType() {
+    public ArrayList<CardType> getCardType() {
         return sourceObject.getCardType();
     }
 
diff --git a/Mage/src/main/java/mage/game/command/Emblem.java b/Mage/src/main/java/mage/game/command/Emblem.java
index b0de304a98..b3396da0b4 100644
--- a/Mage/src/main/java/mage/game/command/Emblem.java
+++ b/Mage/src/main/java/mage/game/command/Emblem.java
@@ -1,5 +1,9 @@
 package mage.game.command;
 
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.UUID;
 import mage.MageInt;
 import mage.MageObject;
 import mage.ObjectColor;
@@ -22,16 +26,12 @@ import mage.game.events.ZoneChangeEvent;
 import mage.util.GameLog;
 import mage.util.SubTypeList;
 
-import java.util.EnumSet;
-import java.util.List;
-import java.util.UUID;
-
 /**
  * @author nantuko
  */
 public class Emblem implements CommandObject {
 
-    private static EnumSet<CardType> emptySet = EnumSet.noneOf(CardType.class);
+    private static ArrayList<CardType> emptySet = new ArrayList<>();
     private static ObjectColor emptyColor = new ObjectColor();
     private static ManaCosts emptyCost = new ManaCostsImpl();
 
@@ -148,7 +148,7 @@ public class Emblem implements CommandObject {
     }
 
     @Override
-    public EnumSet<CardType> getCardType() {
+    public ArrayList<CardType> getCardType() {
         return emptySet;
     }
 
diff --git a/Mage/src/main/java/mage/game/command/Plane.java b/Mage/src/main/java/mage/game/command/Plane.java
index be457ec6ed..764e1fa1a8 100644
--- a/Mage/src/main/java/mage/game/command/Plane.java
+++ b/Mage/src/main/java/mage/game/command/Plane.java
@@ -1,5 +1,10 @@
 package mage.game.command;
 
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.UUID;
 import mage.MageInt;
 import mage.MageObject;
 import mage.ObjectColor;
@@ -24,17 +29,12 @@ import mage.util.GameLog;
 import mage.util.RandomUtil;
 import mage.util.SubTypeList;
 
-import java.lang.reflect.Constructor;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.UUID;
-
 /**
  * @author spjspj
  */
 public class Plane implements CommandObject {
 
-    private static EnumSet<CardType> emptySet = EnumSet.noneOf(CardType.class);
+    private static ArrayList<CardType> emptySet = new ArrayList<>();
     private static ObjectColor emptyColor = new ObjectColor();
     private static ManaCosts emptyCost = new ManaCostsImpl();
 
@@ -157,7 +157,7 @@ public class Plane implements CommandObject {
     }
 
     @Override
-    public EnumSet<CardType> getCardType() {
+    public ArrayList<CardType> getCardType() {
         return emptySet;
     }
 
diff --git a/Mage/src/main/java/mage/game/permanent/token/TilonallisSummonerElementalToken.java b/Mage/src/main/java/mage/game/permanent/token/TilonallisSummonerElementalToken.java
index 50eeebc52a..57ec537789 100644
--- a/Mage/src/main/java/mage/game/permanent/token/TilonallisSummonerElementalToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/TilonallisSummonerElementalToken.java
@@ -24,6 +24,7 @@ public final class TilonallisSummonerElementalToken extends TokenImpl {
         super(token);
     }
 
+    @Override
     public TilonallisSummonerElementalToken copy() {
         return new TilonallisSummonerElementalToken(this);
     }
diff --git a/Mage/src/main/java/mage/game/stack/Spell.java b/Mage/src/main/java/mage/game/stack/Spell.java
index dd96e8421e..a9b5dd2b93 100644
--- a/Mage/src/main/java/mage/game/stack/Spell.java
+++ b/Mage/src/main/java/mage/game/stack/Spell.java
@@ -1,5 +1,6 @@
 package mage.game.stack;
 
+import java.util.*;
 import mage.MageInt;
 import mage.MageObject;
 import mage.Mana;
@@ -31,8 +32,6 @@ import mage.players.Player;
 import mage.util.GameLog;
 import mage.util.SubTypeList;
 
-import java.util.*;
-
 /**
  * @author BetaSteward_at_googlemail.com
  */
@@ -467,14 +466,14 @@ public class Spell extends StackObjImpl implements Card {
     }
 
     @Override
-    public Set<CardType> getCardType() {
+    public ArrayList<CardType> getCardType() {
         if (faceDown) {
-            EnumSet<CardType> cardTypes = EnumSet.noneOf(CardType.class);
+            ArrayList<CardType> cardTypes = new ArrayList<>();
             cardTypes.add(CardType.CREATURE);
             return cardTypes;
         }
         if (this.getSpellAbility() instanceof BestowAbility) {
-            EnumSet<CardType> cardTypes = EnumSet.noneOf(CardType.class);
+            ArrayList<CardType> cardTypes = new ArrayList<>();
             cardTypes.addAll(card.getCardType());
             cardTypes.remove(CardType.CREATURE);
             return cardTypes;
diff --git a/Mage/src/main/java/mage/game/stack/StackAbility.java b/Mage/src/main/java/mage/game/stack/StackAbility.java
index d7f64cac95..ba1d643f66 100644
--- a/Mage/src/main/java/mage/game/stack/StackAbility.java
+++ b/Mage/src/main/java/mage/game/stack/StackAbility.java
@@ -1,5 +1,9 @@
 package mage.game.stack;
 
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.UUID;
 import mage.MageInt;
 import mage.MageObject;
 import mage.ObjectColor;
@@ -30,17 +34,12 @@ import mage.util.GameLog;
 import mage.util.SubTypeList;
 import mage.watchers.Watcher;
 
-import java.util.ArrayList;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.UUID;
-
 /**
  * @author BetaSteward_at_googlemail.com
  */
 public class StackAbility extends StackObjImpl implements Ability {
 
-    private static EnumSet<CardType> emptyCardType = EnumSet.noneOf(CardType.class);
+    private static ArrayList<CardType> emptyCardType = new ArrayList<>();
     private static List<String> emptyString = new ArrayList<>();
     private static ObjectColor emptyColor = new ObjectColor();
     private static ManaCosts<ManaCost> emptyCost = new ManaCostsImpl<>();
@@ -153,7 +152,7 @@ public class StackAbility extends StackObjImpl implements Ability {
     }
 
     @Override
-    public EnumSet<CardType> getCardType() {
+    public ArrayList<CardType> getCardType() {
         return emptyCardType;
     }
 

From d9d7dd49bacc3a1709951dc8477791ebb921dac8 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Mon, 15 Jun 2020 23:36:17 +0400
Subject: [PATCH 284/586] Merge fix

---
 .../plugins/card/dl/sources/ScryfallImageSupportTokens.java   | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java
index 908dd265ff..4fc76ae7aa 100644
--- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java
@@ -110,7 +110,6 @@ public class ScryfallImageSupportTokens {
             put("AKH/Anointer Priest", "https://api.scryfall.com/cards/takh/2/en?format=image");
             put("AKH/Aven Initiate", "https://api.scryfall.com/cards/takh/3/en?format=image");
             put("AKH/Aven Wind Guide", "https://api.scryfall.com/cards/takh/4/en?format=image");
-            put("AKH/Gideon of the Trials Emblem", "https://api.scryfall.com/cards/takh/25/en?format=image");
             put("AKH/Glyph Keeper", "https://api.scryfall.com/cards/takh/5/en?format=image");
             put("AKH/Heart-Piercer Manticore", "https://api.scryfall.com/cards/takh/6/en?format=image");
             put("AKH/Honored Hydra", "https://api.scryfall.com/cards/takh/7/en?format=image");
@@ -204,9 +203,6 @@ public class ScryfallImageSupportTokens {
             put("BFZ/Octopus", "https://api.scryfall.com/cards/tbfz/7/en?format=image");
             put("BFZ/Plant", "https://api.scryfall.com/cards/tbfz/10/en?format=image");
 
-            // C17
-
-
             // WAR
             put("WAR/Angel", "https://api.scryfall.com/cards/twar/2/en?format=image");
             put("WAR/Assassin", "https://api.scryfall.com/cards/twar/6/en?format=image");

From 5411e446e184da88faccaf93638baa068230ed53 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Mon, 15 Jun 2020 22:44:17 +0200
Subject: [PATCH 285/586] * Grip of Chaos - Fixed a problem of handling the
 target not correctly (fixes #6344).

---
 Mage.Sets/src/mage/cards/g/GripOfChaos.java   |   3 +-
 .../test/cards/triggers/GripOfChaosTest.java  | 187 +++++++++---------
 2 files changed, 93 insertions(+), 97 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/g/GripOfChaos.java b/Mage.Sets/src/mage/cards/g/GripOfChaos.java
index 479a74548d..158782a7f8 100644
--- a/Mage.Sets/src/mage/cards/g/GripOfChaos.java
+++ b/Mage.Sets/src/mage/cards/g/GripOfChaos.java
@@ -1,4 +1,3 @@
-
 package mage.cards.g;
 
 import java.util.Iterator;
@@ -126,7 +125,7 @@ class GripOfChaosEffect extends OneShotEffect {
                 Mode mode = stackObject.getStackAbility().getModes().get(modeId);
                 for (Target target : mode.getTargets()) {
                     UUID oldTargetId = target.getFirstTarget();
-                    Set<UUID> possibleTargets = target.possibleTargets(stackObject.getId(), stackObject.getControllerId(), game);
+                    Set<UUID> possibleTargets = target.possibleTargets(stackObject.getSourceId(), stackObject.getControllerId(), game);
                     if (possibleTargets.contains(stackObject.getId())) { // The stackObject can't target itself
                         possibleTargets.remove(stackObject.getId());
                     }
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/GripOfChaosTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/GripOfChaosTest.java
index 6fb41fd6da..5ab1c0c851 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/GripOfChaosTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/GripOfChaosTest.java
@@ -1,95 +1,92 @@
-/*
- * To change this license header, choose License Headers in Project Properties.
- * To change this template file, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.mage.test.cards.triggers;
-
-import mage.constants.PhaseStep;
-import mage.constants.Zone;
-import org.junit.Assert;
-import org.junit.Test;
-import org.mage.test.serverside.base.CardTestPlayerBase;
-
-/**
- *
- * @author LevelX2
- */
-public class GripOfChaosTest extends CardTestPlayerBase {
-
-    /**
-     * From #6344
-     * I just had a game where we had an interaction between Grip of Chaos,
-     * Felidar Guardian, and Panharmonicon in which the cloned Felidar trigger
-     * fizzled with valid targets on field because Grip retargeted that trigger
-     * onto Felidar itself, which isn't a valid target. Grip of Chaos
-     * specifically states it only chooses from valid targets when retargeting,
-     * so this is a bug somewhere in that interaction, though whether it only
-     * happens with cloned triggers or if there's a bad interaction between Grip
-     * and Felidar itself isn't clear.
-     */
-    @Test
-    public void noValidTargetsTest() {
-        // Whenever a spell or ability is put onto the stack, if it has a single target, reselect its target at random.
-        addCard(Zone.BATTLEFIELD, playerB, "Grip of Chaos", 1); // Enchantment
-
-        // If an artifact or creature entering the battlefield causes a triggered ability 
-        // of a permanent you control to trigger, that ability triggers an additional time.
-        addCard(Zone.BATTLEFIELD, playerA, "Panharmonicon", 1); // Artifact
-
-        // When Felidar Guardian enters the battlefield, you may exile another target permanent you control, 
-        // then return that card to the battlefield under its owner's control.
-        addCard(Zone.HAND, playerA, "Felidar Guardian"); // Creature {3}{W}
-        addCard(Zone.BATTLEFIELD, playerA, "Plains", 1);
-        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
-        addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
-        addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
-
-        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Felidar Guardian");
-
-        setChoice(playerA, "When "); // Select order of Felidar trigger
-
-        setChoice(playerB, "Whenever "); // Select order of Grip of Chaos trigger
-
-        setChoice(playerA, "Yes"); // use for the original trigger of Felidar Guardian
-        setChoice(playerA, "Yes"); // use for the copied trigger of Felidar Guardian
-
-        addTarget(playerA, "Forest");
-        addTarget(playerA, "Mountain");
-
-        setStopAt(1, PhaseStep.BEGIN_COMBAT);
-
-        setStrictChooseMode(true);
-        execute();
-
-        assertPermanentCount(playerA, "Felidar Guardian", 1);
-        
-        int zcc = 0;
-        zcc += getPermanent("Mountain").getZoneChangeCounter(currentGame);
-        zcc += getPermanent("Forest").getZoneChangeCounter(currentGame);
-        zcc += getPermanent("Swamp").getZoneChangeCounter(currentGame);
-        zcc += getPermanent("Plains").getZoneChangeCounter(currentGame);
-        zcc += getPermanent("Panharmonicon").getZoneChangeCounter(currentGame);
-        if (zcc != 7) {
-          Assert.assertEquals("Sum of zone change counter should be 9", 9, zcc);
-          assertAllCommandsUsed(); // creates error if the random targets do select the same target twice zcc is 7 then the second trigger has an invalid target
-        }
-    }
-
-    /**
-     * Maybe also good situation to create an test for
-     * 9/20/2016 Panharmonicon
-     * 
-     * In some cases involving linked abilities, an ability requires
-     * information about “the exiled card.” When this happens, the ability gets
-     * multiple answers. If these answers are being used to determine the value
-     * of a variable, the sum is used. For example, if Elite Arcanist’s
-     * enters-the-battlefield ability triggers twice, two cards are exiled. The
-     * value of X in the activation cost of Elite Arcanist’s other ability is
-     * the sum of the two cards’ converted mana costs. As the ability resolves,
-     * you create copies of both cards and can cast none, one, or both of the
-     * copies in any order.
-     */
-    
-    
-}
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.mage.test.cards.triggers;
+
+import mage.constants.PhaseStep;
+import mage.constants.Zone;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestPlayerBase;
+
+/**
+ *
+ * @author LevelX2
+ */
+public class GripOfChaosTest extends CardTestPlayerBase {
+
+    /**
+     * From #6344 I just had a game where we had an interaction between Grip of
+     * Chaos, Felidar Guardian, and Panharmonicon in which the cloned Felidar
+     * trigger fizzled with valid targets on field because Grip retargeted that
+     * trigger onto Felidar itself, which isn't a valid target. Grip of Chaos
+     * specifically states it only chooses from valid targets when retargeting,
+     * so this is a bug somewhere in that interaction, though whether it only
+     * happens with cloned triggers or if there's a bad interaction between Grip
+     * and Felidar itself isn't clear.
+     */
+    @Test
+    public void noValidTargetsTest() {
+        // Whenever a spell or ability is put onto the stack, if it has a single target, reselect its target at random.
+        addCard(Zone.BATTLEFIELD, playerB, "Grip of Chaos", 1); // Enchantment
+
+        // If an artifact or creature entering the battlefield causes a triggered ability
+        // of a permanent you control to trigger, that ability triggers an additional time.
+        addCard(Zone.BATTLEFIELD, playerA, "Panharmonicon", 1); // Artifact
+
+        // When Felidar Guardian enters the battlefield, you may exile another target permanent you control,
+        // then return that card to the battlefield under its owner's control.
+        addCard(Zone.HAND, playerA, "Felidar Guardian"); // Creature {3}{W}
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Felidar Guardian");
+
+        setChoice(playerA, "When "); // Select order of Felidar trigger
+
+        setChoice(playerB, "Whenever "); // Select order of Grip of Chaos trigger
+
+        setChoice(playerA, "Yes"); // use for the original trigger of Felidar Guardian
+        setChoice(playerA, "Yes"); // use for the copied trigger of Felidar Guardian
+
+        addTarget(playerA, "Forest");
+        addTarget(playerA, "Mountain");
+
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+
+        setStrictChooseMode(true);
+        execute();
+
+        assertPermanentCount(playerA, "Felidar Guardian", 1);
+
+        int zcc = 0;
+        zcc += getPermanent("Mountain").getZoneChangeCounter(currentGame);
+        zcc += getPermanent("Forest").getZoneChangeCounter(currentGame);
+        zcc += getPermanent("Swamp").getZoneChangeCounter(currentGame);
+        zcc += getPermanent("Plains").getZoneChangeCounter(currentGame);
+        zcc += getPermanent("Panharmonicon").getZoneChangeCounter(currentGame);
+        // If both select the same permanent to exile, one spell fizzles so zcc == 7 otherwise 9
+        if (zcc != 7) {
+            Assert.assertEquals("Sum of zone change counter should be 9", 9, zcc);
+            assertAllCommandsUsed(); // creates error if the random targets do select the same target twice zcc is 7 then the second trigger has an invalid target
+        }
+    }
+
+    /**
+     * Maybe also good situation to create an test for 9/20/2016 Panharmonicon
+     *
+     * In some cases involving linked abilities, an ability requires information
+     * about “the exiled card.” When this happens, the ability gets multiple
+     * answers. If these answers are being used to determine the value of a
+     * variable, the sum is used. For example, if Elite Arcanist’s
+     * enters-the-battlefield ability triggers twice, two cards are exiled. The
+     * value of X in the activation cost of Elite Arcanist’s other ability is
+     * the sum of the two cards’ converted mana costs. As the ability resolves,
+     * you create copies of both cards and can cast none, one, or both of the
+     * copies in any order.
+     */
+}

From d8f90f919664d7932574fe0c644eaf0db1a87795 Mon Sep 17 00:00:00 2001
From: htrajan <htrajan2002@yahoo.com>
Date: Mon, 15 Jun 2020 15:56:01 -0700
Subject: [PATCH 286/586] Implement chandra cards (#6641)

* Implement Chandra, Heart of Fire

* Implement Chandra's Pyreling

* rename var
---
 Mage.Sets/src/mage/cards/a/ActOnImpulse.java  |  61 +-------
 .../src/mage/cards/c/ChandraHeartOfFire.java  | 135 ++++++++++++++++++
 .../src/mage/cards/c/ChandrasPyreling.java    |  86 +++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |   2 +
 .../ExileTop3MayPlayUntilEndOfTurnEffect.java |  56 ++++++++
 5 files changed, 284 insertions(+), 56 deletions(-)
 create mode 100644 Mage.Sets/src/mage/cards/c/ChandraHeartOfFire.java
 create mode 100644 Mage.Sets/src/mage/cards/c/ChandrasPyreling.java
 create mode 100644 Mage/src/main/java/mage/abilities/effects/common/ExileTop3MayPlayUntilEndOfTurnEffect.java

diff --git a/Mage.Sets/src/mage/cards/a/ActOnImpulse.java b/Mage.Sets/src/mage/cards/a/ActOnImpulse.java
index a2f44ab166..0f7dfa3ff8 100644
--- a/Mage.Sets/src/mage/cards/a/ActOnImpulse.java
+++ b/Mage.Sets/src/mage/cards/a/ActOnImpulse.java
@@ -1,20 +1,11 @@
 package mage.cards.a;
 
-import java.util.HashSet;
-import java.util.Set;
-import java.util.UUID;
-import mage.MageObject;
-import mage.abilities.Ability;
-import mage.abilities.effects.ContinuousEffect;
-import mage.abilities.effects.OneShotEffect;
-import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect;
-import mage.cards.Card;
+import mage.abilities.effects.common.ExileTop3MayPlayUntilEndOfTurnEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.*;
-import mage.game.Game;
-import mage.players.Player;
-import mage.target.targetpointer.FixedTargets;
+import mage.constants.CardType;
+
+import java.util.UUID;
 
 /**
  *
@@ -26,7 +17,7 @@ public final class ActOnImpulse extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}");
 
         // Exile the top three cards of your library. Until end of turn, you may play cards exiled this way.
-        this.getSpellAbility().addEffect(new ActOnImpulseExileEffect());
+        this.getSpellAbility().addEffect(new ExileTop3MayPlayUntilEndOfTurnEffect());
     }
 
     public ActOnImpulse(final ActOnImpulse card) {
@@ -39,45 +30,3 @@ public final class ActOnImpulse extends CardImpl {
     }
 }
 
-class ActOnImpulseExileEffect extends OneShotEffect {
-
-    public ActOnImpulseExileEffect() {
-        super(Outcome.Benefit);
-        this.staticText = "Exile the top three cards of your library. Until end of turn, you may play cards exiled this way";
-    }
-
-    public ActOnImpulseExileEffect(final ActOnImpulseExileEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public ActOnImpulseExileEffect copy() {
-        return new ActOnImpulseExileEffect(this);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        Player controller = game.getPlayer(source.getControllerId());
-        MageObject sourceObject = game.getObject(source.getSourceId());
-        if (controller != null && sourceObject != null) {
-            Set<Card> cards = new HashSet<>(controller.getLibrary().getTopCards(game, 3));
-            if (!cards.isEmpty()) {
-                controller.moveCardsToExile(cards, source, game, true, source.getSourceId(), sourceObject.getIdName());
-                // remove cards that could not be moved to exile
-                for (Card card : cards) {
-                    if (!Zone.EXILED.equals(game.getState().getZone(card.getId()))) {
-                        cards.remove(card);
-                    }
-                }
-                if (!cards.isEmpty()) {
-                    ContinuousEffect effect = new PlayFromNotOwnHandZoneTargetEffect(Zone.EXILED, Duration.EndOfTurn);
-                    effect.setTargetPointer(new FixedTargets(cards, game));
-                    game.addEffect(effect, source);
-                }
-            }
-            return true;
-        }
-        return false;
-    }
-
-}
diff --git a/Mage.Sets/src/mage/cards/c/ChandraHeartOfFire.java b/Mage.Sets/src/mage/cards/c/ChandraHeartOfFire.java
new file mode 100644
index 0000000000..dfd88af342
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/c/ChandraHeartOfFire.java
@@ -0,0 +1,135 @@
+package mage.cards.c;
+
+import mage.Mana;
+import mage.ObjectColor;
+import mage.abilities.Ability;
+import mage.abilities.LoyaltyAbility;
+import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
+import mage.abilities.effects.ContinuousEffect;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.DamageTargetEffect;
+import mage.abilities.effects.common.ExileTop3MayPlayUntilEndOfTurnEffect;
+import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect;
+import mage.abilities.effects.common.discard.DiscardHandControllerEffect;
+import mage.abilities.effects.mana.BasicManaEffect;
+import mage.cards.*;
+import mage.constants.*;
+import mage.filter.FilterCard;
+import mage.filter.predicate.Predicates;
+import mage.filter.predicate.mageobject.ColorPredicate;
+import mage.game.Game;
+import mage.players.Player;
+import mage.target.TargetCard;
+import mage.target.common.TargetAnyTarget;
+import mage.target.common.TargetCardInLibrary;
+import mage.target.targetpointer.FixedTargets;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+/**
+ *
+ * @author htrajan
+ */
+public final class ChandraHeartOfFire extends CardImpl {
+
+    public ChandraHeartOfFire(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{3}{R}{R}");
+        
+        this.addSuperType(SuperType.LEGENDARY);
+        this.subtype.add(SubType.CHANDRA);
+        this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5));
+
+        // +1: Discard your hand, then exile the top three cards of your library. Until end of turn, you may play cards exiled this way.
+        Ability ability = new LoyaltyAbility(new DiscardHandControllerEffect(), 1);
+        ability.addEffect(new ExileTop3MayPlayUntilEndOfTurnEffect() {{
+            setText(", then " + getText(null));
+        }});
+        this.addAbility(ability);
+
+        // +1: Chandra, Heart of Fire deals 2 damage to any target.
+        Ability damageAbility = new LoyaltyAbility(new DamageTargetEffect(2), 1);
+        damageAbility.addTarget(new TargetAnyTarget());
+        this.addAbility(damageAbility);
+
+        // −9: Search your graveyard and library for any number of red instant and/or sorcery spells, exile them, then shuffle your library. You may cast them this turn. Add six {R}.
+        Ability ultimateAbility = new LoyaltyAbility(new ChandraHeartOfFireUltimateEffect(), -9);
+        ultimateAbility.addEffect(new BasicManaEffect(Mana.RedMana(6)).setText("Add six {R}"));
+        this.addAbility(ultimateAbility);
+    }
+
+    private ChandraHeartOfFire(final ChandraHeartOfFire card) {
+        super(card);
+    }
+
+    @Override
+    public ChandraHeartOfFire copy() {
+        return new ChandraHeartOfFire(this);
+    }
+}
+
+class ChandraHeartOfFireUltimateEffect extends OneShotEffect {
+
+    private static final FilterCard filter = new FilterCard();
+
+    static {
+        filter.add(new ColorPredicate(ObjectColor.RED));
+        filter.add(Predicates.or(CardType.INSTANT.getPredicate(), CardType.SORCERY.getPredicate()));
+    }
+
+    ChandraHeartOfFireUltimateEffect() {
+        super(Outcome.Benefit);
+        staticText = "Search your graveyard and library for any number of red instant and/or sorcery spells, exile them, then shuffle your library. You may cast them this turn";
+    }
+
+    private ChandraHeartOfFireUltimateEffect(ChandraHeartOfFireUltimateEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public ChandraHeartOfFireUltimateEffect copy() {
+        return new ChandraHeartOfFireUltimateEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Player controller = game.getPlayer(source.getControllerId());
+        if (controller != null) {
+            Set<Card> exiledCards = new HashSet<>();
+
+            filter.setMessage("red instant or sorcery in your graveyard");
+            TargetCard target = new TargetCard(0, Integer.MAX_VALUE, Zone.GRAVEYARD, filter);
+            if (controller.choose(Outcome.Exile, controller.getGraveyard(), target, game)) {
+                List<UUID> targets = target.getTargets();
+                controller.moveCards(new CardsImpl(targets), Zone.EXILED, source, game);
+                exiledCards.addAll(targets.stream().map(game::getCard).collect(Collectors.toList()));
+            }
+
+            filter.setMessage("red instant or sorcery in your library");
+            Cards cardsInLibrary = new CardsImpl();
+            cardsInLibrary.addAll(controller.getLibrary().getCards(game));
+            target = new TargetCardInLibrary(0, Integer.MAX_VALUE, filter);
+            if (controller.choose(Outcome.Exile, cardsInLibrary, target, game)) {
+                List<UUID> targets = target.getTargets();
+                controller.moveCards(new CardsImpl(targets), Zone.EXILED, source, game);
+                exiledCards.addAll(targets.stream().map(game::getCard).collect(Collectors.toList()));
+            }
+            controller.shuffleLibrary(source, game);
+
+            exiledCards.removeIf(card -> !Zone.EXILED.equals(game.getState().getZone(card.getId())));
+
+            if (!exiledCards.isEmpty()) {
+                ContinuousEffect effect = new PlayFromNotOwnHandZoneTargetEffect(Zone.EXILED, Duration.EndOfTurn);
+                effect.setTargetPointer(new FixedTargets(exiledCards, game));
+                game.addEffect(effect, source);
+            }
+
+            return true;
+        }
+        return false;
+    }
+
+}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/c/ChandrasPyreling.java b/Mage.Sets/src/mage/cards/c/ChandrasPyreling.java
new file mode 100644
index 0000000000..55f8778bd8
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/c/ChandrasPyreling.java
@@ -0,0 +1,86 @@
+package mage.cards.c;
+
+import mage.MageInt;
+import mage.abilities.TriggeredAbilityImpl;
+import mage.abilities.effects.Effect;
+import mage.abilities.effects.common.continuous.BoostSourceEffect;
+import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
+import mage.abilities.keyword.DoubleStrikeAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+import mage.constants.Zone;
+import mage.game.Game;
+import mage.game.events.DamagedPlayerEvent;
+import mage.game.events.GameEvent;
+import mage.game.events.GameEvent.EventType;
+
+import java.util.UUID;
+
+/**
+ *
+ * @author htrajan
+ */
+public final class ChandrasPyreling extends CardImpl {
+
+    public ChandrasPyreling(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}");
+        
+        this.subtype.add(SubType.ELEMENTAL);
+        this.subtype.add(SubType.LIZARD);
+        this.power = new MageInt(1);
+        this.toughness = new MageInt(3);
+
+        // Whenever a source you control deals noncombat damage to an opponent, Chandra's Pyreling gets +1/+0 and gains double strike until end of turn.
+        this.addAbility(new ChandrasPyrelingAbility());
+    }
+
+    private ChandrasPyreling(final ChandrasPyreling card) {
+        super(card);
+    }
+
+    @Override
+    public ChandrasPyreling copy() {
+        return new ChandrasPyreling(this);
+    }
+}
+
+class ChandrasPyrelingAbility extends TriggeredAbilityImpl {
+
+    private static final Effect effect = new BoostSourceEffect(1, 0, Duration.EndOfTurn);
+
+    ChandrasPyrelingAbility() {
+        super(Zone.BATTLEFIELD, effect);
+        addEffect(new GainAbilitySourceEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfTurn));
+    }
+
+    private ChandrasPyrelingAbility(ChandrasPyrelingAbility ability) {
+        super(ability);
+        addEffect(new GainAbilitySourceEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfTurn));
+    }
+
+    @Override
+    public ChandrasPyrelingAbility copy() {
+        return new ChandrasPyrelingAbility(this);
+    }
+
+    @Override
+    public boolean checkEventType(GameEvent event, Game game) {
+        return event.getType() == EventType.DAMAGED_PLAYER;
+    }
+
+    @Override
+    public boolean checkTrigger(GameEvent event, Game game) {
+        DamagedPlayerEvent damageEvent = (DamagedPlayerEvent) event;
+        return !damageEvent.isCombatDamage()
+                && game.getOpponents(controllerId).contains(event.getTargetId())
+                && game.getControllerId(event.getSourceId()).equals(controllerId);
+    }
+
+    @Override
+    public String getRule() {
+        return "Whenever a source you control deals noncombat damage to an opponent, {source} gets +1/+0 and gains double strike until end of turn.";
+    }
+}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index e8345dcebc..945326709c 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -55,7 +55,9 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Chandra's Firemaw", 333, Rarity.RARE, mage.cards.c.ChandrasFiremaw.class));
         cards.add(new SetCardInfo("Chandra's Incinerator", 136, Rarity.RARE, mage.cards.c.ChandrasIncinerator.class));
         cards.add(new SetCardInfo("Chandra's Magmutt", 137, Rarity.COMMON, mage.cards.c.ChandrasMagmutt.class));
+        cards.add(new SetCardInfo("Chandra's Pyreling", 138, Rarity.UNCOMMON, mage.cards.c.ChandrasPyreling.class));
         cards.add(new SetCardInfo("Chandra, Flame's Catalyst", 332, Rarity.MYTHIC, mage.cards.c.ChandraFlamesCatalyst.class));
+        cards.add(new SetCardInfo("Chandra, Heart of Fire", 135, Rarity.MYTHIC, mage.cards.c.ChandraHeartOfFire.class));
         cards.add(new SetCardInfo("Chromatic Orrery", 228, Rarity.MYTHIC, mage.cards.c.ChromaticOrrery.class));
         cards.add(new SetCardInfo("Colossal Dreadmaw", 176, Rarity.COMMON, mage.cards.c.ColossalDreadmaw.class));
         cards.add(new SetCardInfo("Conclave Mentor", 216, Rarity.UNCOMMON, mage.cards.c.ConclaveMentor.class));
diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileTop3MayPlayUntilEndOfTurnEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileTop3MayPlayUntilEndOfTurnEffect.java
new file mode 100644
index 0000000000..3ad21482f6
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/effects/common/ExileTop3MayPlayUntilEndOfTurnEffect.java
@@ -0,0 +1,56 @@
+package mage.abilities.effects.common;
+
+import mage.MageObject;
+import mage.abilities.Ability;
+import mage.abilities.effects.ContinuousEffect;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect;
+import mage.cards.Card;
+import mage.constants.Duration;
+import mage.constants.Outcome;
+import mage.constants.Zone;
+import mage.game.Game;
+import mage.players.Player;
+import mage.target.targetpointer.FixedTargets;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class ExileTop3MayPlayUntilEndOfTurnEffect extends OneShotEffect {
+
+    public ExileTop3MayPlayUntilEndOfTurnEffect() {
+        super(Outcome.Benefit);
+        this.staticText = "exile the top three cards of your library. Until end of turn, you may play cards exiled this way";
+    }
+
+    public ExileTop3MayPlayUntilEndOfTurnEffect(final ExileTop3MayPlayUntilEndOfTurnEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public ExileTop3MayPlayUntilEndOfTurnEffect copy() {
+        return new ExileTop3MayPlayUntilEndOfTurnEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Player controller = game.getPlayer(source.getControllerId());
+        MageObject sourceObject = game.getObject(source.getSourceId());
+        if (controller != null && sourceObject != null) {
+            Set<Card> cards = new HashSet<>(controller.getLibrary().getTopCards(game, 3));
+            if (!cards.isEmpty()) {
+                controller.moveCardsToExile(cards, source, game, true, source.getSourceId(), sourceObject.getIdName());
+                // remove cards that could not be moved to exile
+                cards.removeIf(card -> !Zone.EXILED.equals(game.getState().getZone(card.getId())));
+                if (!cards.isEmpty()) {
+                    ContinuousEffect effect = new PlayFromNotOwnHandZoneTargetEffect(Zone.EXILED, Duration.EndOfTurn);
+                    effect.setTargetPointer(new FixedTargets(cards, game));
+                    game.addEffect(effect, source);
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+}

From b28434553cc374a6a3593365dbbb222603decbd2 Mon Sep 17 00:00:00 2001
From: jmharmon <37360760+jmharmon@users.noreply.github.com>
Date: Mon, 15 Jun 2020 15:56:49 -0700
Subject: [PATCH 287/586] Implement Brash Taunter (#6640)

* Implement Brash Taunter

* Implement Brash Taunter
---
 Mage.Sets/src/mage/cards/b/BrashTaunter.java | 100 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java     |   1 +
 2 files changed, 101 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/b/BrashTaunter.java

diff --git a/Mage.Sets/src/mage/cards/b/BrashTaunter.java b/Mage.Sets/src/mage/cards/b/BrashTaunter.java
new file mode 100644
index 0000000000..d3c35dfb42
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/b/BrashTaunter.java
@@ -0,0 +1,100 @@
+package mage.cards.b;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.DealtDamageToSourceTriggeredAbility;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.common.TapSourceCost;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.FightTargetSourceEffect;
+import mage.abilities.keyword.IndestructibleAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Outcome;
+import mage.constants.SubType;
+import mage.constants.Zone;
+import mage.filter.FilterPermanent;
+import mage.filter.common.FilterCreaturePermanent;
+import mage.filter.predicate.permanent.AnotherPredicate;
+import mage.game.Game;
+import mage.players.Player;
+import mage.target.TargetPermanent;
+import mage.target.common.TargetOpponent;
+
+import java.util.UUID;
+
+/**
+ * @author jmharmon
+ */
+
+public final class BrashTaunter extends CardImpl {
+
+    private static final FilterPermanent filter = new FilterCreaturePermanent();
+
+    static {
+        filter.add(AnotherPredicate.instance);
+    }
+
+    public BrashTaunter(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}");
+
+        this.subtype.add(SubType.GOBLIN);
+        this.power = new MageInt(1);
+        this.toughness = new MageInt(1);
+
+        // Indestructible
+        this.addAbility(IndestructibleAbility.getInstance());
+
+        // Whenever Brash Taunter is dealt damage, it deals that much damage to target opponent.
+        Ability ability = new DealtDamageToSourceTriggeredAbility(new BrashTaunterEffect(), false, false, true);
+        ability.addTarget(new TargetOpponent());
+        this.addAbility(ability);
+
+        // {2}{R}, {T}: Brash Taunter fights another target creature.
+        Ability ability1 = new SimpleActivatedAbility(Zone.BATTLEFIELD, new FightTargetSourceEffect(), new ManaCostsImpl("{2}{R}"));
+        ability1.addCost(new TapSourceCost());
+        ability1.addTarget(new TargetPermanent(filter));
+        this.addAbility(ability1);
+    }
+
+    private BrashTaunter(final BrashTaunter card) {
+        super(card);
+    }
+
+    @Override
+    public BrashTaunter copy() {
+        return new BrashTaunter(this);
+    }
+}
+
+class BrashTaunterEffect extends OneShotEffect {
+
+    public BrashTaunterEffect() {
+        super(Outcome.Damage);
+        this.staticText = "it deals that much damage to target opponent";
+    }
+
+    public BrashTaunterEffect(final BrashTaunterEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public BrashTaunterEffect copy() {
+        return new BrashTaunterEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        int amount = (Integer) getValue("damage");
+        if (amount > 0) {
+            Player player = game.getPlayer(targetPointer.getFirst(game, source));
+            if (player != null) {
+                player.damage(amount, source.getSourceId(), game);
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 945326709c..570b952fc2 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -49,6 +49,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Basri, Devoted Paladin", 320, Rarity.MYTHIC, mage.cards.b.BasriDevotedPaladin.class));
         cards.add(new SetCardInfo("Battle-Rattle Shaman", 130, Rarity.UNCOMMON, mage.cards.b.BattleRattleShaman.class));
         cards.add(new SetCardInfo("Bolt Hound", 131, Rarity.UNCOMMON, mage.cards.b.BoltHound.class));
+        cards.add(new SetCardInfo("Brash Taunter", 133, Rarity.RARE, mage.cards.b.BrashTaunter.class));
         cards.add(new SetCardInfo("Burlfist Oak", 174, Rarity.UNCOMMON, mage.cards.b.BurlfistOak.class));
         cards.add(new SetCardInfo("Carrion Grub", 92, Rarity.UNCOMMON, mage.cards.c.CarrionGrub.class));
         cards.add(new SetCardInfo("Celestial Enforcer", 11, Rarity.COMMON, mage.cards.c.CelestialEnforcer.class));

From 69a9cd1901f6c6b4916710751cac22043bcbce79 Mon Sep 17 00:00:00 2001
From: SpeedProg <SpeedProg@users.noreply.github.com>
Date: Tue, 16 Jun 2020 00:58:26 +0200
Subject: [PATCH 288/586] implemented Feline Sovereign (#6639)

---
 .../src/mage/cards/f/FelineSovereign.java     | 131 ++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |   2 +
 2 files changed, 133 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/f/FelineSovereign.java

diff --git a/Mage.Sets/src/mage/cards/f/FelineSovereign.java b/Mage.Sets/src/mage/cards/f/FelineSovereign.java
new file mode 100644
index 0000000000..113cf3bb74
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/f/FelineSovereign.java
@@ -0,0 +1,131 @@
+
+package mage.cards.f;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.TriggeredAbilityImpl;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.effects.Effect;
+import mage.abilities.effects.common.DestroyTargetEffect;
+import mage.abilities.effects.common.continuous.BoostControlledEffect;
+import mage.abilities.effects.common.continuous.GainAbilityAllEffect;
+import mage.abilities.keyword.ProtectionAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+import mage.constants.TargetController;
+import mage.constants.Zone;
+import mage.filter.FilterCard;
+import mage.filter.common.FilterArtifactOrEnchantmentPermanent;
+import mage.filter.common.FilterCreaturePermanent;
+import mage.filter.predicate.permanent.ControllerIdPredicate;
+import mage.game.Game;
+import mage.game.events.DamagedPlayerEvent;
+import mage.game.events.GameEvent;
+import mage.game.events.GameEvent.EventType;
+import mage.game.permanent.Permanent;
+import mage.target.TargetPermanent;
+
+public final class FelineSovereign extends CardImpl {
+
+    private static final FilterCreaturePermanent filterCat = new FilterCreaturePermanent("Cats");
+    static {
+        filterCat.add(SubType.CAT.getPredicate());
+        filterCat.add(TargetController.YOU.getControllerPredicate());
+    }
+
+    private static final FilterCard filterProtectionFromDogs = new FilterCard("Dogs");
+    static {
+        filterProtectionFromDogs.add(SubType.DOG.getPredicate());
+    }
+
+    public FelineSovereign(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{G}");
+        this.subtype.add(SubType.CAT);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(3);
+
+        // Other cats get +1/+1
+        Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield, filterCat, true));
+        // Cats you control have protection from Dogs
+        Effect effect = new GainAbilityAllEffect(new ProtectionAbility(filterProtectionFromDogs), Duration.WhileOnBattlefield, filterCat);
+        effect.setText("and have protection from Dogs");
+        ability.addEffect(effect);
+        this.addAbility(ability);
+        // if a cat deals combat damage to a player you may destroy up to one artifact/enchantment
+        this.addAbility(new FelineSovereignTriggeredAbility());
+    }
+
+    public FelineSovereign(final FelineSovereign card) {
+        super(card);
+    }
+
+    @Override
+    public FelineSovereign copy() {
+        return new FelineSovereign(this);
+    }
+}
+
+class FelineSovereignTriggeredAbility extends TriggeredAbilityImpl {
+
+    private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Cat you control");
+    static {
+        filter.add(TargetController.YOU.getControllerPredicate());
+        filter.add(SubType.CAT.getPredicate());
+    }
+    
+    private Set<UUID> damagedPlayerIds = new HashSet<>();
+
+    public FelineSovereignTriggeredAbility() {
+        super(Zone.BATTLEFIELD, new DestroyTargetEffect(), true);
+    }
+
+    public FelineSovereignTriggeredAbility(final FelineSovereignTriggeredAbility ability) {
+        super(ability);
+    }
+
+    @Override
+    public FelineSovereignTriggeredAbility copy() {
+        return new FelineSovereignTriggeredAbility(this);
+    }
+
+    @Override
+    public boolean checkEventType(GameEvent event, Game game) {
+        return event.getType() == EventType.DAMAGED_PLAYER
+                || event.getType() == EventType.COMBAT_DAMAGE_STEP_PRIORITY
+                || event.getType() == EventType.ZONE_CHANGE;
+    }
+
+    @Override
+    public boolean checkTrigger(GameEvent event, Game game) {
+        if (event.getType() == EventType.DAMAGED_PLAYER) {
+            DamagedPlayerEvent damageEvent = (DamagedPlayerEvent) event;
+            Permanent p = game.getPermanent(event.getSourceId());
+            if (damageEvent.isCombatDamage() && p != null && p.isControlledBy(this.getControllerId()) &&
+                    filter.match(p, getSourceId(), getControllerId(), game) &&
+                    !damagedPlayerIds.contains(event.getPlayerId())) {
+                damagedPlayerIds.add(event.getPlayerId());
+                this.getTargets().clear();
+                FilterArtifactOrEnchantmentPermanent filter = new FilterArtifactOrEnchantmentPermanent();
+                filter.add(new ControllerIdPredicate(event.getPlayerId()));
+                this.addTarget(new TargetPermanent(0, 1, filter, false)); 
+                return true;
+            }
+        }
+        if (event.getType() == EventType.COMBAT_DAMAGE_STEP_PRIORITY ||
+                (event.getType() == EventType.ZONE_CHANGE && event.getTargetId().equals(getSourceId()))) {
+            damagedPlayerIds.clear();
+        }
+        return false;
+    }
+
+    @Override
+    public String getRule() {
+        return "Whenever one or more Cats you control deal combat damage to a player, destroy up to one target artifact or enchantment that player controls";
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 570b952fc2..6c2628f7ad 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -75,6 +75,8 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Fabled Passage", 246, Rarity.RARE, mage.cards.f.FabledPassage.class));
         cards.add(new SetCardInfo("Faith's Fetters", 17, Rarity.UNCOMMON, mage.cards.f.FaithsFetters.class));
         cards.add(new SetCardInfo("Falconer Adept", 18, Rarity.UNCOMMON, mage.cards.f.FalconerAdept.class));
+        cards.add(new SetCardInfo("Feline Sovereign", 180, Rarity.RARE, mage.cards.f.FelineSovereign.class, ExpansionSet.NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Feline Sovereign", 374, Rarity.RARE, mage.cards.f.FelineSovereign.class, ExpansionSet.NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Fierce Empath", 181, Rarity.UNCOMMON, mage.cards.f.FierceEmpath.class));
         cards.add(new SetCardInfo("Fiery Emancipation", 143, Rarity.MYTHIC, mage.cards.f.FieryEmancipation.class));
         cards.add(new SetCardInfo("Finishing Blow", 99, Rarity.COMMON, mage.cards.f.FinishingBlow.class));

From eee2fde3a4290edaa7f275ccd89d13bc2718b9f5 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 15 Jun 2020 18:59:45 -0400
Subject: [PATCH 289/586] updated M21 spoiler and reprints

---
 Mage.Sets/src/mage/sets/CoreSet2021.java | 15 +++++++++------
 Utils/mtg-cards-data.txt                 | 24 ++++++++++++++++++++----
 2 files changed, 29 insertions(+), 10 deletions(-)

diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 6c2628f7ad..3468dc4fe0 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -75,11 +75,11 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Fabled Passage", 246, Rarity.RARE, mage.cards.f.FabledPassage.class));
         cards.add(new SetCardInfo("Faith's Fetters", 17, Rarity.UNCOMMON, mage.cards.f.FaithsFetters.class));
         cards.add(new SetCardInfo("Falconer Adept", 18, Rarity.UNCOMMON, mage.cards.f.FalconerAdept.class));
-        cards.add(new SetCardInfo("Feline Sovereign", 180, Rarity.RARE, mage.cards.f.FelineSovereign.class, ExpansionSet.NON_FULL_USE_VARIOUS));
-        cards.add(new SetCardInfo("Feline Sovereign", 374, Rarity.RARE, mage.cards.f.FelineSovereign.class, ExpansionSet.NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Feline Sovereign", 180, Rarity.RARE, mage.cards.f.FelineSovereign.class));
         cards.add(new SetCardInfo("Fierce Empath", 181, Rarity.UNCOMMON, mage.cards.f.FierceEmpath.class));
         cards.add(new SetCardInfo("Fiery Emancipation", 143, Rarity.MYTHIC, mage.cards.f.FieryEmancipation.class));
         cards.add(new SetCardInfo("Finishing Blow", 99, Rarity.COMMON, mage.cards.f.FinishingBlow.class));
+        cards.add(new SetCardInfo("Forest", 272, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Frantic Inventory", 50, Rarity.COMMON, mage.cards.f.FranticInventory.class));
         cards.add(new SetCardInfo("Fungal Rebirth", 182, Rarity.UNCOMMON, mage.cards.f.FungalRebirth.class));
         cards.add(new SetCardInfo("Furious Rise", 144, Rarity.UNCOMMON, mage.cards.f.FuriousRise.class));
@@ -87,6 +87,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Garruk's Gorehorn", 184, Rarity.COMMON, mage.cards.g.GarruksGorehorn.class));
         cards.add(new SetCardInfo("Garruk's Warsteed", 337, Rarity.RARE, mage.cards.g.GarruksWarsteed.class));
         cards.add(new SetCardInfo("Garruk, Savage Herald", 336, Rarity.MYTHIC, mage.cards.g.GarrukSavageHerald.class));
+        cards.add(new SetCardInfo("Glorious Anthem", 21, Rarity.RARE, mage.cards.g.GloriousAnthem.class));
         cards.add(new SetCardInfo("Goblin Arsonist", 147, Rarity.COMMON, mage.cards.g.GoblinArsonist.class));
         cards.add(new SetCardInfo("Goremand", 101, Rarity.UNCOMMON, mage.cards.g.Goremand.class));
         cards.add(new SetCardInfo("Grasp of Darkness", 102, Rarity.COMMON, mage.cards.g.GraspOfDarkness.class));
@@ -100,11 +101,12 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Igneous Cur", 153, Rarity.COMMON, mage.cards.i.IgneousCur.class));
         cards.add(new SetCardInfo("Indulging Patrician", 219, Rarity.UNCOMMON, mage.cards.i.IndulgingPatrician.class));
         cards.add(new SetCardInfo("Invigorating Surge", 190, Rarity.UNCOMMON, mage.cards.i.InvigoratingSurge.class));
-        cards.add(new SetCardInfo("Island", 310, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Island", 263, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Jeskai Elder", 53, Rarity.UNCOMMON, mage.cards.j.JeskaiElder.class));
         cards.add(new SetCardInfo("Jolrael, Mwonvuli Recluse", 191, Rarity.RARE, mage.cards.j.JolraelMwonvuliRecluse.class));
         cards.add(new SetCardInfo("Kaervek, the Spiteful", 106, Rarity.RARE, mage.cards.k.KaervekTheSpiteful.class));
         cards.add(new SetCardInfo("Keral Keep Disciples", 334, Rarity.UNCOMMON, mage.cards.k.KeralKeepDisciples.class));
+        cards.add(new SetCardInfo("Kitesail Freebooter", 107, Rarity.UNCOMMON, mage.cards.k.KitesailFreebooter.class));
         cards.add(new SetCardInfo("Library Larcenist", 55, Rarity.COMMON, mage.cards.l.LibraryLarcenist.class));
         cards.add(new SetCardInfo("Light of Promise", 25, Rarity.UNCOMMON, mage.cards.l.LightOfPromise.class));
         cards.add(new SetCardInfo("Liliana's Devotee", 109, Rarity.UNCOMMON, mage.cards.l.LilianasDevotee.class));
@@ -123,7 +125,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Mazemind Tome", 232, Rarity.RARE, mage.cards.m.MazemindTome.class));
         cards.add(new SetCardInfo("Meteorite", 233, Rarity.UNCOMMON, mage.cards.m.Meteorite.class));
         cards.add(new SetCardInfo("Miscast", 57, Rarity.UNCOMMON, mage.cards.m.Miscast.class));
-        cards.add(new SetCardInfo("Mountain", 312, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Mountain", 269, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Mystic Skyfish", 326, Rarity.COMMON, mage.cards.m.MysticSkyfish.class));
         cards.add(new SetCardInfo("Necromentia", 116, Rarity.RARE, mage.cards.n.Necromentia.class));
         cards.add(new SetCardInfo("Obsessive Stitcher", 223, Rarity.UNCOMMON, mage.cards.o.ObsessiveStitcher.class));
@@ -131,7 +133,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Palladium Myr", 234, Rarity.UNCOMMON, mage.cards.p.PalladiumMyr.class));
         cards.add(new SetCardInfo("Peer into the Abyss", 117, Rarity.RARE, mage.cards.p.PeerIntoTheAbyss.class));
         cards.add(new SetCardInfo("Pestilent Haze", 118, Rarity.UNCOMMON, mage.cards.p.PestilentHaze.class));
-        cards.add(new SetCardInfo("Plains", 309, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Plains", 260, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Predatory Wurm", 338, Rarity.UNCOMMON, mage.cards.p.PredatoryWurm.class));
         cards.add(new SetCardInfo("Pridemalkin", 196, Rarity.COMMON, mage.cards.p.Pridemalkin.class));
         cards.add(new SetCardInfo("Primal Might", 197, Rarity.RARE, mage.cards.p.PrimalMight.class));
@@ -166,7 +168,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Storm Caller", 335, Rarity.COMMON, mage.cards.s.StormCaller.class));
         cards.add(new SetCardInfo("Stormwing Entity", 73, Rarity.RARE, mage.cards.s.StormwingEntity.class));
         cards.add(new SetCardInfo("Sublime Epiphany", 74, Rarity.RARE, mage.cards.s.SublimeEpiphany.class));
-        cards.add(new SetCardInfo("Swamp", 311, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Swamp", 266, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Teferi's Ageless Insight", 76, Rarity.RARE, mage.cards.t.TeferisAgelessInsight.class));
         cards.add(new SetCardInfo("Teferi's Protege", 77, Rarity.COMMON, mage.cards.t.TeferisProtege.class));
         cards.add(new SetCardInfo("Teferi's Tutelage", 78, Rarity.UNCOMMON, mage.cards.t.TeferisTutelage.class));
@@ -189,6 +191,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Valorous Steed", 42, Rarity.COMMON, mage.cards.v.ValorousSteed.class));
         cards.add(new SetCardInfo("Village Rites", 126, Rarity.COMMON, mage.cards.v.VillageRites.class));
         cards.add(new SetCardInfo("Vito, Thorn of the Dusk Rose", 127, Rarity.RARE, mage.cards.v.VitoThornOfTheDuskRose.class));
+        cards.add(new SetCardInfo("Volcanic Geyser", 171, Rarity.UNCOMMON, mage.cards.v.VolcanicGeyser.class));
         cards.add(new SetCardInfo("Volcanic Salvo", 172, Rarity.RARE, mage.cards.v.VolcanicSalvo.class));
         cards.add(new SetCardInfo("Vryn Wingmare", 43, Rarity.UNCOMMON, mage.cards.v.VrynWingmare.class));
         cards.add(new SetCardInfo("Waker of Waves", 84, Rarity.UNCOMMON, mage.cards.w.WakerOfWaves.class));
diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt
index b422f3ae6a..b5d430a083 100644
--- a/Utils/mtg-cards-data.txt
+++ b/Utils/mtg-cards-data.txt
@@ -37450,14 +37450,17 @@ Celestial Enforcer|Core Set 2021|11|C|{2}{W}|Creature - Human Cleric|2|3|{1}{W},
 Defiant Strike|Core Set 2021|15|C|{W}|Instant|||Target creature gets +1/+0 until end of turn.$Draw a card.|
 Faith's Fetters|Core Set 2021|17|U|{3}{W}|Enchantment - Aura|||Enchant permanent$When Faith's Fetters enters the battlefield, you gain 4 life.$Enchanted permanent can't attack or block, and its activated abilities can't be activated unless they're mana abilities.|
 Falconer Adept|Core Set 2021|18|U|{3}{W}|Creature - Human Soldier|2|3|Whenever Falconer Adept attacks, create a 1/1 white Bird creature token with flying that's tapped and attacking.|
+Glorious Anthem|Core Set 2021|21|R|{1}{W}{W}|Enchantment|||Creatures you control get +1/+1.|
 Griffin Aerie|Core Set 2021|22|U|{1}{W}|Enchantment|||At the beginning of your end step, if you gained 3 or more life this turn, create a 2/2 white Griffin creature token with flying.|
 Idol of Endurance|Core Set 2021|23|R|{2}{W}|Artifact|||When Idol of Endurance enters the battlefield, exile all creature cards with converted mana cost 3 or less from your graveyard until Idol of Endurance leaves the battlefield.${1}{W}, {T}: Until end of turn, you may cast a creature spell from among the cards exiled with Idol of Endurance without paying its mana cost.|
 Light of Promise|Core Set 2021|25|U|{2}{W}|Enchantment - Aura|||Enchant creature$Enchanted creature has "Whenever you gain life, put that many +1/+1 counters on this creature."|
 Mangara, the Diplomat|Core Set 2021|27|M|{3}{W}|Legendary Creature - Human Cleric|2|4|Lifelink$Whenever an opponent attacks with creatures, if two or more of those creatures are attacking you and/or a planeswalker you control, draw a card.$Whenever an opponent casts their second spell each turn, draw a card.|
+Nine Lives|Core Set 2021|28|R|{1}{W}{W}|Enchantment|||Hexproof$If a source would deal damage to you, prevent that damage and put an incarnation counter on Nine Lives.$When there are nine or more incarnation counters on Nine Lives, exile it.$When Nine Lives leaves the battlefield, you lose the game.|
 Pack Leader|Core Set 2021|29|R|{1}{W}|Creature - Dog|2|2|Other Dogs you control get +1/+1.$Whenever Pack Leader attacks, prevent all combat damage that would be dealt this turn to Dogs you control.|
 Rambunctious Mutt|Core Set 2021|30|C|{3}{W}{W}|Creature - Dog|3|4|When Rambunctious Mutt enters the battlefield, destroy target artifact or enchantment an opponent controls.|
 Revitalize|Core Set 2021|31|C|{1}{W}|Instant|||You gain 3 life.$Draw a card.|
 Runed Halo|Core Set 2021|32|R|{W}{W}|Enchantment|||As Runed Halo enters the battlefield, choose a card name.$You have protection from the chosen name.|
+Sanctum of Tranquil Light|Core Set 2021|33|U|{W}|Legendary Enchantment - Shrine|||{5}{W}: Tap target creature. This ability costs {1} less to activate for each Shrine you control.|
 Seasoned Hallowblade|Core Set 2021|34|U|{1}{W}|Creature - Human Warrior|3|1|Discard a card: Tap Seasoned Hallowblade. It gains indestructible until end of turn.|
 Selfless Savior|Core Set 2021|36|U|{W}|Creature - Dog|1|1|Sacrifice Selfless Savior: Another target creature you control gains indestructible until end of turn.|
 Siege Striker|Core Set 2021|37|U|{2}{W}|Creature - Human Soldier|1|1|Double strike$Whenever Siege Striker attacks, you may tap any number of untapped creatures you control. Siege Striker gets +1/+1 until end of turn for each creature tapped this way.|
@@ -37465,19 +37468,24 @@ Speaker of the Heavens|Core Set 2021|38|R|{W}|Creature - Human Cleric|1|1|Vigila
 Tempered Veteran|Core Set 2021|41|U|{1}{W}|Creature - Human Knight|1|2|{W}, {T}: Put a +1/+1 counter on target creature with a +1/+1 counter on it.${4}{W}{W}, {T}: Put a +1/+1 counter on target creature.|
 Valorous Steed|Core Set 2021|42|C|{4}{W}|Creature - Unicorn|3|3|Vigilance$When Valorous Steed enters the battlefield, create a 2/2 white Knight creature token with vigilance.|
 Vryn Wingmare|Core Set 2021|43|U|{2}{W}|Creature - Pegasus|2|1|Flying$Noncreature spells cost {1} more to cast.|
+Barrin, Tolarian Archmage|Core Set 2021|45|R|{1}{U}{U}|Legendary Creature - Human Wizard|2|2|When Barrin, Tolarian Archmage enters the battlefield, return up to one other target creature or planeswalker to its owner's hand.$At the beginning of your end step, if a permanent was put into your hand from the battlefield this turn, draw a card.|
 Discontinuity|Core Set 2021|48|M|{3}{U}{U}{U}|Instant|||As long as it's your turn, this spell costs {2}{U}{U} less to cast.$End the turn.|
 Enthralling Hold|Core Set 2021|49|U|{3}{U}{U}|Enchantment - Aura|||Enchant creature$You can't choose an untapped creature as this spell's target as you cast it.$You control enchanted creature.|
 Frantic Inventory|Core Set 2021|50|C|{1}{U}|Instant|||Draw a card, then draw cards equal to the number of cards named Frantic Inventory in your graveyard.|
+Ghostly Pilferer|Core Set 2021|52|R|{1}{U}|Creature - Spirit Rogue|2|1|Whenever Ghostly Pilferer becomes tapped, you may pay {2}. If you do, draw a card.$Whenever an opponent casts a spell from anywhere other than their hand, draw a card.$Discard a card: Ghostly Pilferer can't be blocked this turn.|
 Jeskai Elder|Core Set 2021|53|U|{1}{U}|Creature - Human Monk|1|2|Prowess$Whenever Jeskai Elder deals combat damage to a player, you may draw a card. If you do, discard a card.|
 Library Larcenist|Core Set 2021|55|C|{2}{U}|Creature - Merfolk Rogue|1|2|Whenever Library Larcenist attacks, draw a card.|
 Lofty Denial|Core Set 2021|56|C|{1}{U}|Instant|||Counter target spell unless its controller pays {1}. If you control a creature with flying, counter that spell unless its controller pays {4} instead.|
 Miscast|Core Set 2021|57|U|{U}|Instant|||Counter target instant or sorcery spell unless its controller pays {3}.|
+Mistral Singer|Core Set 2021|58|C|{2}{U}|Creature - Siren|2|2|Flying$Prowess|
 Pursued Whale|Core Set 2021|60|R|{5}{U}{U}|Creature - Whale|8|8|When Pursued Whale enters the battlefield, each opponent creates a 1/1 red Pirate creature token with "This creature can't block" and "Creatures you control attack each combat if able."$Spells your opponents cast that target Pursued Whale cost {3} more to cast.|
 Rain of Revelation|Core Set 2021|61|U|{3}{U}|Instant|||Draw three cards, then discard a card.|
 Rewind|Core Set 2021|63|U|{2}{U}{U}|Instant|||Counter target spell. Untap up to four lands.|
 Riddleform|Core Set 2021|64|U|{1}{U}|Enchantment|||Whenever you cast a noncreature spell, you may have Riddleform become a 3/3 Sphinx creature with flying in addition to its other types until end of turn.${2}{U}: Scry 1.|
 Rousing Read|Core Set 2021|67|C|{2}{U}|Enchantment - Aura|||Enchant creature$When Rousing Read enters the battlefield, draw two cards, then discard a card.$Enchanted creature gets +1/+1 and has flying.|
+Sanctum of Calm Waters|Core Set 2021|68|U|{3}{U}|Legendary Enchantment - Shrine|||At the beginning of your precombat main phase, you may draw X cards, where X is the number of Shrines you control. If you do, discard a card.|
 See the Truth|Core Set 2021|69|R|{1}{U}|Sorcery|||Look at the top three cards of your library. Put one of those cards into your hand and the rest on the bottom of your library in any order. If this spell was cast from anywhere other than your hand, put each of those cards into your hand instead.|
+Shacklegeist|Core Set 2021|70|R|{1}{U}|Creature - Spirit|2|2|Flying$Shacklegeist can block only creatures with flying.$Tap two untapped Spirits you control: Tap target creature you don't control.|
 Shipwreck Dowser|Core Set 2021|71|U|{3}{U}{U}|Creature - Merfolk Wizard|3|3|Prowess$When Shipwreck Dowser enters the battlefield, return target instant or sorcery card from your graveyard to your hand.|
 Spined Megalodon|Core Set 2021|72|C|{5}{U}{U}|Creature - Shark|5|7|Hexproof$Whenever Spined Megalodon attacks, scry 1.|
 Stormwing Entity|Core Set 2021|73|R|{3}{U}{U}|Creature - Elemental|3|3|This spell costs {2}{U} less to cast if you've cast an instant or sorcery spell this turn.$Flying$Prowess$When Stormwing Entity enters the battlefield, scry 2.|
@@ -37501,6 +37509,7 @@ Grasp of Darkness|Core Set 2021|102|C|{B}{B}|Instant|||Target creature gets -4/-
 Grim Tutor|Core Set 2021|103|M|{1}{B}{B}|Sorcery|||Search your library for a card and put that card into your hand, then shuffle your library. You lose 3 life.|
 Hooded Blightfang|Core Set 2021|104|R|{2}{B}|Creature - Snake|1|4|Deathtouch$Whenever a creature you control with deathtouch attacks, each opponent loses 1 life and you gain 1 life.$Whenever a creature you control with deathtouch deals damage to a planeswalker, destroy that planeswalker.|
 Kaervek, the Spiteful|Core Set 2021|106|R|{2}{B}{B}|Legendary Creature - Human Warlock|3|2|Other creatures get -1/-1.|
+Kitesail Freebooter|Core Set 2021|107|U|{1}{B}|Creature - Human Pirate|1|2|Flying$When Kitesail Freebooter enters the battlefield, target opponent reveals their hand. You choose a noncreature, nonland card from it. Exile that card until Kitesail Freebooter leaves the battlefield.|
 Liliana, Waker of the Dead|Core Set 2021|108|M|{2}{B}{B}|Legendary Planeswalker - Liliana|4|+1: Each player discards a card. Each opponent who can't loses 3 life.$−3: Target creature gets -X/-X until end of turn, where X is the number of cards in your graveyard.$−7: You get an emblem with "At the beginning of combat on your turn, put target creature card from a graveyard onto the battlefield under your control. It gains haste."|
 Liliana's Devotee|Core Set 2021|109|U|{2}{B}|Creature - Human Warlock|2|3|Zombies you control get +1/+0.$At the beginning of your end step, if a creature died this turn, you may pay {1}{B}. If you do, create a 2/2 black Zombie creature token.|
 Liliana's Standard Bearer|Core Set 2021|110|R|{2}{B}|Creature - Zombie Knight|3|1|Flash$When Liliana's Standard Bearer enters the battlefield, draw X cards, where X is the number of creatures that died under your control this turn.|
@@ -37510,6 +37519,7 @@ Massacre Wurm|Core Set 2021|114|M|{3}{B}{B}{B}|Creature - Wurm|6|5|When Massacre
 Necromentia|Core Set 2021|116|R|{1}{B}{B}|Sorcery|||Choose a card name other than a basic land card name. Search target opponent's graveyard, hand, and library for any number of cards with that name and exile them. That player shuffles their library, then creates a 2/2 black Zombie creature token for each card exiled from their hand this way.|
 Peer into the Abyss|Core Set 2021|117|R|{4}{B}{B}{B}|Sorcery|||Target player draws cards equal to half the number of cards in their library and loses half their life. Round up each time.|
 Pestilent Haze|Core Set 2021|118|U|{1}{B}{B}|Sorcery|||Choose one —$• All creatures get -2/-2 until end of turn.$• Remove two loyalty counters from each planeswalker.|
+Sanctum of Stone Fangs|Core Set 2021|120|U|{1}{B}|Legendary Enchantment - Shrine|||At the beginning of your precombat main phase, each opponent loses X life and you gain X life, where X is the number of Shrines you control.|
 Silversmote Ghoul|Core Set 2021|122|U|{2}{B}|Creature - Zombie Vampire|3|1|At the beginning of your end step, if you gained 3 or more life this turn, return Silversmote Ghoul from your graveyard to the battlefield tapped.${1}{B}, Sacrifice Silversmote Ghoul: Draw a card.|
 Thieves' Guild Enforcer|Core Set 2021|125|R|{B}|Creature - Human Rogue|1|1|Flash$Whenever Thieves' Guild Enforcer or another Rogue enters the battlefield under your control, each opponent mills two cards.$As long as an opponent has eight or more cards in their graveyard, Thieves' Guild Enforcer gets +2/+1 and has deathtouch.|
 Village Rites|Core Set 2021|126|C|{B}|Instant|||As an additional cost to cast this spell, sacrifice a creature.$Draw two cards.|
@@ -37532,11 +37542,13 @@ Heartfire Immolator|Core Set 2021|150|U|{1}{R}|Creature - Human Wizard|2|2|Prowe
 Hellkite Punisher|Core Set 2021|151|U|{5}{R}{R}|Creature - Dragon|6|6|Flying${R}: Hellkite Punisher gets +1/+0 until end of turn.|
 Igneous Cur|Core Set 2021|153|C|{1}{R}|Creature - Elemental Dog|1|2|{1}{R}: Igneous Cur gets +2/+0 until end of turn.|
 Kinetic Augur|Core Set 2021|154|U|{3}{R}|Creature - Human Shaman|*|4|Trample$Kinetic Augur's power is equal to the number of instant and sorcery cards in your graveyard.$When Kinetic Augur enters the battlefield, discard up to two cards, then draw that many cards.|
+Sanctum of Shattered Heights|Core Set 2021|157|U|{2}{R}|Legendary Enchantment - Shrine|||{1}, Discard a land card or Shrine card: Sanctum of Shattered Heights deals X damage to target creature or planeswalker, where X is the number of Shrines you control.|
 Soul Sear|Core Set 2021|160|U|{2}{R}|Instant|||Soul Sear deals 5 damage to target creature or planeswalker. That permanent loses indestructible until end of turn.|
 Subira, Tulzidi Caravanner|Core Set 2021|162|R|{2}{R}|Legendary Creature - Human Shaman|2|3|Haste${1}: Another target creature with power 2 or less can't be blocked this turn.${1}{R}, {T}, Discard your hand: Until end of turn, whenever a creature you control with power 2 or less deals combat damage to a player, draw a card.|
 Terror of the Peaks|Core Set 2021|164|M|{3}{R}{R}|Creature - Dragon|5|4|Flying$Spells your opponents cast that target Terror of the Peaks cost an additional 3 life to cast.$Whenever another creature enters the battlefield under your control, Terror of the Peaks deals damage equal to that creature's power to any taret.|
 Traitorous Greed|Core Set 2021|166|U|{3}{R}|Sorcery|||Gain control of target creature until end of turn. Untap that creature. It gains haste until end of turn. Add two mana of any one color.|
 Transmogrify|Core Set 2021|167|R|{3}{R}|Sorcery|||Exile target creature. That creature's controller reveals cards from the top of their library until they reveal a creature card. That player puts that card onto the battlefield, then shuffles the rest into their library.|
+Volcanic Geyser|Core Set 2021|171|U|{X}{R}{R}|Instant|||Volcanic Geyser deals X damage to any target.|
 Volcanic Salvo|Core Set 2021|172|R|{10}{R}{R}|Sorcery|||This spell costs {X} less to cast, where X is the total power of creatures you control.$Volcanic Salvo deals 6 damage to each of up to two target creatures and/or planeswalkers.|
 Azusa, Lost but Seeking|Core Set 2021|173|R|{2}{G}|Legendary Creature - Human Monk|1|2|You may play two additional lands on each of your turns.|
 Burlfist Oak|Core Set 2021|174|U|{2}{G}{G}|Creature - Treefolk|2|3|Whenever you draw a card, Burlfist Oak gets +2/+2 until end of turn.|
@@ -37560,6 +37572,7 @@ Primal Might|Core Set 2021|197|R|{X}{G}|Sorcery|||Target creature you control ge
 Quirion Dryad|Core Set 2021|198|U|{1}{G}|Creature - Dryad|1|1|Whenever you cast a white, blue, black, or red spell, put a +1/+1 counter on Quirion Dryad.|
 Ranger's Guile|Core Set 2021|199|C|{G}|Instant|||Target creature you control gets +1/+1 and gains hexproof until end of turn.|
 Sabertooth Mauler|Core Set 2021|202|C|{3}{G}|Creature - Cat|3|3|At the beginning of your end step, if a creature died this turn, put a +1/+1 counter on Sabertooth Mauler and untap it.|
+Sanctum of Fruitful Harvest|Core Set 2021|203|U|{2}{G}|Legendary Enchantment - Shrine|||At the beginning of your precombat main phase, add X mana of any one color, where X is the number of Shrines you control.|
 Scavenging Ooze|Core Set 2021|204|R|{1}{G}|Creature - Ooze|2|2|{G}: Exile target card from a graveyard. If it was a creature card, put a +1/+1 counter on Scavenging Ooze and you gain 1 life.|
 Sporeweb Weaver|Core Set 2021|208|R|{2}{G}|Creature - Spider|1|4|Reach, hexproof from blue$Whenever Sporeweb Weaver is dealt damage, you gain 1 life and create a 1/1 green Saproling creature token.|
 Trufflesnout|Core Set 2021|212|C|{2}{G}|Creature - Boar|2|2|When Trufflesnout enters the battlefield, choose one —$• Put a +1/+1 counter on Trufflesnout.$• You gain 4 life.|
@@ -37568,12 +37581,14 @@ Wildwood Scourge|Core Set 2021|214|U|{X}{G}|Creature - Hydra|0|0|Wildwood Scourg
 Alpine Houndmaster|Core Set 2021|215|U|{R}{W}|Creature - Human Warrior|2|2|When Alpine Houndmaster enters the battlefield, you may search your library for a card named Alpine Watchdog and/or a card named Igneous Cur, reveal them, put them into your hand, then shuffle your library.$Whenever Alpine Houndmaster attacks, it gets +X/+0 until end of turn, where X is the number of other attacking creatures.|
 Conclave Mentor|Core Set 2021|216|U|{G}{W}|Creature - Centaur Cleric|2|2|If one or more +1/+1 counters would be put on a creature you control, that many plus one +1/+1 counters are put on that creature instead.$When Conclave Mentor dies, you gain life equal to its power.|
 Dire Fleet Warmonger|Core Set 2021|217|U|{1}{B}{R}|Creature - Orc Pirate|3|3|At the beginning of combat on your turn, you may sacrifice another creature. If you do, Dire Fleet Warmonger gets +2/+2 and gains trample until end of turn.|
+Experimental Overload|Core Set 2021|218|U|{2}{U}{R}|Sorcery|||Create an X/X blue and red Weird creature token, where X is the number of instant and sorcery cards in your graveyard. Then you may return an instant or sorcery card from your graveyard to your hand. Exile Experimental Overload.|
 Indulging Patrician|Core Set 2021|219|U|{1}{W}{B}|Creature - Vampire Noble|1|4|Flying$Lifelink$At the beginning of your end step, if you gained 3 or more life this turn, each opponent loses 3 life.|
 Leafkin Avenger|Core Set 2021|220|U|{2}{R}{G}|Creature - Elemental Druid|4|3|{T}: Add {G} for each creature you control with power 4 or greater.${7}{R}: Leafkin Avenger deals damage equal to its power to target player or planeswalker.|
 Lorescale Coatl|Core Set 2021|221|U|{1}{G}{U}|Creature - Snake|2|2|Whenever you draw a card, you may put a +1/+1 counter on Lorescale Coatl.|
 Niambi, Esteemed Speaker|Core Set 2021|222|R|{W}{U}|Legendary Creature - Human Cleric|2|1|Flash$When Niambi, Esteemed Speaker enters the battlefield, you may return another target creature you control to its owner's hand. If you do, you gain life equal to that creature's converted mana cost.${1}{W}{U}, {T}, Discard a legendary card: Draw two cards.|
 Obsessive Stitcher|Core Set 2021|223|U|{1}{U}{B}|Creature - Human Wizard|0|3|{T}: Draw a card, then discard a card.${2}{U}{B}, {T}, Sacrifice Obsessive Stitcher: Return target creature card from your graveyard to the battlefield.|
 Radha, Heart of Keld|Core Set 2021|224|R|{1}{R}{G}|Legendary Creature - Elf Warrior|3|3|As long as it's your turn, Radha, Heart of Keld has first strike.$You may look at the top card of your library any time, and you may play lands from the top of your library.${4}{R}{G}: Radha gets +X/+X until end of turn, where X is the number of lands you control.|
+Sanctum of All|Core Set 2021|225|R|{W}{U}{B}{R}{G}|Legendary Enchantment - Shrine|||At the beginning of your upkeep, you may search your library and/or graveyard for a Shrine card and put it onto the battlefield. If you search your library this way, shuffle it.$If an ability of another Shrine you control triggers while you control six or more Shrines, that ability triggers an additional time.|
 Watcher of the Spheres|Core Set 2021|227|U|{W}{U}|Creature - Bird Wizard|2|2|Flying$Creature spells with flying you cast cost {1} less to cast.$Whenever another creature with flying enters the battlefield under your control, Watcher of the Spheres gets +1/+1 until end of turn.|
 Chromatic Orrery|Core Set 2021|228|M|{7}|Legendary Artifact|||You may spend mana as though it were mana of any color.${T}: Add {C}{C}{C}{C}{C}.${5}, {T}: Draw a card for each color among permanents you control.|
 Mazemind Tome|Core Set 2021|232|R|{2}|Artifact|||{T}, Put a page counter on Mazemind Tome: Scry 1.${2}, {T}, Put a page counter on Mazemind Tome: Draw a card.$When there are four or more page counters on Mazemind Tome, exile it. If you do, you gain 4 life.|
@@ -37590,11 +37605,12 @@ Temple of Malady|Core Set 2021|253|R||Land|||Temple of Malady enters the battlef
 Temple of Mystery|Core Set 2021|254|R||Land|||Temple of Mystery enters the battlefield tapped.$When Temple of Mystery enters the battlefield, scry 1.${T}: Add {G} or {U}.|
 Temple of Silence|Core Set 2021|255|R||Land|||Temple of Silence enters the battlefield tapped.$When Temple of Silence enters the battlefield, scry 1.${T}: Add {W} or {B}.|
 Temple of Triumph|Core Set 2021|256|R||Land|||Temple of Triumph enters the battlefield tapped.$When Temple of Triumph enters the battlefield, scry 1.${T}: Add {R} or {W}.|
+Plains|Core Set 2021|260|C||Basic Land - Plains|||({T}: Add {W}.)|
+Island|Core Set 2021|263|C||Basic Land - Island|||({T}: Add {U}.)|
+Swamp|Core Set 2021|266|C||Basic Land - Swamp|||({T}: Add {B}.)|
+Mountain|Core Set 2021|269|C||Basic Land - Mountain|||({T}: Add {R}.)|
+Forest|Core Set 2021|272|C||Basic Land - Forest|||({T}: Add {G}.)|
 Rin and Seri, Inseparable|Core Set 2021|278|M|{1}{R}{G}{W}|Legendary Creature - Dog Cat|4|4|Whenever you cast a Dog spell, create a 1/1 green Cat creature token.$Whenever you cast a Cat spell, create a 1/1 white Dog creature token.${R}{G}{W}, {T}: Rin and Seri, Inseparable deals damage to any target equal to the number of Dogs you control. You gain life equal to the number of Cats you control.|
-Plains|Core Set 2021|309|C||Basic Land - Plains|||({T}: Add {W}.)|
-Island|Core Set 2021|310|C||Basic Land - Island|||({T}: Add {U}.)|
-Swamp|Core Set 2021|311|C||Basic Land - Swamp|||({T}: Add {B}.)|
-Mountain|Core Set 2021|312|C||Basic Land - Mountain|||({T}: Add {R}.)|
 Containment Priest|Core Set 2021|314|R|{1}{W}|Creature - Human Cleric|2|2|Flash$If a nontoken creature would enter the battlefield and it wasn't cast, exile it instead.|
 Basri, Devoted Paladin|Core Set 2021|320|M|{4}{W}{W}|Legendary Planeswalker - Basri|4|+1: Put a +1/+1 counter on up to one target creature. It gains vigilance until end of turn.$−1: Whenever a creature attacks this turn, put a +1/+1 counter on it.$−6: Creatures you control get +2/+2 and gain flying until end of turn.|
 Adherent of Hope|Core Set 2021|321|C|{1}{W}|Creature - Human Soldier|2|1|At the beginning of combat on your turn, if you control a Basri planeswalker, put a +1/+1 counter on Adherent of Hope.|

From c46afe2cf1497729d8adf5edeff9e62cf1bcb01f Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 15 Jun 2020 19:06:51 -0400
Subject: [PATCH 290/586] Implemented Mistral Singer

---
 Mage.Sets/src/mage/cards/m/MistralSinger.java | 40 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 41 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/m/MistralSinger.java

diff --git a/Mage.Sets/src/mage/cards/m/MistralSinger.java b/Mage.Sets/src/mage/cards/m/MistralSinger.java
new file mode 100644
index 0000000000..fb669618d9
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/m/MistralSinger.java
@@ -0,0 +1,40 @@
+package mage.cards.m;
+
+import mage.MageInt;
+import mage.abilities.keyword.FlyingAbility;
+import mage.abilities.keyword.ProwessAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class MistralSinger extends CardImpl {
+
+    public MistralSinger(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}");
+
+        this.subtype.add(SubType.SIREN);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(2);
+
+        // Flying
+        this.addAbility(FlyingAbility.getInstance());
+
+        // Prowess
+        this.addAbility(new ProwessAbility());
+    }
+
+    private MistralSinger(final MistralSinger card) {
+        super(card);
+    }
+
+    @Override
+    public MistralSinger copy() {
+        return new MistralSinger(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 3468dc4fe0..d4822159bd 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -125,6 +125,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Mazemind Tome", 232, Rarity.RARE, mage.cards.m.MazemindTome.class));
         cards.add(new SetCardInfo("Meteorite", 233, Rarity.UNCOMMON, mage.cards.m.Meteorite.class));
         cards.add(new SetCardInfo("Miscast", 57, Rarity.UNCOMMON, mage.cards.m.Miscast.class));
+        cards.add(new SetCardInfo("Mistral Singer", 58, Rarity.COMMON, mage.cards.m.MistralSinger.class));
         cards.add(new SetCardInfo("Mountain", 269, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Mystic Skyfish", 326, Rarity.COMMON, mage.cards.m.MysticSkyfish.class));
         cards.add(new SetCardInfo("Necromentia", 116, Rarity.RARE, mage.cards.n.Necromentia.class));

From ba599ae4d989559f657dacf9d5ae298f0a1e1783 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 15 Jun 2020 19:12:04 -0400
Subject: [PATCH 291/586] Implemented Shacklegeist

---
 Mage.Sets/src/mage/cards/s/Shacklegeist.java | 65 ++++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java     |  1 +
 2 files changed, 66 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/s/Shacklegeist.java

diff --git a/Mage.Sets/src/mage/cards/s/Shacklegeist.java b/Mage.Sets/src/mage/cards/s/Shacklegeist.java
new file mode 100644
index 0000000000..d5a633beca
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/Shacklegeist.java
@@ -0,0 +1,65 @@
+package mage.cards.s;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.CanBlockOnlyFlyingAbility;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.common.TapTargetCost;
+import mage.abilities.effects.common.TapTargetEffect;
+import mage.abilities.keyword.FlyingAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.filter.StaticFilters;
+import mage.filter.common.FilterControlledPermanent;
+import mage.filter.predicate.Predicates;
+import mage.filter.predicate.permanent.TappedPredicate;
+import mage.target.TargetPermanent;
+import mage.target.common.TargetControlledPermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class Shacklegeist extends CardImpl {
+
+    private static final FilterControlledPermanent filter
+            = new FilterControlledPermanent("untapped Spirits you control");
+
+    static {
+        filter.add(Predicates.not(TappedPredicate.instance));
+        filter.add(SubType.SPIRIT.getPredicate());
+    }
+
+    public Shacklegeist(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}");
+
+        this.subtype.add(SubType.SPIRIT);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(2);
+
+        // Flying
+        this.addAbility(FlyingAbility.getInstance());
+
+        // Shacklegeist can block only creatures with flying.
+        this.addAbility(new CanBlockOnlyFlyingAbility());
+
+        // Tap two untapped Spirits you control: Tap target creature you don't control.
+        Ability ability = new SimpleActivatedAbility(
+                new TapTargetEffect(), new TapTargetCost(new TargetControlledPermanent(2, filter))
+        );
+        ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL));
+        this.addAbility(ability);
+    }
+
+    private Shacklegeist(final Shacklegeist card) {
+        super(card);
+    }
+
+    @Override
+    public Shacklegeist copy() {
+        return new Shacklegeist(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index d4822159bd..a63ff59cc3 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -155,6 +155,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Seasoned Hallowblade", 34, Rarity.UNCOMMON, mage.cards.s.SeasonedHallowblade.class));
         cards.add(new SetCardInfo("See the Truth", 69, Rarity.RARE, mage.cards.s.SeeTheTruth.class));
         cards.add(new SetCardInfo("Selfless Savior", 36, Rarity.UNCOMMON, mage.cards.s.SelflessSavior.class));
+        cards.add(new SetCardInfo("Shacklegeist", 70, Rarity.RARE, mage.cards.s.Shacklegeist.class));
         cards.add(new SetCardInfo("Shipwreck Dowser", 71, Rarity.UNCOMMON, mage.cards.s.ShipwreckDowser.class));
         cards.add(new SetCardInfo("Siege Striker", 37, Rarity.UNCOMMON, mage.cards.s.SiegeStriker.class));
         cards.add(new SetCardInfo("Sigiled Contender", 323, Rarity.UNCOMMON, mage.cards.s.SigiledContender.class));

From 8356b3392d15710b1298c46dae9a52ad6657be4c Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 15 Jun 2020 19:25:34 -0400
Subject: [PATCH 292/586] Implemented Ghostly Pilferer

---
 .../src/mage/cards/g/GhostlyPilferer.java     | 94 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 95 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/g/GhostlyPilferer.java

diff --git a/Mage.Sets/src/mage/cards/g/GhostlyPilferer.java b/Mage.Sets/src/mage/cards/g/GhostlyPilferer.java
new file mode 100644
index 0000000000..f583333542
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/g/GhostlyPilferer.java
@@ -0,0 +1,94 @@
+package mage.cards.g;
+
+import mage.MageInt;
+import mage.abilities.TriggeredAbilityImpl;
+import mage.abilities.common.BecomesTappedSourceTriggeredAbility;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.common.DiscardCardCost;
+import mage.abilities.costs.mana.GenericManaCost;
+import mage.abilities.effects.common.DoIfCostPaid;
+import mage.abilities.effects.common.DrawCardSourceControllerEffect;
+import mage.abilities.effects.common.combat.CantBeBlockedSourceEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+import mage.constants.Zone;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+import mage.game.stack.Spell;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class GhostlyPilferer extends CardImpl {
+
+    public GhostlyPilferer(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}");
+
+        this.subtype.add(SubType.SPIRIT);
+        this.subtype.add(SubType.ROGUE);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(1);
+
+        // Whenever Ghostly Pilferer becomes tapped, you may pay {2}. If you do, draw a card.
+        this.addAbility(new BecomesTappedSourceTriggeredAbility(new DoIfCostPaid(
+                new DrawCardSourceControllerEffect(1), new GenericManaCost(2)
+        )));
+
+        // Whenever an opponent casts a spell from anywhere other than their hand, draw a card.
+        this.addAbility(new GhostlyPilfererTriggeredAbility());
+
+        // Discard a card: Ghostly Pilferer can't be blocked this turn.
+        this.addAbility(new SimpleActivatedAbility(
+                new CantBeBlockedSourceEffect(Duration.EndOfTurn), new DiscardCardCost()
+        ));
+    }
+
+    private GhostlyPilferer(final GhostlyPilferer card) {
+        super(card);
+    }
+
+    @Override
+    public GhostlyPilferer copy() {
+        return new GhostlyPilferer(this);
+    }
+}
+
+class GhostlyPilfererTriggeredAbility extends TriggeredAbilityImpl {
+
+    GhostlyPilfererTriggeredAbility() {
+        super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), false);
+    }
+
+    private GhostlyPilfererTriggeredAbility(final GhostlyPilfererTriggeredAbility ability) {
+        super(ability);
+    }
+
+    @Override
+    public GhostlyPilfererTriggeredAbility copy() {
+        return new GhostlyPilfererTriggeredAbility(this);
+    }
+
+    @Override
+    public boolean checkEventType(GameEvent event, Game game) {
+        return event.getType() == GameEvent.EventType.SPELL_CAST;
+    }
+
+    @Override
+    public boolean checkTrigger(GameEvent event, Game game) {
+        if (event.getZone() == Zone.HAND) {
+            return false;
+        }
+        Spell spell = game.getStack().getSpell(event.getTargetId());
+        return spell != null;
+    }
+
+    @Override
+    public String getRule() {
+        return "Whenever an opponent casts a spell from anywhere other than their hand, draw a card.";
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index a63ff59cc3..5d8d3329b9 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -87,6 +87,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Garruk's Gorehorn", 184, Rarity.COMMON, mage.cards.g.GarruksGorehorn.class));
         cards.add(new SetCardInfo("Garruk's Warsteed", 337, Rarity.RARE, mage.cards.g.GarruksWarsteed.class));
         cards.add(new SetCardInfo("Garruk, Savage Herald", 336, Rarity.MYTHIC, mage.cards.g.GarrukSavageHerald.class));
+        cards.add(new SetCardInfo("Ghostly Pilferer", 52, Rarity.RARE, mage.cards.g.GhostlyPilferer.class));
         cards.add(new SetCardInfo("Glorious Anthem", 21, Rarity.RARE, mage.cards.g.GloriousAnthem.class));
         cards.add(new SetCardInfo("Goblin Arsonist", 147, Rarity.COMMON, mage.cards.g.GoblinArsonist.class));
         cards.add(new SetCardInfo("Goremand", 101, Rarity.UNCOMMON, mage.cards.g.Goremand.class));

From 0af261aeb37b37eb7f2d53e9de310acf847b7e9e Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 15 Jun 2020 19:54:00 -0400
Subject: [PATCH 293/586] Implemented Barrin, Tolarian Archmage

---
 .../mage/cards/b/BarrinTolarianArchmage.java  | 111 ++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |   1 +
 2 files changed, 112 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/b/BarrinTolarianArchmage.java

diff --git a/Mage.Sets/src/mage/cards/b/BarrinTolarianArchmage.java b/Mage.Sets/src/mage/cards/b/BarrinTolarianArchmage.java
new file mode 100644
index 0000000000..77dc1727ab
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/b/BarrinTolarianArchmage.java
@@ -0,0 +1,111 @@
+package mage.cards.b;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.BeginningOfEndStepTriggeredAbility;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.condition.Condition;
+import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
+import mage.abilities.effects.common.DrawCardSourceControllerEffect;
+import mage.abilities.effects.common.ReturnToHandTargetEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.*;
+import mage.filter.FilterPermanent;
+import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent;
+import mage.filter.predicate.permanent.AnotherPredicate;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+import mage.game.events.ZoneChangeEvent;
+import mage.target.TargetPermanent;
+import mage.watchers.Watcher;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class BarrinTolarianArchmage extends CardImpl {
+
+    private static final FilterPermanent filter
+            = new FilterCreatureOrPlaneswalkerPermanent("other target creature or planeswalker");
+
+    static {
+        filter.add(AnotherPredicate.instance);
+    }
+
+    public BarrinTolarianArchmage(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{U}");
+
+        this.addSuperType(SuperType.LEGENDARY);
+        this.subtype.add(SubType.HUMAN);
+        this.subtype.add(SubType.WIZARD);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(2);
+
+        // When Barrin, Tolarian Archmage enters the battlefield, return up to one other target creature or planeswalker to its owner's hand.
+        Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnToHandTargetEffect());
+        ability.addTarget(new TargetPermanent(0, 1, filter, false));
+        this.addAbility(ability);
+
+        // At the beginning of your end step, if a permanent was put into your hand from the battlefield this turn, draw a card.
+        this.addAbility(new ConditionalInterveningIfTriggeredAbility(
+                new BeginningOfEndStepTriggeredAbility(
+                        new DrawCardSourceControllerEffect(1), TargetController.YOU, false
+                ), BarrinTolarianArchmageCondition.instance, "At the beginning of your end step, " +
+                "if a permanent was put into your hand from the battlefield this turn, draw a card."
+        ), new BarrinTolarianArchmageWatcher());
+    }
+
+    private BarrinTolarianArchmage(final BarrinTolarianArchmage card) {
+        super(card);
+    }
+
+    @Override
+    public BarrinTolarianArchmage copy() {
+        return new BarrinTolarianArchmage(this);
+    }
+}
+
+enum BarrinTolarianArchmageCondition implements Condition {
+    instance;
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        BarrinTolarianArchmageWatcher watcher = game.getState().getWatcher(BarrinTolarianArchmageWatcher.class);
+        return watcher != null && watcher.checkPlayer(source.getControllerId());
+    }
+}
+
+class BarrinTolarianArchmageWatcher extends Watcher {
+
+    private final Set<UUID> playerSet = new HashSet<>();
+
+    BarrinTolarianArchmageWatcher() {
+        super(WatcherScope.GAME);
+    }
+
+    @Override
+    public void watch(GameEvent event, Game game) {
+        if (event.getType() != GameEvent.EventType.ZONE_CHANGE) {
+            return;
+        }
+        ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
+        if (zEvent.getFromZone() == Zone.BATTLEFIELD
+                && zEvent.getToZone() == Zone.HAND) {
+            playerSet.add(zEvent.getTarget().getOwnerId());
+        }
+    }
+
+    @Override
+    public void reset() {
+        playerSet.clear();
+        super.reset();
+    }
+
+    boolean checkPlayer(UUID playerId) {
+        return playerSet.contains(playerId);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 5d8d3329b9..f7a714dd57 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -41,6 +41,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Azusa, Lost but Seeking", 173, Rarity.RARE, mage.cards.a.AzusaLostButSeeking.class));
         cards.add(new SetCardInfo("Bad Deal", 89, Rarity.UNCOMMON, mage.cards.b.BadDeal.class));
         cards.add(new SetCardInfo("Baneslayer Angel", 6, Rarity.MYTHIC, mage.cards.b.BaneslayerAngel.class));
+        cards.add(new SetCardInfo("Barrin, Tolarian Archmage", 45, Rarity.RARE, mage.cards.b.BarrinTolarianArchmage.class));
         cards.add(new SetCardInfo("Basri Ket", 7, Rarity.MYTHIC, mage.cards.b.BasriKet.class));
         cards.add(new SetCardInfo("Basri's Acolyte", 8, Rarity.COMMON, mage.cards.b.BasrisAcolyte.class));
         cards.add(new SetCardInfo("Basri's Aegis", 322, Rarity.RARE, mage.cards.b.BasrisAegis.class));

From e8a99ec7f3f3e2ca285f20947b2fd440a08b5b91 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 15 Jun 2020 20:09:31 -0400
Subject: [PATCH 294/586] Implemented Experimental Overload

---
 .../mage/cards/e/ExperimentalOverload.java    | 78 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 .../game/permanent/token/WeirdToken2.java     | 32 ++++++++
 3 files changed, 111 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/e/ExperimentalOverload.java
 create mode 100644 Mage/src/main/java/mage/game/permanent/token/WeirdToken2.java

diff --git a/Mage.Sets/src/mage/cards/e/ExperimentalOverload.java b/Mage.Sets/src/mage/cards/e/ExperimentalOverload.java
new file mode 100644
index 0000000000..ae7d41f907
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/e/ExperimentalOverload.java
@@ -0,0 +1,78 @@
+package mage.cards.e;
+
+import mage.abilities.Ability;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.ExileSpellEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Outcome;
+import mage.constants.Zone;
+import mage.filter.StaticFilters;
+import mage.game.Game;
+import mage.game.permanent.token.WeirdToken2;
+import mage.players.Player;
+import mage.target.TargetCard;
+import mage.target.common.TargetCardInYourGraveyard;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class ExperimentalOverload extends CardImpl {
+
+    public ExperimentalOverload(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}{R}");
+
+        // Create an X/X blue and red Weird creature token, where X is the number of instant and sorcery cards in your graveyard. Then you may return an instant or sorcery card from your graveyard to your hand. Exile Experimental Overload.
+        this.getSpellAbility().addEffect(new ExperimentalOverloadEffect());
+        this.getSpellAbility().addEffect(ExileSpellEffect.getInstance());
+    }
+
+    private ExperimentalOverload(final ExperimentalOverload card) {
+        super(card);
+    }
+
+    @Override
+    public ExperimentalOverload copy() {
+        return new ExperimentalOverload(this);
+    }
+}
+
+class ExperimentalOverloadEffect extends OneShotEffect {
+
+    ExperimentalOverloadEffect() {
+        super(Outcome.Benefit);
+        staticText = "Create an X/X blue and red Weird creature token, " +
+                "where X is the number of instant and sorcery cards in your graveyard. " +
+                "Then you may return an instant or sorcery card from your graveyard to your hand.";
+    }
+
+    private ExperimentalOverloadEffect(final ExperimentalOverloadEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public ExperimentalOverloadEffect copy() {
+        return new ExperimentalOverloadEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Player player = game.getPlayer(source.getControllerId());
+        if (player == null) {
+            return false;
+        }
+        int spellCount = player.getGraveyard().count(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY, game);
+        new WeirdToken2(spellCount).putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId());
+        if (spellCount < 1) {
+            return true;
+        }
+        TargetCard target = new TargetCardInYourGraveyard(
+                0, 1, StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY, true
+        );
+        player.choose(outcome, player.getGraveyard(), target, game);
+        return player.moveCards(game.getCard(target.getFirstTarget()), Zone.HAND, source, game);
+    }
+}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index f7a714dd57..0d3f879740 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -73,6 +73,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Duress", 96, Rarity.COMMON, mage.cards.d.Duress.class));
         cards.add(new SetCardInfo("Elder Gargaroth", 179, Rarity.MYTHIC, mage.cards.e.ElderGargaroth.class));
         cards.add(new SetCardInfo("Eliminate", 97, Rarity.UNCOMMON, mage.cards.e.Eliminate.class));
+        cards.add(new SetCardInfo("Experimental Overload", 218, Rarity.UNCOMMON, mage.cards.e.ExperimentalOverload.class));
         cards.add(new SetCardInfo("Fabled Passage", 246, Rarity.RARE, mage.cards.f.FabledPassage.class));
         cards.add(new SetCardInfo("Faith's Fetters", 17, Rarity.UNCOMMON, mage.cards.f.FaithsFetters.class));
         cards.add(new SetCardInfo("Falconer Adept", 18, Rarity.UNCOMMON, mage.cards.f.FalconerAdept.class));
diff --git a/Mage/src/main/java/mage/game/permanent/token/WeirdToken2.java b/Mage/src/main/java/mage/game/permanent/token/WeirdToken2.java
new file mode 100644
index 0000000000..837479df7f
--- /dev/null
+++ b/Mage/src/main/java/mage/game/permanent/token/WeirdToken2.java
@@ -0,0 +1,32 @@
+package mage.game.permanent.token;
+
+import mage.MageInt;
+import mage.abilities.keyword.DefenderAbility;
+import mage.abilities.keyword.FlyingAbility;
+import mage.constants.CardType;
+import mage.constants.SubType;
+
+/**
+ *
+ * @author TheElk801
+ */
+public final class WeirdToken2 extends TokenImpl {
+
+    public WeirdToken2(int xValue) {
+        super("Weird", "X/X blue and red Weird creature token");
+        cardType.add(CardType.CREATURE);
+        color.setBlue(true);
+        color.setRed(true);
+        subtype.add(SubType.WEIRD);
+        power = new MageInt(xValue);
+        toughness = new MageInt(xValue);
+    }
+
+    private WeirdToken2(final WeirdToken2 token) {
+        super(token);
+    }
+
+    public WeirdToken2 copy() {
+        return new WeirdToken2(this);
+    }
+}

From 83b614a6eb23594c9a0ef928bd33fa1971306dc4 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 15 Jun 2020 20:23:42 -0400
Subject: [PATCH 295/586] Implemented Niambi, Esteemed Speaker

---
 .../mage/cards/n/NiambiEsteemedSpeaker.java   | 109 ++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |   1 +
 2 files changed, 110 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/n/NiambiEsteemedSpeaker.java

diff --git a/Mage.Sets/src/mage/cards/n/NiambiEsteemedSpeaker.java b/Mage.Sets/src/mage/cards/n/NiambiEsteemedSpeaker.java
new file mode 100644
index 0000000000..515650dbab
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/n/NiambiEsteemedSpeaker.java
@@ -0,0 +1,109 @@
+package mage.cards.n;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.common.DiscardTargetCost;
+import mage.abilities.costs.common.TapSourceCost;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.DrawCardSourceControllerEffect;
+import mage.abilities.effects.common.ReturnToHandTargetEffect;
+import mage.abilities.keyword.FlashAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Outcome;
+import mage.constants.SubType;
+import mage.constants.SuperType;
+import mage.filter.FilterCard;
+import mage.filter.FilterPermanent;
+import mage.filter.common.FilterControlledCreaturePermanent;
+import mage.filter.predicate.permanent.AnotherPredicate;
+import mage.game.Game;
+import mage.game.permanent.Permanent;
+import mage.players.Player;
+import mage.target.TargetPermanent;
+import mage.target.common.TargetCardInHand;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class NiambiEsteemedSpeaker extends CardImpl {
+
+    private static final FilterPermanent filter
+            = new FilterControlledCreaturePermanent("another target creature you control");
+    private static final FilterCard filter2 = new FilterCard("a legendary card");
+
+    static {
+        filter.add(AnotherPredicate.instance);
+        filter2.add(SuperType.LEGENDARY.getPredicate());
+    }
+
+    public NiambiEsteemedSpeaker(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{U}");
+
+        this.addSuperType(SuperType.LEGENDARY);
+        this.subtype.add(SubType.HUMAN);
+        this.subtype.add(SubType.CLERIC);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(1);
+
+        // Flash
+        this.addAbility(FlashAbility.getInstance());
+
+        // When Niambi, Esteemed Speaker enters the battlefield, you may return another target creature you control to its owner's hand. If you do, you gain life equal to that creature's converted mana cost.
+        Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnToHandTargetEffect(), true);
+        ability.addEffect(new NiambiEsteemedSpeakerEffect());
+        ability.addTarget(new TargetPermanent(filter));
+        this.addAbility(ability);
+
+        // {1}{W}{U}, {T}, Discard a legendary card: Draw two cards.
+        ability = new SimpleActivatedAbility(
+                new DrawCardSourceControllerEffect(2), new ManaCostsImpl("{1}{W}{U}")
+        );
+        ability.addCost(new TapSourceCost());
+        ability.addCost(new DiscardTargetCost(new TargetCardInHand(filter2)));
+        this.addAbility(ability);
+    }
+
+    private NiambiEsteemedSpeaker(final NiambiEsteemedSpeaker card) {
+        super(card);
+    }
+
+    @Override
+    public NiambiEsteemedSpeaker copy() {
+        return new NiambiEsteemedSpeaker(this);
+    }
+}
+
+class NiambiEsteemedSpeakerEffect extends OneShotEffect {
+
+    NiambiEsteemedSpeakerEffect() {
+        super(Outcome.Benefit);
+        staticText = "If you do, you gain life equal to that creature's converted mana cost.";
+    }
+
+    private NiambiEsteemedSpeakerEffect(final NiambiEsteemedSpeakerEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public NiambiEsteemedSpeakerEffect copy() {
+        return new NiambiEsteemedSpeakerEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Permanent permanent = game.getPermanentOrLKIBattlefield(source.getFirstTarget());
+        Player player = game.getPlayer(source.getControllerId());
+        if (permanent == null || player == null) {
+            return false;
+        }
+        return permanent.getConvertedManaCost() > 0
+                && player.gainLife(permanent.getConvertedManaCost(), game, source) > 0;
+    }
+}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 0d3f879740..854ddb84ea 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -132,6 +132,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Mountain", 269, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Mystic Skyfish", 326, Rarity.COMMON, mage.cards.m.MysticSkyfish.class));
         cards.add(new SetCardInfo("Necromentia", 116, Rarity.RARE, mage.cards.n.Necromentia.class));
+        cards.add(new SetCardInfo("Niambi, Esteemed Speaker", 222, Rarity.RARE, mage.cards.n.NiambiEsteemedSpeaker.class));
         cards.add(new SetCardInfo("Obsessive Stitcher", 223, Rarity.UNCOMMON, mage.cards.o.ObsessiveStitcher.class));
         cards.add(new SetCardInfo("Pack Leader", 29, Rarity.RARE, mage.cards.p.PackLeader.class));
         cards.add(new SetCardInfo("Palladium Myr", 234, Rarity.UNCOMMON, mage.cards.p.PalladiumMyr.class));

From 00c01408f11f08d93f5cbf95f268425255b39a0c Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 15 Jun 2020 20:29:53 -0400
Subject: [PATCH 296/586] Implemented Unleash Fury

---
 Mage.Sets/src/mage/cards/u/UnleashFury.java | 67 +++++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java    |  1 +
 Utils/mtg-cards-data.txt                    |  1 +
 3 files changed, 69 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/u/UnleashFury.java

diff --git a/Mage.Sets/src/mage/cards/u/UnleashFury.java b/Mage.Sets/src/mage/cards/u/UnleashFury.java
new file mode 100644
index 0000000000..6ae8ccdea9
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/u/UnleashFury.java
@@ -0,0 +1,67 @@
+package mage.cards.u;
+
+import mage.abilities.Ability;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.continuous.BoostTargetEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.Outcome;
+import mage.game.Game;
+import mage.game.permanent.Permanent;
+import mage.target.common.TargetCreaturePermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class UnleashFury extends CardImpl {
+
+    public UnleashFury(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{R}");
+
+        // Double the power of target creature until end of turn.
+        this.getSpellAbility().addEffect(new UnleashFuryEffect());
+        this.getSpellAbility().addTarget(new TargetCreaturePermanent());
+    }
+
+    private UnleashFury(final UnleashFury card) {
+        super(card);
+    }
+
+    @Override
+    public UnleashFury copy() {
+        return new UnleashFury(this);
+    }
+}
+
+class UnleashFuryEffect extends OneShotEffect {
+
+    UnleashFuryEffect() {
+        super(Outcome.Benefit);
+        staticText = "double the power of target creature until end of turn";
+    }
+
+    private UnleashFuryEffect(final UnleashFuryEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public UnleashFuryEffect copy() {
+        return new UnleashFuryEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Permanent permanent = game.getPermanent(source.getFirstTarget());
+        if (permanent == null) {
+            return false;
+        }
+        game.addEffect(new BoostTargetEffect(
+                2 * permanent.getPower().getValue(), 0, Duration.EndOfTurn
+        ), source);
+        return true;
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 854ddb84ea..f949dd72cb 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -193,6 +193,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Traitorous Greed", 166, Rarity.UNCOMMON, mage.cards.t.TraitorousGreed.class));
         cards.add(new SetCardInfo("Trufflesnout", 212, Rarity.COMMON, mage.cards.t.Trufflesnout.class));
         cards.add(new SetCardInfo("Ugin, the Spirit Dragon", 1, Rarity.MYTHIC, mage.cards.u.UginTheSpiritDragon.class));
+        cards.add(new SetCardInfo("Unleash Fury", 170, Rarity.UNCOMMON, mage.cards.u.UnleashFury.class));
         cards.add(new SetCardInfo("Unsubstantiate", 82, Rarity.UNCOMMON, mage.cards.u.Unsubstantiate.class));
         cards.add(new SetCardInfo("Valorous Steed", 42, Rarity.COMMON, mage.cards.v.ValorousSteed.class));
         cards.add(new SetCardInfo("Village Rites", 126, Rarity.COMMON, mage.cards.v.VillageRites.class));
diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt
index b5d430a083..c8405a6902 100644
--- a/Utils/mtg-cards-data.txt
+++ b/Utils/mtg-cards-data.txt
@@ -37548,6 +37548,7 @@ Subira, Tulzidi Caravanner|Core Set 2021|162|R|{2}{R}|Legendary Creature - Human
 Terror of the Peaks|Core Set 2021|164|M|{3}{R}{R}|Creature - Dragon|5|4|Flying$Spells your opponents cast that target Terror of the Peaks cost an additional 3 life to cast.$Whenever another creature enters the battlefield under your control, Terror of the Peaks deals damage equal to that creature's power to any taret.|
 Traitorous Greed|Core Set 2021|166|U|{3}{R}|Sorcery|||Gain control of target creature until end of turn. Untap that creature. It gains haste until end of turn. Add two mana of any one color.|
 Transmogrify|Core Set 2021|167|R|{3}{R}|Sorcery|||Exile target creature. That creature's controller reveals cards from the top of their library until they reveal a creature card. That player puts that card onto the battlefield, then shuffles the rest into their library.|
+Unleash Fury|Core Set 2021|170|U|{1}{R}|Instant|||Double the power of target creature until end of turn.|
 Volcanic Geyser|Core Set 2021|171|U|{X}{R}{R}|Instant|||Volcanic Geyser deals X damage to any target.|
 Volcanic Salvo|Core Set 2021|172|R|{10}{R}{R}|Sorcery|||This spell costs {X} less to cast, where X is the total power of creatures you control.$Volcanic Salvo deals 6 damage to each of up to two target creatures and/or planeswalkers.|
 Azusa, Lost but Seeking|Core Set 2021|173|R|{2}{G}|Legendary Creature - Human Monk|1|2|You may play two additional lands on each of your turns.|

From 2f211dfe3bba839f8a3b563d662c2ffb754d1c65 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 15 Jun 2020 21:43:28 -0400
Subject: [PATCH 297/586] updated Lorescale Coatl triggered ability

---
 .../src/mage/cards/l/LorescaleCoatl.java      | 20 +++++++++----------
 1 file changed, 9 insertions(+), 11 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/l/LorescaleCoatl.java b/Mage.Sets/src/mage/cards/l/LorescaleCoatl.java
index 45a24b501a..80077d3d8f 100644
--- a/Mage.Sets/src/mage/cards/l/LorescaleCoatl.java
+++ b/Mage.Sets/src/mage/cards/l/LorescaleCoatl.java
@@ -1,8 +1,5 @@
-
-
 package mage.cards.l;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.common.DrawCardControllerTriggeredAbility;
 import mage.abilities.effects.common.counter.AddCountersSourceEffect;
@@ -12,25 +9,26 @@ import mage.constants.CardType;
 import mage.constants.SubType;
 import mage.counters.CounterType;
 
+import java.util.UUID;
+
 /**
- *
  * @author Loki
  */
 public final class LorescaleCoatl extends CardImpl {
 
-    public LorescaleCoatl (UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{G}{U}");
+    public LorescaleCoatl(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}{U}");
         this.subtype.add(SubType.SNAKE);
 
-
-
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
 
-        this.addAbility(new DrawCardControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), true));
+        this.addAbility(new DrawCardControllerTriggeredAbility(
+                new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false
+        ));
     }
 
-    public LorescaleCoatl (final LorescaleCoatl card) {
+    private LorescaleCoatl(final LorescaleCoatl card) {
         super(card);
     }
 
@@ -38,4 +36,4 @@ public final class LorescaleCoatl extends CardImpl {
     public LorescaleCoatl copy() {
         return new LorescaleCoatl(this);
     }
-}
\ No newline at end of file
+}

From 3f4f0bba8c840cbacefcb60e30c676693a380c33 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 15 Jun 2020 22:15:14 -0400
Subject: [PATCH 298/586] fixed Unleash Fury tripling power rather than
 doubling

---
 Mage.Sets/src/mage/cards/u/UnleashFury.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Mage.Sets/src/mage/cards/u/UnleashFury.java b/Mage.Sets/src/mage/cards/u/UnleashFury.java
index 6ae8ccdea9..564a6ccfe9 100644
--- a/Mage.Sets/src/mage/cards/u/UnleashFury.java
+++ b/Mage.Sets/src/mage/cards/u/UnleashFury.java
@@ -60,7 +60,7 @@ class UnleashFuryEffect extends OneShotEffect {
             return false;
         }
         game.addEffect(new BoostTargetEffect(
-                2 * permanent.getPower().getValue(), 0, Duration.EndOfTurn
+                permanent.getPower().getValue(), 0, Duration.EndOfTurn
         ), source);
         return true;
     }

From bb391e92bbf45181400d32280a623ad388f8e3c0 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Tue, 16 Jun 2020 08:02:06 +0200
Subject: [PATCH 299/586] * Updated com.google.guava dependency to 29.0-jre.

---
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index 09cfb75e74..cee6748623 100644
--- a/pom.xml
+++ b/pom.xml
@@ -111,7 +111,7 @@
             <dependency>
                 <groupId>com.google.guava</groupId>
                 <artifactId>guava</artifactId>
-                <version>20.0</version>
+                <version>29.0-jre</version>
             </dependency>
         </dependencies>
     </dependencyManagement>

From 98feb0dd95ba2ab5bdfb68e5c31c24efe23dd90b Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Tue, 16 Jun 2020 12:31:26 +0400
Subject: [PATCH 300/586] Additional fixes and improves for PR #6641

---
 .../src/mage/cards/c/ChandraHeartOfFire.java  | 47 +++++++++----------
 .../src/mage/cards/c/ChandrasPyreling.java    | 14 ++----
 .../mage/cards/k/KumanoMasterYamabushi.java   | 21 ++++-----
 Mage.Sets/src/mage/cards/o/OrzhovKeyrune.java | 13 +++--
 4 files changed, 43 insertions(+), 52 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/c/ChandraHeartOfFire.java b/Mage.Sets/src/mage/cards/c/ChandraHeartOfFire.java
index dfd88af342..169b6fcd4a 100644
--- a/Mage.Sets/src/mage/cards/c/ChandraHeartOfFire.java
+++ b/Mage.Sets/src/mage/cards/c/ChandraHeartOfFire.java
@@ -12,42 +12,41 @@ import mage.abilities.effects.common.ExileTop3MayPlayUntilEndOfTurnEffect;
 import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect;
 import mage.abilities.effects.common.discard.DiscardHandControllerEffect;
 import mage.abilities.effects.mana.BasicManaEffect;
-import mage.cards.*;
+import mage.cards.Card;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.cards.CardsImpl;
 import mage.constants.*;
 import mage.filter.FilterCard;
 import mage.filter.predicate.Predicates;
 import mage.filter.predicate.mageobject.ColorPredicate;
 import mage.game.Game;
 import mage.players.Player;
-import mage.target.TargetCard;
+import mage.target.Target;
 import mage.target.common.TargetAnyTarget;
 import mage.target.common.TargetCardInLibrary;
+import mage.target.common.TargetCardInYourGraveyard;
 import mage.target.targetpointer.FixedTargets;
 
 import java.util.HashSet;
-import java.util.List;
 import java.util.Set;
 import java.util.UUID;
-import java.util.stream.Collectors;
 
 /**
- *
  * @author htrajan
  */
 public final class ChandraHeartOfFire extends CardImpl {
 
     public ChandraHeartOfFire(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{3}{R}{R}");
-        
+
         this.addSuperType(SuperType.LEGENDARY);
         this.subtype.add(SubType.CHANDRA);
         this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5));
 
         // +1: Discard your hand, then exile the top three cards of your library. Until end of turn, you may play cards exiled this way.
         Ability ability = new LoyaltyAbility(new DiscardHandControllerEffect(), 1);
-        ability.addEffect(new ExileTop3MayPlayUntilEndOfTurnEffect() {{
-            setText(", then " + getText(null));
-        }});
+        ability.addEffect(new ExileTop3MayPlayUntilEndOfTurnEffect().concatBy(", then"));
         this.addAbility(ability);
 
         // +1: Chandra, Heart of Fire deals 2 damage to any target.
@@ -73,7 +72,7 @@ public final class ChandraHeartOfFire extends CardImpl {
 
 class ChandraHeartOfFireUltimateEffect extends OneShotEffect {
 
-    private static final FilterCard filter = new FilterCard();
+    private static final FilterCard filter = new FilterCard("red instant or sorcery");
 
     static {
         filter.add(new ColorPredicate(ObjectColor.RED));
@@ -100,22 +99,22 @@ class ChandraHeartOfFireUltimateEffect extends OneShotEffect {
         if (controller != null) {
             Set<Card> exiledCards = new HashSet<>();
 
-            filter.setMessage("red instant or sorcery in your graveyard");
-            TargetCard target = new TargetCard(0, Integer.MAX_VALUE, Zone.GRAVEYARD, filter);
-            if (controller.choose(Outcome.Exile, controller.getGraveyard(), target, game)) {
-                List<UUID> targets = target.getTargets();
-                controller.moveCards(new CardsImpl(targets), Zone.EXILED, source, game);
-                exiledCards.addAll(targets.stream().map(game::getCard).collect(Collectors.toList()));
+            // from graveyard
+            Target target = new TargetCardInYourGraveyard(0, Integer.MAX_VALUE, filter, true).withChooseHint("from graveyard");
+            if (target.canChoose(source.getSourceId(), controller.getId(), game)
+                    && target.choose(Outcome.AIDontUseIt, controller.getId(), source.getSourceId(), game)) {
+                Set<Card> cards = new CardsImpl(target.getTargets()).getCards(game);
+                controller.moveCards(cards, Zone.EXILED, source, game);
+                exiledCards.addAll(cards);
             }
 
-            filter.setMessage("red instant or sorcery in your library");
-            Cards cardsInLibrary = new CardsImpl();
-            cardsInLibrary.addAll(controller.getLibrary().getCards(game));
-            target = new TargetCardInLibrary(0, Integer.MAX_VALUE, filter);
-            if (controller.choose(Outcome.Exile, cardsInLibrary, target, game)) {
-                List<UUID> targets = target.getTargets();
-                controller.moveCards(new CardsImpl(targets), Zone.EXILED, source, game);
-                exiledCards.addAll(targets.stream().map(game::getCard).collect(Collectors.toList()));
+            // from library
+            target = new TargetCardInLibrary(0, Integer.MAX_VALUE, filter).withChooseHint("from library");
+            if (target.canChoose(source.getSourceId(), controller.getId(), game)
+                    && target.choose(Outcome.AIDontUseIt, controller.getId(), source.getSourceId(), game)) {
+                Set<Card> cards = new CardsImpl(target.getTargets()).getCards(game);
+                controller.moveCards(cards, Zone.EXILED, source, game);
+                exiledCards.addAll(cards);
             }
             controller.shuffleLibrary(source, game);
 
diff --git a/Mage.Sets/src/mage/cards/c/ChandrasPyreling.java b/Mage.Sets/src/mage/cards/c/ChandrasPyreling.java
index 55f8778bd8..bca970e2a6 100644
--- a/Mage.Sets/src/mage/cards/c/ChandrasPyreling.java
+++ b/Mage.Sets/src/mage/cards/c/ChandrasPyreling.java
@@ -2,7 +2,6 @@ package mage.cards.c;
 
 import mage.MageInt;
 import mage.abilities.TriggeredAbilityImpl;
-import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.continuous.BoostSourceEffect;
 import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
 import mage.abilities.keyword.DoubleStrikeAbility;
@@ -17,17 +16,17 @@ import mage.game.events.DamagedPlayerEvent;
 import mage.game.events.GameEvent;
 import mage.game.events.GameEvent.EventType;
 
+import java.util.Objects;
 import java.util.UUID;
 
 /**
- *
  * @author htrajan
  */
 public final class ChandrasPyreling extends CardImpl {
 
     public ChandrasPyreling(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}");
-        
+
         this.subtype.add(SubType.ELEMENTAL);
         this.subtype.add(SubType.LIZARD);
         this.power = new MageInt(1);
@@ -49,16 +48,13 @@ public final class ChandrasPyreling extends CardImpl {
 
 class ChandrasPyrelingAbility extends TriggeredAbilityImpl {
 
-    private static final Effect effect = new BoostSourceEffect(1, 0, Duration.EndOfTurn);
-
     ChandrasPyrelingAbility() {
-        super(Zone.BATTLEFIELD, effect);
+        super(Zone.BATTLEFIELD, new BoostSourceEffect(1, 0, Duration.EndOfTurn));
         addEffect(new GainAbilitySourceEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfTurn));
     }
 
-    private ChandrasPyrelingAbility(ChandrasPyrelingAbility ability) {
+    private ChandrasPyrelingAbility(final ChandrasPyrelingAbility ability) {
         super(ability);
-        addEffect(new GainAbilitySourceEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfTurn));
     }
 
     @Override
@@ -76,7 +72,7 @@ class ChandrasPyrelingAbility extends TriggeredAbilityImpl {
         DamagedPlayerEvent damageEvent = (DamagedPlayerEvent) event;
         return !damageEvent.isCombatDamage()
                 && game.getOpponents(controllerId).contains(event.getTargetId())
-                && game.getControllerId(event.getSourceId()).equals(controllerId);
+                && Objects.equals(controllerId, game.getControllerId(event.getSourceId()));
     }
 
     @Override
diff --git a/Mage.Sets/src/mage/cards/k/KumanoMasterYamabushi.java b/Mage.Sets/src/mage/cards/k/KumanoMasterYamabushi.java
index 371a716cbf..a833e83fdb 100644
--- a/Mage.Sets/src/mage/cards/k/KumanoMasterYamabushi.java
+++ b/Mage.Sets/src/mage/cards/k/KumanoMasterYamabushi.java
@@ -1,5 +1,5 @@
 /*
- *  
+ *
  * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without modification, are
@@ -25,12 +25,11 @@
  *  The views and conclusions contained in the software and documentation are those of the
  *  authors and should not be interpreted as representing official policies, either expressed
  *  or implied, of BetaSteward_at_googlemail.com.
- * 
+ *
  */
 
 package mage.cards.k;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleActivatedAbility;
@@ -40,21 +39,19 @@ import mage.abilities.effects.common.DamageTargetEffect;
 import mage.abilities.effects.common.replacement.DealtDamageToCreatureBySourceDies;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.CardType;
-import mage.constants.SubType;
-import mage.constants.Duration;
-import mage.constants.SuperType;
-import mage.constants.Zone;
+import mage.constants.*;
 import mage.target.common.TargetAnyTarget;
 import mage.watchers.common.DamagedByWatcher;
 
+import java.util.UUID;
+
 /**
  * @author LevelX
  */
 public final class KumanoMasterYamabushi extends CardImpl {
 
     public KumanoMasterYamabushi(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{R}{R}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{R}");
         this.addSuperType(SuperType.LEGENDARY);
         this.subtype.add(SubType.HUMAN);
         this.subtype.add(SubType.SHAMAN);
@@ -62,13 +59,13 @@ public final class KumanoMasterYamabushi extends CardImpl {
         this.power = new MageInt(4);
         this.toughness = new MageInt(4);
 
-        // {{1}{R}: Kumano, Master Yamabushi deals 1 damage to any target.
-        Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(1), new ManaCostsImpl("{1}{R}") );
+        // {1}{R}: Kumano, Master Yamabushi deals 1 damage to any target.
+        Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(1), new ManaCostsImpl("{1}{R}"));
         ability.addTarget(new TargetAnyTarget());
         this.addAbility(ability);
         // If a creature dealt damage by Kumano this turn would die, exile it instead.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DealtDamageToCreatureBySourceDies(this, Duration.WhileOnBattlefield)), new DamagedByWatcher(false));
-        
+
     }
 
     public KumanoMasterYamabushi(final KumanoMasterYamabushi card) {
diff --git a/Mage.Sets/src/mage/cards/o/OrzhovKeyrune.java b/Mage.Sets/src/mage/cards/o/OrzhovKeyrune.java
index debb0f28d1..66d63b8294 100644
--- a/Mage.Sets/src/mage/cards/o/OrzhovKeyrune.java
+++ b/Mage.Sets/src/mage/cards/o/OrzhovKeyrune.java
@@ -1,7 +1,5 @@
-
 package mage.cards.o;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
@@ -12,22 +10,22 @@ import mage.abilities.mana.WhiteManaAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.SubType;
 import mage.constants.Duration;
+import mage.constants.SubType;
 import mage.constants.Zone;
 import mage.game.permanent.token.TokenImpl;
-import mage.game.permanent.token.Token;
+
+import java.util.UUID;
 
 /**
- *
  * @author LevelX2
  */
 public final class OrzhovKeyrune extends CardImpl {
 
     public OrzhovKeyrune(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}");
+        super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}");
 
-        // {{T}: Add {W} or {B}.
+        // {T}: Add {W} or {B}.
         this.addAbility(new WhiteManaAbility());
         this.addAbility(new BlackManaAbility());
 
@@ -56,6 +54,7 @@ public final class OrzhovKeyrune extends CardImpl {
             toughness = new MageInt(4);
             this.addAbility(LifelinkAbility.getInstance());
         }
+
         public OrzhovKeyruneToken(final OrzhovKeyruneToken token) {
             super(token);
         }

From 146c9571ca795e0cefe84b97e56f6af126c052aa Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Tue, 16 Jun 2020 10:42:33 +0200
Subject: [PATCH 301/586] * See the Truth - Fixed some problems of the
 implementation (#6646).

---
 Mage.Sets/src/mage/cards/s/SeeTheTruth.java | 37 ++++++++++-----------
 1 file changed, 18 insertions(+), 19 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/s/SeeTheTruth.java b/Mage.Sets/src/mage/cards/s/SeeTheTruth.java
index 66540a9ae3..d348423e46 100644
--- a/Mage.Sets/src/mage/cards/s/SeeTheTruth.java
+++ b/Mage.Sets/src/mage/cards/s/SeeTheTruth.java
@@ -1,7 +1,6 @@
 package mage.cards.s;
 
 import mage.abilities.Ability;
-import mage.abilities.condition.common.CastFromHandSourceCondition;
 import mage.abilities.effects.OneShotEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -13,9 +12,10 @@ import mage.constants.Zone;
 import mage.game.Game;
 import mage.players.Player;
 import mage.target.common.TargetCardInLibrary;
-import mage.watchers.common.CastFromHandWatcher;
 
 import java.util.UUID;
+import mage.filter.FilterCard;
+import mage.game.stack.Spell;
 
 /**
  * @author TheElk801
@@ -27,7 +27,6 @@ public final class SeeTheTruth extends CardImpl {
 
         // Look at the top three cards of your library. Put one of those cards into your hand and the rest on the bottom of your library in any order. If this spell was cast from anywhere other than your hand, put each of those cards into your hand instead.
         this.getSpellAbility().addEffect(new SeeTheTruthEffect());
-        this.getSpellAbility().addWatcher(new CastFromHandWatcher());
     }
 
     private SeeTheTruth(final SeeTheTruth card) {
@@ -44,10 +43,10 @@ class SeeTheTruthEffect extends OneShotEffect {
 
     SeeTheTruthEffect() {
         super(Outcome.Benefit);
-        staticText = "Look at the top three cards of your library. " +
-                "Put one of those cards into your hand and the rest on the bottom of your library in any order. " +
-                "If this spell was cast from anywhere other than your hand, " +
-                "put each of those cards into your hand instead.";
+        staticText = "Look at the top three cards of your library. "
+                + "Put one of those cards into your hand and the rest on the bottom of your library in any order. "
+                + "If this spell was cast from anywhere other than your hand, "
+                + "put each of those cards into your hand instead.";
     }
 
     private SeeTheTruthEffect(final SeeTheTruthEffect effect) {
@@ -62,21 +61,21 @@ class SeeTheTruthEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player player = game.getPlayer(source.getControllerId());
-        if (player == null) {
+        Spell sourceSpell = game.getStack().getSpell(source.getId()); // Use id to get the correct spell in case of copied spells
+        if (player == null || sourceSpell == null) {
             return false;
         }
         Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, 3));
-        if (cards.size() < 1) {
-            return false;
-        }
-        if (CastFromHandSourceCondition.instance.apply(game, source)) {
-            TargetCardInLibrary target = new TargetCardInLibrary();
-            player.choose(outcome, cards, target, game);
-            cards.removeIf(target.getFirstTarget()::equals);
-            player.moveCards(game.getCard(target.getFirstTarget()), Zone.HAND, source, game);
-            player.putCardsOnBottomOfLibrary(cards, game, source, true);
-        } else {
-            player.moveCards(cards, Zone.HAND, source, game);
+        if (!cards.isEmpty()) {
+            if (sourceSpell.isCopy() || Zone.HAND.equals(sourceSpell.getFromZone())) { // A copied spell was NOT cast at all
+                TargetCardInLibrary target = new TargetCardInLibrary(new FilterCard("card to put into your hand"));
+                player.chooseTarget(outcome, cards, target, source, game);
+                cards.removeIf(target.getFirstTarget()::equals);
+                player.moveCards(game.getCard(target.getFirstTarget()), Zone.HAND, source, game);
+                player.putCardsOnBottomOfLibrary(cards, game, source, true);
+            } else {
+                player.moveCards(cards, Zone.HAND, source, game);
+            }
         }
         return true;
     }

From ff3a51cc22fd40c001919f6fe92d509f652b4c7d Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Tue, 16 Jun 2020 10:42:53 +0200
Subject: [PATCH 302/586] * Some minor code clean up.

---
 .../src/main/java/mage/view/CardView.java     | 34 ++++++++-----------
 .../src/mage/cards/e/EssenceBacklash.java     |  2 +-
 2 files changed, 15 insertions(+), 21 deletions(-)

diff --git a/Mage.Common/src/main/java/mage/view/CardView.java b/Mage.Common/src/main/java/mage/view/CardView.java
index 2ea469997a..4b71867d57 100644
--- a/Mage.Common/src/main/java/mage/view/CardView.java
+++ b/Mage.Common/src/main/java/mage/view/CardView.java
@@ -317,13 +317,6 @@ public class CardView extends SimpleCardView {
             }
         }
 
-        AdventureCard adventureCard = null;
-        AdventureCardSpell adventureCardSpell = null;
-        if (card instanceof AdventureCard) {
-            adventureCard = (AdventureCard) card;
-            adventureCardSpell = (AdventureCardSpell) adventureCard.getSpellCard();
-        }
-
         String fullCardName;
         if (splitCard != null) {
             this.isSplitCard = true;
@@ -339,7 +332,9 @@ public class CardView extends SimpleCardView {
             fullCardName = card.getName(); // split card contains full name as normal
             this.manaCostLeft = splitCard.getLeftHalfCard().getManaCost().getSymbols();
             this.manaCostRight = splitCard.getRightHalfCard().getManaCost().getSymbols();
-        } else if (adventureCard != null) {
+        } else if (card instanceof AdventureCard) {
+            AdventureCard adventureCard = ((AdventureCard) card);
+            AdventureCardSpell adventureCardSpell = ((AdventureCardSpell) adventureCard.getSpellCard());            
             fullCardName = adventureCard.getName() + MockCard.ADVENTURE_NAME_SEPARATOR + adventureCardSpell.getName();
             this.manaCostLeft = adventureCardSpell.getManaCost().getSymbols();
             this.manaCostRight = adventureCard.getManaCost().getSymbols();
@@ -466,7 +461,7 @@ public class CardView extends SimpleCardView {
                 } else if (spell.getCard() != null) {
                     SplitCard wholeCard = ((SplitCardHalf) spell.getCard()).getParentCard();
                     Abilities<Ability> aftermathHalfAbilities = wholeCard.getRightHalfCard().getAbilities(game);
-                    if (aftermathHalfAbilities.stream().anyMatch(ability -> ability instanceof AftermathAbility)) {
+                    if (aftermathHalfAbilities.stream().anyMatch(halfAbility -> halfAbility instanceof AftermathAbility)) {
                         if (ty == SpellAbilityType.SPLIT_RIGHT) {
                             artRect = ArtRect.AFTERMATH_BOTTOM;
                         } else {
@@ -1029,26 +1024,25 @@ public class CardView extends SimpleCardView {
     }
 
     public String getColorText() {
-
-        String color = getColor().getDescription();
-        return color.substring(0, 1).toUpperCase(Locale.ENGLISH) + color.substring(1);
+        String colorText = getColor().getDescription();
+        return colorText.substring(0, 1).toUpperCase(Locale.ENGLISH) + colorText.substring(1);
     }
 
     public String getTypeText() {
-        StringBuilder type = new StringBuilder();
+        StringBuilder typeText = new StringBuilder();
         if (!getSuperTypes().isEmpty()) {
-            type.append(String.join(" ", getSuperTypes().stream().map(SuperType::toString).collect(Collectors.toList())));
-            type.append(" ");
+            typeText.append(String.join(" ", getSuperTypes().stream().map(SuperType::toString).collect(Collectors.toList())));
+            typeText.append(" ");
         }
         if (!getCardTypes().isEmpty()) {
-            type.append(String.join(" ", getCardTypes().stream().map(CardType::toString).collect(Collectors.toList())));
-            type.append(" ");
+            typeText.append(String.join(" ", getCardTypes().stream().map(CardType::toString).collect(Collectors.toList())));
+            typeText.append(" ");
         }
         if (!getSubTypes().isEmpty()) {
-            type.append(" - ");
-            type.append(String.join(" ", getSubTypes().stream().map(SubType::toString).collect(Collectors.toList())));
+            typeText.append(" - ");
+            typeText.append(String.join(" ", getSubTypes().stream().map(SubType::toString).collect(Collectors.toList())));
         }
-        return type.toString();
+        return typeText.toString();
     }
 
     public boolean isLand() {
diff --git a/Mage.Sets/src/mage/cards/e/EssenceBacklash.java b/Mage.Sets/src/mage/cards/e/EssenceBacklash.java
index 0fcc6e2e70..50410118aa 100644
--- a/Mage.Sets/src/mage/cards/e/EssenceBacklash.java
+++ b/Mage.Sets/src/mage/cards/e/EssenceBacklash.java
@@ -41,7 +41,7 @@ class EssenceBacklashEffect extends OneShotEffect {
 
     public EssenceBacklashEffect() {
         super(Outcome.Damage);
-        staticText = "Counter target creature spell. Essence Backlash deals damage equal to that spell's power to its controller";
+        staticText = "Counter target creature spell. {this} deals damage equal to that spell's power to its controller";
     }
 
     public EssenceBacklashEffect(final EssenceBacklashEffect effect) {

From 0e81b8fd78adfd36c091419650e7bf4cc60680e0 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Tue, 16 Jun 2020 10:43:26 +0200
Subject: [PATCH 303/586] * Adding M21 to Scryfall Image support sets.

---
 .../mage/plugins/card/dl/sources/ScryfallImageSupportCards.java  | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java
index e2234547e8..6d5670d7b0 100644
--- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java
@@ -260,6 +260,7 @@ public class ScryfallImageSupportCards {
             add("PPP1");
             add("PF19");
             add("MPS-AKH");
+            add("M21");
         }
     };
 

From 8e1d7c3ffbbf5df1a2169032e89b9949daada1e5 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Tue, 16 Jun 2020 10:53:52 +0200
Subject: [PATCH 304/586] Renamed condition to prevent misuse.

---
 Mage.Sets/src/mage/cards/a/AngelOfTheDireHour.java            | 4 ++--
 Mage.Sets/src/mage/cards/b/BreachingLeviathan.java            | 4 ++--
 Mage.Sets/src/mage/cards/c/CoalStoker.java                    | 4 ++--
 Mage.Sets/src/mage/cards/d/DeathbringerRegent.java            | 4 ++--
 Mage.Sets/src/mage/cards/d/DreadCacodemon.java                | 4 ++--
 Mage.Sets/src/mage/cards/e/Epochrasite.java                   | 4 ++--
 Mage.Sets/src/mage/cards/f/FeastingTrollKing.java             | 4 ++--
 Mage.Sets/src/mage/cards/f/FurnaceDragon.java                 | 4 ++--
 Mage.Sets/src/mage/cards/h/Hypnox.java                        | 4 ++--
 Mage.Sets/src/mage/cards/i/InameAsOne.java                    | 4 ++--
 Mage.Sets/src/mage/cards/m/MyojinOfCleansingFire.java         | 4 ++--
 Mage.Sets/src/mage/cards/m/MyojinOfInfiniteRage.java          | 4 ++--
 Mage.Sets/src/mage/cards/m/MyojinOfLifesWeb.java              | 4 ++--
 Mage.Sets/src/mage/cards/m/MyojinOfNightsReach.java           | 4 ++--
 Mage.Sets/src/mage/cards/m/MyojinOfSeeingWinds.java           | 4 ++--
 Mage.Sets/src/mage/cards/p/PhageTheUntouchable.java           | 4 ++--
 Mage.Sets/src/mage/cards/r/ReiverDemon.java                   | 4 ++--
 Mage.Sets/src/mage/cards/s/ScionOfVituGhazi.java              | 4 ++--
 Mage.Sets/src/mage/cards/w/WakeningSunsAvatar.java            | 4 ++--
 ...ndition.java => CastFromHandSourcePermanentCondition.java} | 2 +-
 20 files changed, 39 insertions(+), 39 deletions(-)
 rename Mage/src/main/java/mage/abilities/condition/common/{CastFromHandSourceCondition.java => CastFromHandSourcePermanentCondition.java} (95%)

diff --git a/Mage.Sets/src/mage/cards/a/AngelOfTheDireHour.java b/Mage.Sets/src/mage/cards/a/AngelOfTheDireHour.java
index 2b3826169c..e02c3656b3 100644
--- a/Mage.Sets/src/mage/cards/a/AngelOfTheDireHour.java
+++ b/Mage.Sets/src/mage/cards/a/AngelOfTheDireHour.java
@@ -4,7 +4,7 @@ package mage.cards.a;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
-import mage.abilities.condition.common.CastFromHandSourceCondition;
+import mage.abilities.condition.common.CastFromHandSourcePermanentCondition;
 import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
 import mage.abilities.effects.common.ExileAllEffect;
 import mage.abilities.keyword.FlashAbility;
@@ -36,7 +36,7 @@ public final class AngelOfTheDireHour extends CardImpl {
         // When Angel of the Dire Hour enters the battlefield, if you cast it from your hand, exile all attacking creatures.
         this.addAbility(new ConditionalInterveningIfTriggeredAbility(
                 new EntersBattlefieldTriggeredAbility(new ExileAllEffect(new FilterAttackingCreature("attacking creatures")), false),
-                CastFromHandSourceCondition.instance,
+                CastFromHandSourcePermanentCondition.instance,
                 "When {this} enters the battlefield, if you cast it from your hand, exile all attacking creatures."),
                 new CastFromHandWatcher());
     }
diff --git a/Mage.Sets/src/mage/cards/b/BreachingLeviathan.java b/Mage.Sets/src/mage/cards/b/BreachingLeviathan.java
index 3171564a79..2c5230f397 100644
--- a/Mage.Sets/src/mage/cards/b/BreachingLeviathan.java
+++ b/Mage.Sets/src/mage/cards/b/BreachingLeviathan.java
@@ -8,7 +8,7 @@ import mage.MageInt;
 import mage.ObjectColor;
 import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
-import mage.abilities.condition.common.CastFromHandSourceCondition;
+import mage.abilities.condition.common.CastFromHandSourcePermanentCondition;
 import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
 import mage.abilities.effects.ContinuousEffect;
 import mage.abilities.effects.OneShotEffect;
@@ -42,7 +42,7 @@ public final class BreachingLeviathan extends CardImpl {
         // When Breaching Leviathan enters the battlefield, if you cast it from your hand, tap all nonblue creatures. Those creatures don't untap during their controllers' next untap steps.
         this.addAbility(new ConditionalInterveningIfTriggeredAbility(
                 new EntersBattlefieldTriggeredAbility(new BreachingLeviathanEffect(), false),
-                CastFromHandSourceCondition.instance,
+                CastFromHandSourcePermanentCondition.instance,
                 "When {this} enters the battlefield, if you cast it from your hand, tap all nonblue creatures. Those creatures don't untap during their controllers' next untap steps."),
                 new CastFromHandWatcher());
     }
diff --git a/Mage.Sets/src/mage/cards/c/CoalStoker.java b/Mage.Sets/src/mage/cards/c/CoalStoker.java
index 17ead83cd5..26787c90d6 100644
--- a/Mage.Sets/src/mage/cards/c/CoalStoker.java
+++ b/Mage.Sets/src/mage/cards/c/CoalStoker.java
@@ -5,7 +5,7 @@ import java.util.UUID;
 import mage.MageInt;
 import mage.Mana;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
-import mage.abilities.condition.common.CastFromHandSourceCondition;
+import mage.abilities.condition.common.CastFromHandSourcePermanentCondition;
 import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
 import mage.abilities.effects.mana.BasicManaEffect;
 import mage.cards.CardImpl;
@@ -29,7 +29,7 @@ public final class CoalStoker extends CardImpl {
         // When Coal Stoker enters the battlefield, if you cast it from your hand, add {R}{R}{R}.
         this.addAbility(new ConditionalInterveningIfTriggeredAbility(
                 new EntersBattlefieldTriggeredAbility(new BasicManaEffect(Mana.RedMana(3)), false),
-                CastFromHandSourceCondition.instance,
+                CastFromHandSourcePermanentCondition.instance,
                 "When {this} enters the battlefield, if you cast it from your hand, add {R}{R}{R}."),
                 new CastFromHandWatcher());
     }
diff --git a/Mage.Sets/src/mage/cards/d/DeathbringerRegent.java b/Mage.Sets/src/mage/cards/d/DeathbringerRegent.java
index 023015b9e0..d306c5e7a1 100644
--- a/Mage.Sets/src/mage/cards/d/DeathbringerRegent.java
+++ b/Mage.Sets/src/mage/cards/d/DeathbringerRegent.java
@@ -6,7 +6,7 @@ import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.condition.Condition;
-import mage.abilities.condition.common.CastFromHandSourceCondition;
+import mage.abilities.condition.common.CastFromHandSourcePermanentCondition;
 import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
 import mage.abilities.effects.common.DestroyAllEffect;
 import mage.abilities.keyword.FlyingAbility;
@@ -63,7 +63,7 @@ class DeathbringerRegentCondition implements Condition {
 
     @Override
     public boolean apply(Game game, Ability source) {
-        return CastFromHandSourceCondition.instance.apply(game, source)
+        return CastFromHandSourcePermanentCondition.instance.apply(game, source)
                 && game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, game).size() >= 6;
     }
 }
diff --git a/Mage.Sets/src/mage/cards/d/DreadCacodemon.java b/Mage.Sets/src/mage/cards/d/DreadCacodemon.java
index cb2b37606c..ab005dde42 100644
--- a/Mage.Sets/src/mage/cards/d/DreadCacodemon.java
+++ b/Mage.Sets/src/mage/cards/d/DreadCacodemon.java
@@ -5,7 +5,7 @@ import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.TriggeredAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
-import mage.abilities.condition.common.CastFromHandSourceCondition;
+import mage.abilities.condition.common.CastFromHandSourcePermanentCondition;
 import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
 import mage.abilities.effects.common.DestroyAllEffect;
 import mage.abilities.effects.common.TapAllEffect;
@@ -44,7 +44,7 @@ public final class DreadCacodemon extends CardImpl {
         // if you cast it from your hand, destroy all creatures your opponents control, then tap all other creatures you control. 
         TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new DestroyAllEffect(opponentsCreatures, false));
         ability.addEffect(new TapAllEffect(otherCreaturesYouControl));
-        this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, CastFromHandSourceCondition.instance,
+        this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, CastFromHandSourcePermanentCondition.instance,
                 "When {this} enters the battlefield, if you cast it from your hand, destroy all creatures your opponents control, then tap all other creatures you control."), new CastFromHandWatcher());
     }
 
diff --git a/Mage.Sets/src/mage/cards/e/Epochrasite.java b/Mage.Sets/src/mage/cards/e/Epochrasite.java
index 439e042ddf..96a70cb2b2 100644
--- a/Mage.Sets/src/mage/cards/e/Epochrasite.java
+++ b/Mage.Sets/src/mage/cards/e/Epochrasite.java
@@ -8,7 +8,7 @@ import mage.abilities.Ability;
 import mage.abilities.common.DiesTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldAbility;
 import mage.abilities.condition.InvertCondition;
-import mage.abilities.condition.common.CastFromHandSourceCondition;
+import mage.abilities.condition.common.CastFromHandSourcePermanentCondition;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.continuous.GainSuspendEffect;
 import mage.abilities.effects.common.counter.AddCountersSourceEffect;
@@ -41,7 +41,7 @@ public final class Epochrasite extends CardImpl {
         // Epochrasite enters the battlefield with three +1/+1 counters on it if you didn't cast it from your hand.
         this.addAbility(new EntersBattlefieldAbility(
                     new AddCountersSourceEffect(CounterType.P1P1.createInstance(3)),
-                    new InvertCondition(CastFromHandSourceCondition.instance),
+                    new InvertCondition(CastFromHandSourcePermanentCondition.instance),
                     "{this} enters the battlefield with three +1/+1 counters on it if you didn't cast it from your hand",""), 
                 new CastFromHandWatcher());
 
diff --git a/Mage.Sets/src/mage/cards/f/FeastingTrollKing.java b/Mage.Sets/src/mage/cards/f/FeastingTrollKing.java
index af752d5c84..5e021705ba 100644
--- a/Mage.Sets/src/mage/cards/f/FeastingTrollKing.java
+++ b/Mage.Sets/src/mage/cards/f/FeastingTrollKing.java
@@ -3,7 +3,7 @@ package mage.cards.f;
 import mage.MageInt;
 import mage.abilities.common.ActivateIfConditionActivatedAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
-import mage.abilities.condition.common.CastFromHandSourceCondition;
+import mage.abilities.condition.common.CastFromHandSourcePermanentCondition;
 import mage.abilities.condition.common.MyTurnCondition;
 import mage.abilities.costs.common.SacrificeTargetCost;
 import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
@@ -48,7 +48,7 @@ public final class FeastingTrollKing extends CardImpl {
         // When Feasting Troll King enters the battlefield, if you cast it from your hand, create three Food tokens.
         this.addAbility(new ConditionalInterveningIfTriggeredAbility(
                 new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new FoodToken(), 3)),
-                CastFromHandSourceCondition.instance, "When {this} enters the battlefield, " +
+                CastFromHandSourcePermanentCondition.instance, "When {this} enters the battlefield, " +
                 "if you cast it from your hand, create three Food tokens."
         ), new CastFromHandWatcher());
 
diff --git a/Mage.Sets/src/mage/cards/f/FurnaceDragon.java b/Mage.Sets/src/mage/cards/f/FurnaceDragon.java
index 8f508a93bb..92a267b0db 100644
--- a/Mage.Sets/src/mage/cards/f/FurnaceDragon.java
+++ b/Mage.Sets/src/mage/cards/f/FurnaceDragon.java
@@ -4,7 +4,7 @@ package mage.cards.f;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
-import mage.abilities.condition.common.CastFromHandSourceCondition;
+import mage.abilities.condition.common.CastFromHandSourcePermanentCondition;
 import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
 import mage.abilities.effects.common.ExileAllEffect;
 import mage.abilities.keyword.AffinityForArtifactsAbility;
@@ -43,7 +43,7 @@ public final class FurnaceDragon extends CardImpl {
         // When Furnace Dragon enters the battlefield, if you cast it from your hand, exile all artifacts.
         this.addAbility(new ConditionalInterveningIfTriggeredAbility(
                 new EntersBattlefieldTriggeredAbility(new ExileAllEffect(filter), false),
-                CastFromHandSourceCondition.instance,
+                CastFromHandSourcePermanentCondition.instance,
                 "When {this} enters the battlefield, if you cast it from your hand, exile all artifacts."),
                 new CastFromHandWatcher());
     }
diff --git a/Mage.Sets/src/mage/cards/h/Hypnox.java b/Mage.Sets/src/mage/cards/h/Hypnox.java
index 41e0be205e..a9fd985565 100644
--- a/Mage.Sets/src/mage/cards/h/Hypnox.java
+++ b/Mage.Sets/src/mage/cards/h/Hypnox.java
@@ -7,7 +7,7 @@ import mage.abilities.Ability;
 import mage.abilities.TriggeredAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.common.LeavesBattlefieldTriggeredAbility;
-import mage.abilities.condition.common.CastFromHandSourceCondition;
+import mage.abilities.condition.common.CastFromHandSourcePermanentCondition;
 import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.keyword.FlyingAbility;
@@ -43,7 +43,7 @@ public final class Hypnox extends CardImpl {
         // When Hypnox enters the battlefield, if you cast it from your hand, exile all cards from target opponent's hand.
         TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new HypnoxExileEffect());
         ability.addTarget(new TargetOpponent());
-        this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, CastFromHandSourceCondition.instance,
+        this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, CastFromHandSourcePermanentCondition.instance,
                 "When {this} enters the battlefield, if you cast it from your hand, exile all cards from target opponent's hand."), new CastFromHandWatcher());
 
         // When Hypnox leaves the battlefield, return the exiled cards to their owner's hand.
diff --git a/Mage.Sets/src/mage/cards/i/InameAsOne.java b/Mage.Sets/src/mage/cards/i/InameAsOne.java
index ba36e99e3c..7009a9b60f 100644
--- a/Mage.Sets/src/mage/cards/i/InameAsOne.java
+++ b/Mage.Sets/src/mage/cards/i/InameAsOne.java
@@ -7,7 +7,7 @@ import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.common.DiesTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
-import mage.abilities.condition.common.CastFromHandSourceCondition;
+import mage.abilities.condition.common.CastFromHandSourcePermanentCondition;
 import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.OneShotEffect;
@@ -51,7 +51,7 @@ public final class InameAsOne extends CardImpl {
         // When Iname as One enters the battlefield, if you cast it from your hand, you may search your library for a Spirit permanent card, put it onto the battlefield, then shuffle your library.
         this.addAbility(new ConditionalInterveningIfTriggeredAbility(
                 new EntersBattlefieldTriggeredAbility(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0, 1, filter)), true),
-                CastFromHandSourceCondition.instance,
+                CastFromHandSourcePermanentCondition.instance,
                 "When {this} enters the battlefield, if you cast it from your hand, you may search your library for a Spirit permanent card, put it onto the battlefield, then shuffle your library."),
                 new CastFromHandWatcher());
 
diff --git a/Mage.Sets/src/mage/cards/m/MyojinOfCleansingFire.java b/Mage.Sets/src/mage/cards/m/MyojinOfCleansingFire.java
index 6bad3165ae..f3da7615d1 100644
--- a/Mage.Sets/src/mage/cards/m/MyojinOfCleansingFire.java
+++ b/Mage.Sets/src/mage/cards/m/MyojinOfCleansingFire.java
@@ -6,7 +6,7 @@ import mage.MageInt;
 import mage.abilities.common.EntersBattlefieldAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.common.SimpleStaticAbility;
-import mage.abilities.condition.common.CastFromHandSourceCondition;
+import mage.abilities.condition.common.CastFromHandSourcePermanentCondition;
 import mage.abilities.condition.common.SourceHasCounterCondition;
 import mage.abilities.costs.common.RemoveCountersSourceCost;
 import mage.abilities.decorator.ConditionalContinuousEffect;
@@ -48,7 +48,7 @@ public final class MyojinOfCleansingFire extends CardImpl {
         this.getSpellAbility().addWatcher(new CastFromHandWatcher());
 
         // Myojin of Cleansing Fire enters the battlefield with a divinity counter on it if you cast it from your hand.
-        this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.DIVINITY.createInstance()), CastFromHandSourceCondition.instance, ""), "{this} enters the battlefield with a divinity counter on it if you cast it from your hand"));
+        this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.DIVINITY.createInstance()), CastFromHandSourcePermanentCondition.instance, ""), "{this} enters the battlefield with a divinity counter on it if you cast it from your hand"));
         // Myojin of Cleansing Fire is indestructible as long as it has a divinity counter on it.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(new GainAbilitySourceEffect(IndestructibleAbility.getInstance(), Duration.WhileOnBattlefield),
                 new SourceHasCounterCondition(CounterType.DIVINITY), "{this} is indestructible as long as it has a divinity counter on it")));
diff --git a/Mage.Sets/src/mage/cards/m/MyojinOfInfiniteRage.java b/Mage.Sets/src/mage/cards/m/MyojinOfInfiniteRage.java
index 0c664bb6c5..f217d6ca29 100644
--- a/Mage.Sets/src/mage/cards/m/MyojinOfInfiniteRage.java
+++ b/Mage.Sets/src/mage/cards/m/MyojinOfInfiniteRage.java
@@ -6,7 +6,7 @@ import mage.MageInt;
 import mage.abilities.common.EntersBattlefieldAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.common.SimpleStaticAbility;
-import mage.abilities.condition.common.CastFromHandSourceCondition;
+import mage.abilities.condition.common.CastFromHandSourcePermanentCondition;
 import mage.abilities.condition.common.SourceHasCounterCondition;
 import mage.abilities.costs.common.RemoveCountersSourceCost;
 import mage.abilities.decorator.ConditionalContinuousEffect;
@@ -42,7 +42,7 @@ public final class MyojinOfInfiniteRage extends CardImpl {
         this.getSpellAbility().addWatcher(new CastFromHandWatcher());
 
         // Myojin of Infinite Rage enters the battlefield with a divinity counter on it if you cast it from your hand.
-        this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.DIVINITY.createInstance()), CastFromHandSourceCondition.instance, ""), "{this} enters the battlefield with a divinity counter on it if you cast it from your hand"));
+        this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.DIVINITY.createInstance()), CastFromHandSourcePermanentCondition.instance, ""), "{this} enters the battlefield with a divinity counter on it if you cast it from your hand"));
         // Myojin of Infinite Rage is indestructible as long as it has a divinity counter on it.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(new GainAbilitySourceEffect(IndestructibleAbility.getInstance(), Duration.WhileOnBattlefield),
                 new SourceHasCounterCondition(CounterType.DIVINITY), "{this} is indestructible as long as it has a divinity counter on it")));
diff --git a/Mage.Sets/src/mage/cards/m/MyojinOfLifesWeb.java b/Mage.Sets/src/mage/cards/m/MyojinOfLifesWeb.java
index a9a628b5ed..3f5403b25e 100644
--- a/Mage.Sets/src/mage/cards/m/MyojinOfLifesWeb.java
+++ b/Mage.Sets/src/mage/cards/m/MyojinOfLifesWeb.java
@@ -7,7 +7,7 @@ import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.common.SimpleStaticAbility;
-import mage.abilities.condition.common.CastFromHandSourceCondition;
+import mage.abilities.condition.common.CastFromHandSourcePermanentCondition;
 import mage.abilities.condition.common.SourceHasCounterCondition;
 import mage.abilities.costs.common.RemoveCountersSourceCost;
 import mage.abilities.decorator.ConditionalContinuousEffect;
@@ -43,7 +43,7 @@ public final class MyojinOfLifesWeb extends CardImpl {
         this.getSpellAbility().addWatcher(new CastFromHandWatcher());
 
         // Myojin of Life's Web enters the battlefield with a divinity counter on it if you cast it from your hand.
-        this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.DIVINITY.createInstance()), CastFromHandSourceCondition.instance, ""), "{this} enters the battlefield with a divinity counter on it if you cast it from your hand"));
+        this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.DIVINITY.createInstance()), CastFromHandSourcePermanentCondition.instance, ""), "{this} enters the battlefield with a divinity counter on it if you cast it from your hand"));
         // Myojin of Life's Web is indestructible as long as it has a divinity counter on it.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
                 new ConditionalContinuousEffect(new GainAbilitySourceEffect(IndestructibleAbility.getInstance(), Duration.WhileOnBattlefield),
diff --git a/Mage.Sets/src/mage/cards/m/MyojinOfNightsReach.java b/Mage.Sets/src/mage/cards/m/MyojinOfNightsReach.java
index 559c409e51..dee0e16457 100644
--- a/Mage.Sets/src/mage/cards/m/MyojinOfNightsReach.java
+++ b/Mage.Sets/src/mage/cards/m/MyojinOfNightsReach.java
@@ -5,7 +5,7 @@ import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.common.SimpleStaticAbility;
-import mage.abilities.condition.common.CastFromHandSourceCondition;
+import mage.abilities.condition.common.CastFromHandSourcePermanentCondition;
 import mage.abilities.condition.common.SourceHasCounterCondition;
 import mage.abilities.costs.common.RemoveCountersSourceCost;
 import mage.abilities.decorator.ConditionalContinuousEffect;
@@ -40,7 +40,7 @@ public final class MyojinOfNightsReach extends CardImpl {
         this.getSpellAbility().addWatcher(new CastFromHandWatcher());
 
         // Myojin of Night's Reach enters the battlefield with a divinity counter on it if you cast it from your hand.
-        this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.DIVINITY.createInstance()), CastFromHandSourceCondition.instance, ""), "{this} enters the battlefield with a divinity counter on it if you cast it from your hand"));
+        this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.DIVINITY.createInstance()), CastFromHandSourcePermanentCondition.instance, ""), "{this} enters the battlefield with a divinity counter on it if you cast it from your hand"));
         // Myojin of Night's Reach is indestructible as long as it has a divinity counter on it.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(new GainAbilitySourceEffect(IndestructibleAbility.getInstance(), Duration.WhileOnBattlefield),
                 new SourceHasCounterCondition(CounterType.DIVINITY), "{this} is indestructible as long as it has a divinity counter on it")));
diff --git a/Mage.Sets/src/mage/cards/m/MyojinOfSeeingWinds.java b/Mage.Sets/src/mage/cards/m/MyojinOfSeeingWinds.java
index a2ffdec473..319780ad6b 100644
--- a/Mage.Sets/src/mage/cards/m/MyojinOfSeeingWinds.java
+++ b/Mage.Sets/src/mage/cards/m/MyojinOfSeeingWinds.java
@@ -7,7 +7,7 @@ import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.common.SimpleStaticAbility;
-import mage.abilities.condition.common.CastFromHandSourceCondition;
+import mage.abilities.condition.common.CastFromHandSourcePermanentCondition;
 import mage.abilities.condition.common.SourceHasCounterCondition;
 import mage.abilities.costs.common.RemoveCountersSourceCost;
 import mage.abilities.decorator.ConditionalContinuousEffect;
@@ -45,7 +45,7 @@ public final class MyojinOfSeeingWinds extends CardImpl {
         this.getSpellAbility().addWatcher(new CastFromHandWatcher());
 
         // Myojin of Seeing Winds enters the battlefield with a divinity counter on it if you cast it from your hand.
-        this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.DIVINITY.createInstance()), CastFromHandSourceCondition.instance, ""), "{this} enters the battlefield with a divinity counter on it if you cast it from your hand"));
+        this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.DIVINITY.createInstance()), CastFromHandSourcePermanentCondition.instance, ""), "{this} enters the battlefield with a divinity counter on it if you cast it from your hand"));
         // Myojin of Seeing Winds is indestructible as long as it has a divinity counter on it.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(new GainAbilitySourceEffect(IndestructibleAbility.getInstance(), Duration.WhileOnBattlefield),
                 new SourceHasCounterCondition(CounterType.DIVINITY), "{this} is indestructible as long as it has a divinity counter on it")));
diff --git a/Mage.Sets/src/mage/cards/p/PhageTheUntouchable.java b/Mage.Sets/src/mage/cards/p/PhageTheUntouchable.java
index e1bc4cafc8..db9dec6f2b 100644
--- a/Mage.Sets/src/mage/cards/p/PhageTheUntouchable.java
+++ b/Mage.Sets/src/mage/cards/p/PhageTheUntouchable.java
@@ -7,7 +7,7 @@ import mage.abilities.common.DealsCombatDamageToACreatureTriggeredAbility;
 import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.condition.InvertCondition;
-import mage.abilities.condition.common.CastFromHandSourceCondition;
+import mage.abilities.condition.common.CastFromHandSourcePermanentCondition;
 import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
 import mage.abilities.effects.common.DestroyTargetEffect;
 import mage.abilities.effects.common.LoseGameSourceControllerEffect;
@@ -37,7 +37,7 @@ public final class PhageTheUntouchable extends CardImpl {
         // When Phage the Untouchable enters the battlefield, if you didn't cast it from your hand, you lose the game.
         this.addAbility(new ConditionalInterveningIfTriggeredAbility(
                 new EntersBattlefieldTriggeredAbility(new LoseGameSourceControllerEffect(), false),
-                new InvertCondition(CastFromHandSourceCondition.instance),
+                new InvertCondition(CastFromHandSourcePermanentCondition.instance),
                 "When {this} enters the battlefield, if you didn't cast it from your hand, you lose the game"
         ), new CastFromHandWatcher());
 
diff --git a/Mage.Sets/src/mage/cards/r/ReiverDemon.java b/Mage.Sets/src/mage/cards/r/ReiverDemon.java
index 695f4151a0..71167330d3 100644
--- a/Mage.Sets/src/mage/cards/r/ReiverDemon.java
+++ b/Mage.Sets/src/mage/cards/r/ReiverDemon.java
@@ -5,7 +5,7 @@ import java.util.UUID;
 import mage.MageInt;
 import mage.ObjectColor;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
-import mage.abilities.condition.common.CastFromHandSourceCondition;
+import mage.abilities.condition.common.CastFromHandSourcePermanentCondition;
 import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
 import mage.abilities.effects.common.DestroyAllEffect;
 import mage.abilities.keyword.FlyingAbility;
@@ -43,7 +43,7 @@ public final class ReiverDemon extends CardImpl {
         // When Reiver Demon enters the battlefield, if you cast it from your hand, destroy all nonartifact, nonblack creatures. They can't be regenerated.
         this.addAbility(new ConditionalInterveningIfTriggeredAbility(
                 new EntersBattlefieldTriggeredAbility(new DestroyAllEffect(filter, true), false),
-                CastFromHandSourceCondition.instance,
+                CastFromHandSourcePermanentCondition.instance,
                 "When {this} enters the battlefield, if you cast it from your hand, destroy all nonartifact, nonblack creatures. They can't be regenerated."),
                 new CastFromHandWatcher());
     }
diff --git a/Mage.Sets/src/mage/cards/s/ScionOfVituGhazi.java b/Mage.Sets/src/mage/cards/s/ScionOfVituGhazi.java
index 35d37c2cee..0d52f4ecf3 100644
--- a/Mage.Sets/src/mage/cards/s/ScionOfVituGhazi.java
+++ b/Mage.Sets/src/mage/cards/s/ScionOfVituGhazi.java
@@ -5,7 +5,7 @@ import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.TriggeredAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
-import mage.abilities.condition.common.CastFromHandSourceCondition;
+import mage.abilities.condition.common.CastFromHandSourcePermanentCondition;
 import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.abilities.effects.common.PopulateEffect;
@@ -32,7 +32,7 @@ public final class ScionOfVituGhazi extends CardImpl {
         //When Scion of Vitu-Ghazi enters the battlefield, if you cast it from your hand, create a 1/1 white Bird creature token with flying, then populate.
         TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new BirdToken()), false);
         ability.addEffect(new PopulateEffect("then"));
-        this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, CastFromHandSourceCondition.instance,
+        this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, CastFromHandSourcePermanentCondition.instance,
                 "When {this} enters the battlefield, if you cast it from your hand, create a 1/1 white Bird creature token with flying, then populate."),
                 new CastFromHandWatcher());
     }
diff --git a/Mage.Sets/src/mage/cards/w/WakeningSunsAvatar.java b/Mage.Sets/src/mage/cards/w/WakeningSunsAvatar.java
index c80a09564e..2216a53e5f 100644
--- a/Mage.Sets/src/mage/cards/w/WakeningSunsAvatar.java
+++ b/Mage.Sets/src/mage/cards/w/WakeningSunsAvatar.java
@@ -4,7 +4,7 @@ package mage.cards.w;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
-import mage.abilities.condition.common.CastFromHandSourceCondition;
+import mage.abilities.condition.common.CastFromHandSourcePermanentCondition;
 import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
 import mage.abilities.effects.common.DestroyAllEffect;
 import mage.cards.CardImpl;
@@ -38,7 +38,7 @@ public final class WakeningSunsAvatar extends CardImpl {
         // When Wakening Sun's Avatar enters the battlefield, if you cast it from you hand, destroy all non-Dinosaur creatures.
         this.addAbility(new ConditionalInterveningIfTriggeredAbility(
                 new EntersBattlefieldTriggeredAbility(new DestroyAllEffect(filter), false),
-                CastFromHandSourceCondition.instance,
+                CastFromHandSourcePermanentCondition.instance,
                 "When {this} enters the battlefield, if you cast it from your hand, destroy all non-Dinosaur creatures."),
                 new CastFromHandWatcher());
     }
diff --git a/Mage/src/main/java/mage/abilities/condition/common/CastFromHandSourceCondition.java b/Mage/src/main/java/mage/abilities/condition/common/CastFromHandSourcePermanentCondition.java
similarity index 95%
rename from Mage/src/main/java/mage/abilities/condition/common/CastFromHandSourceCondition.java
rename to Mage/src/main/java/mage/abilities/condition/common/CastFromHandSourcePermanentCondition.java
index 1a584533ec..e65dc1f59a 100644
--- a/Mage/src/main/java/mage/abilities/condition/common/CastFromHandSourceCondition.java
+++ b/Mage/src/main/java/mage/abilities/condition/common/CastFromHandSourcePermanentCondition.java
@@ -13,7 +13,7 @@ import mage.watchers.common.CastFromHandWatcher;
  *
  * @author Loki
  */
-public enum CastFromHandSourceCondition implements Condition {
+public enum CastFromHandSourcePermanentCondition implements Condition {
 
     instance;
 

From 8deebbc6d389bd36062479f5b788caf1b0c39bdd Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Tue, 16 Jun 2020 12:33:07 +0200
Subject: [PATCH 305/586] * Wildwood Scourge - Fixed the missing check for "non
 Hydra" creatures.

---
 Mage.Sets/src/mage/cards/w/WildwoodScourge.java | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/Mage.Sets/src/mage/cards/w/WildwoodScourge.java b/Mage.Sets/src/mage/cards/w/WildwoodScourge.java
index 71ed9a279f..737b4d5b49 100644
--- a/Mage.Sets/src/mage/cards/w/WildwoodScourge.java
+++ b/Mage.Sets/src/mage/cards/w/WildwoodScourge.java
@@ -76,6 +76,7 @@ class EnduringScalelordTriggeredAbility extends TriggeredAbilityImpl {
             return (permanent != null
                     && !event.getTargetId().equals(this.getSourceId())
                     && permanent.isCreature()
+                    && !permanent.getSubtype(game).contains(SubType.HYDRA)
                     && permanent.isControlledBy(this.getControllerId()));
         }
         return false;
@@ -83,6 +84,6 @@ class EnduringScalelordTriggeredAbility extends TriggeredAbilityImpl {
 
     @Override
     public String getRule() {
-        return "Whenever one or more +1/+1 counters are put on another creature you control, put a +1/+1 counter on {this}.";
+        return "Whenever one or more +1/+1 counters are put on another non-Hydra creature you control, put a +1/+1 counter on {this}.";
     }
 }

From 5cf1aaf78bb19672d74be78f6017f3d1c6d5c7b5 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Tue, 16 Jun 2020 14:41:46 +0400
Subject: [PATCH 306/586] [M21] Feline Sovereign - fixed that it gives
 protection to itself (#6646);

---
 .../src/mage/cards/f/FelineSovereign.java     | 34 +++++++++----------
 1 file changed, 17 insertions(+), 17 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/f/FelineSovereign.java b/Mage.Sets/src/mage/cards/f/FelineSovereign.java
index 113cf3bb74..05b7397a45 100644
--- a/Mage.Sets/src/mage/cards/f/FelineSovereign.java
+++ b/Mage.Sets/src/mage/cards/f/FelineSovereign.java
@@ -1,9 +1,5 @@
-
 package mage.cards.f;
 
-import java.util.HashSet;
-import java.util.Set;
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.TriggeredAbilityImpl;
@@ -15,11 +11,7 @@ import mage.abilities.effects.common.continuous.GainAbilityAllEffect;
 import mage.abilities.keyword.ProtectionAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.CardType;
-import mage.constants.Duration;
-import mage.constants.SubType;
-import mage.constants.TargetController;
-import mage.constants.Zone;
+import mage.constants.*;
 import mage.filter.FilterCard;
 import mage.filter.common.FilterArtifactOrEnchantmentPermanent;
 import mage.filter.common.FilterCreaturePermanent;
@@ -31,33 +23,40 @@ import mage.game.events.GameEvent.EventType;
 import mage.game.permanent.Permanent;
 import mage.target.TargetPermanent;
 
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
+
 public final class FelineSovereign extends CardImpl {
 
     private static final FilterCreaturePermanent filterCat = new FilterCreaturePermanent("Cats");
+
     static {
         filterCat.add(SubType.CAT.getPredicate());
         filterCat.add(TargetController.YOU.getControllerPredicate());
     }
 
     private static final FilterCard filterProtectionFromDogs = new FilterCard("Dogs");
+
     static {
         filterProtectionFromDogs.add(SubType.DOG.getPredicate());
     }
 
     public FelineSovereign(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{G}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}");
         this.subtype.add(SubType.CAT);
         this.power = new MageInt(2);
         this.toughness = new MageInt(3);
 
-        // Other cats get +1/+1
+        // Other Cats you control get +1/+1 and have protection from Dogs.
         Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield, filterCat, true));
-        // Cats you control have protection from Dogs
-        Effect effect = new GainAbilityAllEffect(new ProtectionAbility(filterProtectionFromDogs), Duration.WhileOnBattlefield, filterCat);
+        //
+        Effect effect = new GainAbilityAllEffect(new ProtectionAbility(filterProtectionFromDogs), Duration.WhileOnBattlefield, filterCat, true);
         effect.setText("and have protection from Dogs");
         ability.addEffect(effect);
         this.addAbility(ability);
-        // if a cat deals combat damage to a player you may destroy up to one artifact/enchantment
+
+        // Whenever one or more Cats you control deal combat damage to a player, destroy up to one target artifact or enchantment that player controls.
         this.addAbility(new FelineSovereignTriggeredAbility());
     }
 
@@ -74,12 +73,13 @@ public final class FelineSovereign extends CardImpl {
 class FelineSovereignTriggeredAbility extends TriggeredAbilityImpl {
 
     private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Cat you control");
+
     static {
         filter.add(TargetController.YOU.getControllerPredicate());
         filter.add(SubType.CAT.getPredicate());
     }
-    
-    private Set<UUID> damagedPlayerIds = new HashSet<>();
+
+    private final Set<UUID> damagedPlayerIds = new HashSet<>();
 
     public FelineSovereignTriggeredAbility() {
         super(Zone.BATTLEFIELD, new DestroyTargetEffect(), true);
@@ -113,7 +113,7 @@ class FelineSovereignTriggeredAbility extends TriggeredAbilityImpl {
                 this.getTargets().clear();
                 FilterArtifactOrEnchantmentPermanent filter = new FilterArtifactOrEnchantmentPermanent();
                 filter.add(new ControllerIdPredicate(event.getPlayerId()));
-                this.addTarget(new TargetPermanent(0, 1, filter, false)); 
+                this.addTarget(new TargetPermanent(0, 1, filter, false));
                 return true;
             }
         }

From 55b38ad2750a4992f6d8f73cb101622e658ccf13 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Tue, 16 Jun 2020 14:47:31 +0400
Subject: [PATCH 307/586] [M21] Feline Sovereign - removed redundant dialog
 (#6646);

---
 Mage.Sets/src/mage/cards/f/FelineSovereign.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Mage.Sets/src/mage/cards/f/FelineSovereign.java b/Mage.Sets/src/mage/cards/f/FelineSovereign.java
index 05b7397a45..7b84e45e8b 100644
--- a/Mage.Sets/src/mage/cards/f/FelineSovereign.java
+++ b/Mage.Sets/src/mage/cards/f/FelineSovereign.java
@@ -82,7 +82,7 @@ class FelineSovereignTriggeredAbility extends TriggeredAbilityImpl {
     private final Set<UUID> damagedPlayerIds = new HashSet<>();
 
     public FelineSovereignTriggeredAbility() {
-        super(Zone.BATTLEFIELD, new DestroyTargetEffect(), true);
+        super(Zone.BATTLEFIELD, new DestroyTargetEffect(), false);
     }
 
     public FelineSovereignTriggeredAbility(final FelineSovereignTriggeredAbility ability) {

From d2f20e3924e0f5110c0c5273ee83645526d37cd7 Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Tue, 16 Jun 2020 11:11:46 -0500
Subject: [PATCH 308/586] - Fixed #6632

---
 Mage.Sets/src/mage/cards/t/Torchling.java | 31 +++++++++++------------
 1 file changed, 15 insertions(+), 16 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/t/Torchling.java b/Mage.Sets/src/mage/cards/t/Torchling.java
index 2de726dc71..0cae200ef7 100644
--- a/Mage.Sets/src/mage/cards/t/Torchling.java
+++ b/Mage.Sets/src/mage/cards/t/Torchling.java
@@ -1,7 +1,6 @@
 package mage.cards.t;
 
 import mage.MageInt;
-import mage.MageItem;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.mana.ColoredManaCost;
@@ -24,11 +23,8 @@ import mage.game.stack.StackObject;
 import mage.target.Target;
 import mage.target.TargetSpell;
 import mage.target.common.TargetCreaturePermanent;
-
-import java.util.Collection;
-import java.util.Objects;
 import java.util.UUID;
-import java.util.stream.Stream;
+import mage.abilities.Mode;
 
 /**
  * @author emerald000
@@ -88,20 +84,23 @@ public final class Torchling extends CardImpl {
 }
 
 enum TorchlingPredicate implements ObjectSourcePlayerPredicate<ObjectSourcePlayer<StackObject>> {
+
     instance;
 
     @Override
     public boolean apply(ObjectSourcePlayer<StackObject> input, Game game) {
-        Stream<UUID> stream = input.getObject()
-                .getStackAbility()
-                .getTargets()
-                .stream()
-                .map(Target::getTargets)
-                .flatMap(Collection::stream)
-                .map(game::getPermanent)
-                .filter(Objects::nonNull)
-                .map(MageItem::getId);
-        return stream.allMatch(input.getSourceId()::equals)
-                && stream.anyMatch(input.getSourceId()::equals);
+        StackObject stackObject = game.getState().getStack().getStackObject(input.getObject().getId());
+        if (stackObject != null) {
+            for (UUID modeId : stackObject.getStackAbility().getModes().getSelectedModes()) {
+                Mode mode = stackObject.getStackAbility().getModes().get(modeId);
+                for (Target target : mode.getTargets()) {
+                    if (target.getTargets().contains(input.getSourceId()) // contains this card
+                            && target.getTargets().size() == 1) { // only one target
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
     }
 }

From d0553b3c3afbce561a85c24fe2b104463c4221c7 Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Tue, 16 Jun 2020 11:14:28 -0500
Subject: [PATCH 309/586] - little cleanup

---
 .../mageobject/NumberOfTargetsPredicate.java       | 14 --------------
 1 file changed, 14 deletions(-)

diff --git a/Mage/src/main/java/mage/filter/predicate/mageobject/NumberOfTargetsPredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/NumberOfTargetsPredicate.java
index e5c060ea0d..97cfe4d37f 100644
--- a/Mage/src/main/java/mage/filter/predicate/mageobject/NumberOfTargetsPredicate.java
+++ b/Mage/src/main/java/mage/filter/predicate/mageobject/NumberOfTargetsPredicate.java
@@ -1,8 +1,6 @@
-
 package mage.filter.predicate.mageobject;
 
 import java.util.UUID;
-import mage.MageObject;
 import mage.abilities.Mode;
 import mage.filter.predicate.Predicate;
 import mage.game.Controllable;
@@ -37,18 +35,6 @@ public class NumberOfTargetsPredicate implements Predicate<Controllable> {
                 return true;
             }
         }
-//        Spell spell = game.getStack().getSpell(input.getId());
-//        if (spell != null) {
-//            int numberOfTargets = 0;
-//            for (Mode mode : spell.getSpellAbility().getModes().getSelectedModes()) {
-//                for (Target target : mode.getTargets()) {
-//                    numberOfTargets += target.getTargets().size();
-//                }
-//            }
-//            if (numberOfTargets == targets) {
-//                return true;
-//            }
-//        }
         return false;
     }
 

From 7d91200612e6439c34ae87929bb678d439daf9ed Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Tue, 16 Jun 2020 20:36:11 +0200
Subject: [PATCH 310/586] * Memnarch - Fixed that the card type changing effect
 did not work correctly (wrong duration) (#6461).

---
 Mage.Sets/src/mage/cards/m/Memnarch.java | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/m/Memnarch.java b/Mage.Sets/src/mage/cards/m/Memnarch.java
index 75129bda42..09a585fdfe 100644
--- a/Mage.Sets/src/mage/cards/m/Memnarch.java
+++ b/Mage.Sets/src/mage/cards/m/Memnarch.java
@@ -1,4 +1,3 @@
-
 package mage.cards.m;
 
 import java.util.UUID;
@@ -12,8 +11,8 @@ import mage.abilities.effects.common.continuous.GainControlTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.SubType;
 import mage.constants.Duration;
+import mage.constants.SubType;
 import mage.constants.SuperType;
 import mage.constants.Zone;
 import mage.target.TargetPermanent;
@@ -26,7 +25,7 @@ import mage.target.common.TargetArtifactPermanent;
 public final class Memnarch extends CardImpl {
 
     public Memnarch(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{7}");
+        super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{7}");
         addSuperType(SuperType.LEGENDARY);
         this.subtype.add(SubType.WIZARD);
 
@@ -34,7 +33,7 @@ public final class Memnarch extends CardImpl {
         this.toughness = new MageInt(5);
 
         // {1}{U}{U}: Target permanent becomes an artifact in addition to its other types.
-        Effect effect = new AddCardTypeTargetEffect(Duration.WhileOnBattlefield, CardType.ARTIFACT);
+        Effect effect = new AddCardTypeTargetEffect(Duration.Custom, CardType.ARTIFACT);
         effect.setText("Target permanent becomes an artifact in addition to its other types");
         Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("{1}{U}{U}"));
         ability.addTarget(new TargetPermanent());
@@ -46,8 +45,12 @@ public final class Memnarch extends CardImpl {
         this.addAbility(ability2);
     }
 
-    public Memnarch(final Memnarch card){ super(card); }
+    public Memnarch(final Memnarch card) {
+        super(card);
+    }
 
     @Override
-    public Memnarch copy() { return new Memnarch(this); }
+    public Memnarch copy() {
+        return new Memnarch(this);
+    }
 }

From 1d0059fb535b3c3ec24a4f7b6141fa44b4a8a17f Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Tue, 16 Jun 2020 22:47:52 +0200
Subject: [PATCH 311/586] * Watcher - Fixed a problem copying HashMaps in
 watcher deep copy (fixes bug of Muldrotha, the Gravetide #6461).

---
 Mage/src/main/java/mage/watchers/Watcher.java | 22 +++++++------------
 1 file changed, 8 insertions(+), 14 deletions(-)

diff --git a/Mage/src/main/java/mage/watchers/Watcher.java b/Mage/src/main/java/mage/watchers/Watcher.java
index 14f2323a38..2b75cba4d8 100644
--- a/Mage/src/main/java/mage/watchers/Watcher.java
+++ b/Mage/src/main/java/mage/watchers/Watcher.java
@@ -1,14 +1,12 @@
-
 package mage.watchers;
 
-import mage.constants.WatcherScope;
-import mage.game.Game;
-import mage.game.events.GameEvent;
-import org.apache.log4j.Logger;
-
 import java.io.Serializable;
 import java.lang.reflect.*;
 import java.util.*;
+import mage.constants.WatcherScope;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+import org.apache.log4j.Logger;
 
 /**
  * watches for certain game events to occur and flags condition
@@ -24,7 +22,6 @@ public abstract class Watcher implements Serializable {
     protected boolean condition;
     protected final WatcherScope scope;
 
-
     public Watcher(WatcherScope scope) {
         this.scope = scope;
     }
@@ -114,7 +111,7 @@ public abstract class Watcher implements Serializable {
                     if (field.getType() == Set.class) {
                         ((Set) field.get(watcher)).clear();
                         ((Set) field.get(watcher)).addAll((Set) field.get(this));
-                    } else if (field.getType() == Map.class) {
+                    } else if (field.getType() == Map.class || field.getType() == HashMap.class) {
                         ParameterizedType parameterizedType = (ParameterizedType) field.getGenericType();
                         Type valueType = parameterizedType.getActualTypeArguments()[1];
                         if (valueType.getTypeName().contains("Set")) {
@@ -126,8 +123,7 @@ public abstract class Watcher implements Serializable {
                                 set.addAll(e.getValue());
                                 target.put(e.getKey(), set);
                             }
-                        }
-                        else if (valueType.getTypeName().contains("List")) {
+                        } else if (valueType.getTypeName().contains("List")) {
                             Map<Object, List<Object>> source = (Map<Object, List<Object>>) field.get(this);
                             Map<Object, List<Object>> target = (Map<Object, List<Object>>) field.get(watcher);
                             target.clear();
@@ -136,8 +132,7 @@ public abstract class Watcher implements Serializable {
                                 list.addAll(e.getValue());
                                 target.put(e.getKey(), list);
                             }
-                        }
-                        else if (valueType.getTypeName().contains("Map")) {
+                        } else if (valueType.getTypeName().contains("Map")) {
                             Map<Object, Map<Object, Object>> source = (Map<Object, Map<Object, Object>>) field.get(this);
                             Map<Object, Map<Object, Object>> target = (Map<Object, Map<Object, Object>>) field.get(watcher);
                             target.clear();
@@ -147,8 +142,7 @@ public abstract class Watcher implements Serializable {
                                 target.put(e.getKey(), map);
                             }
 
-                        }
-                        else {
+                        } else {
                             ((Map) field.get(watcher)).putAll((Map) field.get(this));
                         }
                     } else if (field.getType() == List.class) {

From b001d807ddf0129c06a3d3999a587908e1019b30 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 16 Jun 2020 18:09:51 -0400
Subject: [PATCH 312/586] updated M21 spoiler and reprints

---
 Mage.Sets/src/mage/sets/CoreSet2021.java | 56 ++++++++++++++-
 Utils/mtg-cards-data.txt                 | 92 +++++++++++++++++++++++-
 2 files changed, 146 insertions(+), 2 deletions(-)

diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index f949dd72cb..fcd5d40f26 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -49,9 +49,14 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Basri's Solidarity", 10, Rarity.UNCOMMON, mage.cards.b.BasrisSolidarity.class));
         cards.add(new SetCardInfo("Basri, Devoted Paladin", 320, Rarity.MYTHIC, mage.cards.b.BasriDevotedPaladin.class));
         cards.add(new SetCardInfo("Battle-Rattle Shaman", 130, Rarity.UNCOMMON, mage.cards.b.BattleRattleShaman.class));
+        cards.add(new SetCardInfo("Bloodfell Caves", 243, Rarity.COMMON, mage.cards.b.BloodfellCaves.class));
+        cards.add(new SetCardInfo("Blossoming Sands", 244, Rarity.COMMON, mage.cards.b.BlossomingSands.class));
         cards.add(new SetCardInfo("Bolt Hound", 131, Rarity.UNCOMMON, mage.cards.b.BoltHound.class));
         cards.add(new SetCardInfo("Brash Taunter", 133, Rarity.RARE, mage.cards.b.BrashTaunter.class));
         cards.add(new SetCardInfo("Burlfist Oak", 174, Rarity.UNCOMMON, mage.cards.b.BurlfistOak.class));
+        cards.add(new SetCardInfo("Burn Bright", 134, Rarity.COMMON, mage.cards.b.BurnBright.class));
+        cards.add(new SetCardInfo("Cancel", 46, Rarity.COMMON, mage.cards.c.Cancel.class));
+        cards.add(new SetCardInfo("Capture Sphere", 47, Rarity.COMMON, mage.cards.c.CaptureSphere.class));
         cards.add(new SetCardInfo("Carrion Grub", 92, Rarity.UNCOMMON, mage.cards.c.CarrionGrub.class));
         cards.add(new SetCardInfo("Celestial Enforcer", 11, Rarity.COMMON, mage.cards.c.CelestialEnforcer.class));
         cards.add(new SetCardInfo("Chandra's Firemaw", 333, Rarity.RARE, mage.cards.c.ChandrasFiremaw.class));
@@ -63,28 +68,39 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Chromatic Orrery", 228, Rarity.MYTHIC, mage.cards.c.ChromaticOrrery.class));
         cards.add(new SetCardInfo("Colossal Dreadmaw", 176, Rarity.COMMON, mage.cards.c.ColossalDreadmaw.class));
         cards.add(new SetCardInfo("Conclave Mentor", 216, Rarity.UNCOMMON, mage.cards.c.ConclaveMentor.class));
-        cards.add(new SetCardInfo("Containment Priest", 314, Rarity.RARE, mage.cards.c.ContainmentPriest.class));
+        cards.add(new SetCardInfo("Concordia Pegasus", 12, Rarity.COMMON, mage.cards.c.ConcordiaPegasus.class));
+        cards.add(new SetCardInfo("Containment Priest", 13, Rarity.RARE, mage.cards.c.ContainmentPriest.class));
+        cards.add(new SetCardInfo("Crash Through", 140, Rarity.COMMON, mage.cards.c.CrashThrough.class));
         cards.add(new SetCardInfo("Cultivate", 177, Rarity.UNCOMMON, mage.cards.c.Cultivate.class));
+        cards.add(new SetCardInfo("Deathbloom Thallid", 94, Rarity.COMMON, mage.cards.d.DeathbloomThallid.class));
         cards.add(new SetCardInfo("Defiant Strike", 15, Rarity.COMMON, mage.cards.d.DefiantStrike.class));
+        cards.add(new SetCardInfo("Destructive Tampering", 141, Rarity.COMMON, mage.cards.d.DestructiveTampering.class));
         cards.add(new SetCardInfo("Dire Fleet Warmonger", 217, Rarity.UNCOMMON, mage.cards.d.DireFleetWarmonger.class));
         cards.add(new SetCardInfo("Discontinuity", 48, Rarity.MYTHIC, mage.cards.d.Discontinuity.class));
+        cards.add(new SetCardInfo("Dismal Backwater", 245, Rarity.COMMON, mage.cards.d.DismalBackwater.class));
         cards.add(new SetCardInfo("Double Vision", 142, Rarity.RARE, mage.cards.d.DoubleVision.class));
         cards.add(new SetCardInfo("Drowsing Tyrannodon", 178, Rarity.COMMON, mage.cards.d.DrowsingTyrannodon.class));
+        cards.add(new SetCardInfo("Dub", 16, Rarity.COMMON, mage.cards.d.Dub.class));
         cards.add(new SetCardInfo("Duress", 96, Rarity.COMMON, mage.cards.d.Duress.class));
         cards.add(new SetCardInfo("Elder Gargaroth", 179, Rarity.MYTHIC, mage.cards.e.ElderGargaroth.class));
         cards.add(new SetCardInfo("Eliminate", 97, Rarity.UNCOMMON, mage.cards.e.Eliminate.class));
+        cards.add(new SetCardInfo("Epitaph Golem", 230, Rarity.UNCOMMON, mage.cards.e.EpitaphGolem.class));
         cards.add(new SetCardInfo("Experimental Overload", 218, Rarity.UNCOMMON, mage.cards.e.ExperimentalOverload.class));
         cards.add(new SetCardInfo("Fabled Passage", 246, Rarity.RARE, mage.cards.f.FabledPassage.class));
         cards.add(new SetCardInfo("Faith's Fetters", 17, Rarity.UNCOMMON, mage.cards.f.FaithsFetters.class));
         cards.add(new SetCardInfo("Falconer Adept", 18, Rarity.UNCOMMON, mage.cards.f.FalconerAdept.class));
+        cards.add(new SetCardInfo("Feat of Resistance", 19, Rarity.COMMON, mage.cards.f.FeatOfResistance.class));
         cards.add(new SetCardInfo("Feline Sovereign", 180, Rarity.RARE, mage.cards.f.FelineSovereign.class));
+        cards.add(new SetCardInfo("Fetid Imp", 98, Rarity.COMMON, mage.cards.f.FetidImp.class));
         cards.add(new SetCardInfo("Fierce Empath", 181, Rarity.UNCOMMON, mage.cards.f.FierceEmpath.class));
         cards.add(new SetCardInfo("Fiery Emancipation", 143, Rarity.MYTHIC, mage.cards.f.FieryEmancipation.class));
         cards.add(new SetCardInfo("Finishing Blow", 99, Rarity.COMMON, mage.cards.f.FinishingBlow.class));
         cards.add(new SetCardInfo("Forest", 272, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Frantic Inventory", 50, Rarity.COMMON, mage.cards.f.FranticInventory.class));
+        cards.add(new SetCardInfo("Frost Breath", 51, Rarity.COMMON, mage.cards.f.FrostBreath.class));
         cards.add(new SetCardInfo("Fungal Rebirth", 182, Rarity.UNCOMMON, mage.cards.f.FungalRebirth.class));
         cards.add(new SetCardInfo("Furious Rise", 144, Rarity.UNCOMMON, mage.cards.f.FuriousRise.class));
+        cards.add(new SetCardInfo("Furor of the Bitten", 145, Rarity.COMMON, mage.cards.f.FurorOfTheBitten.class));
         cards.add(new SetCardInfo("Gadrak, the Crown-Scourge", 146, Rarity.RARE, mage.cards.g.GadrakTheCrownScourge.class));
         cards.add(new SetCardInfo("Garruk's Gorehorn", 184, Rarity.COMMON, mage.cards.g.GarruksGorehorn.class));
         cards.add(new SetCardInfo("Garruk's Warsteed", 337, Rarity.RARE, mage.cards.g.GarruksWarsteed.class));
@@ -103,14 +119,18 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Historian of Zhalfir", 325, Rarity.UNCOMMON, mage.cards.h.HistorianOfZhalfir.class));
         cards.add(new SetCardInfo("Igneous Cur", 153, Rarity.COMMON, mage.cards.i.IgneousCur.class));
         cards.add(new SetCardInfo("Indulging Patrician", 219, Rarity.UNCOMMON, mage.cards.i.IndulgingPatrician.class));
+        cards.add(new SetCardInfo("Infernal Scarring", 105, Rarity.COMMON, mage.cards.i.InfernalScarring.class));
         cards.add(new SetCardInfo("Invigorating Surge", 190, Rarity.UNCOMMON, mage.cards.i.InvigoratingSurge.class));
         cards.add(new SetCardInfo("Island", 263, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Jeskai Elder", 53, Rarity.UNCOMMON, mage.cards.j.JeskaiElder.class));
         cards.add(new SetCardInfo("Jolrael, Mwonvuli Recluse", 191, Rarity.RARE, mage.cards.j.JolraelMwonvuliRecluse.class));
+        cards.add(new SetCardInfo("Jungle Hollow", 247, Rarity.COMMON, mage.cards.j.JungleHollow.class));
         cards.add(new SetCardInfo("Kaervek, the Spiteful", 106, Rarity.RARE, mage.cards.k.KaervekTheSpiteful.class));
         cards.add(new SetCardInfo("Keral Keep Disciples", 334, Rarity.UNCOMMON, mage.cards.k.KeralKeepDisciples.class));
         cards.add(new SetCardInfo("Kitesail Freebooter", 107, Rarity.UNCOMMON, mage.cards.k.KitesailFreebooter.class));
+        cards.add(new SetCardInfo("Legion's Judgment", 24, Rarity.COMMON, mage.cards.l.LegionsJudgment.class));
         cards.add(new SetCardInfo("Library Larcenist", 55, Rarity.COMMON, mage.cards.l.LibraryLarcenist.class));
+        cards.add(new SetCardInfo("Life Goes On", 192, Rarity.COMMON, mage.cards.l.LifeGoesOn.class));
         cards.add(new SetCardInfo("Light of Promise", 25, Rarity.UNCOMMON, mage.cards.l.LightOfPromise.class));
         cards.add(new SetCardInfo("Liliana's Devotee", 109, Rarity.UNCOMMON, mage.cards.l.LilianasDevotee.class));
         cards.add(new SetCardInfo("Liliana's Scorn", 329, Rarity.RARE, mage.cards.l.LilianasScorn.class));
@@ -122,11 +142,13 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Llanowar Visionary", 193, Rarity.COMMON, mage.cards.l.LlanowarVisionary.class));
         cards.add(new SetCardInfo("Lofty Denial", 56, Rarity.COMMON, mage.cards.l.LoftyDenial.class));
         cards.add(new SetCardInfo("Lorescale Coatl", 221, Rarity.UNCOMMON, mage.cards.l.LorescaleCoatl.class));
+        cards.add(new SetCardInfo("Makeshift Battalion", 26, Rarity.COMMON, mage.cards.m.MakeshiftBattalion.class));
         cards.add(new SetCardInfo("Malefic Scythe", 112, Rarity.UNCOMMON, mage.cards.m.MaleficScythe.class));
         cards.add(new SetCardInfo("Mangara, the Diplomat", 27, Rarity.MYTHIC, mage.cards.m.MangaraTheDiplomat.class));
         cards.add(new SetCardInfo("Massacre Wurm", 114, Rarity.MYTHIC, mage.cards.m.MassacreWurm.class));
         cards.add(new SetCardInfo("Mazemind Tome", 232, Rarity.RARE, mage.cards.m.MazemindTome.class));
         cards.add(new SetCardInfo("Meteorite", 233, Rarity.UNCOMMON, mage.cards.m.Meteorite.class));
+        cards.add(new SetCardInfo("Mind Rot", 115, Rarity.COMMON, mage.cards.m.MindRot.class));
         cards.add(new SetCardInfo("Miscast", 57, Rarity.UNCOMMON, mage.cards.m.Miscast.class));
         cards.add(new SetCardInfo("Mistral Singer", 58, Rarity.COMMON, mage.cards.m.MistralSinger.class));
         cards.add(new SetCardInfo("Mountain", 269, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS));
@@ -134,47 +156,67 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Necromentia", 116, Rarity.RARE, mage.cards.n.Necromentia.class));
         cards.add(new SetCardInfo("Niambi, Esteemed Speaker", 222, Rarity.RARE, mage.cards.n.NiambiEsteemedSpeaker.class));
         cards.add(new SetCardInfo("Obsessive Stitcher", 223, Rarity.UNCOMMON, mage.cards.o.ObsessiveStitcher.class));
+        cards.add(new SetCardInfo("Onakke Ogre", 155, Rarity.COMMON, mage.cards.o.OnakkeOgre.class));
+        cards.add(new SetCardInfo("Opt", 59, Rarity.COMMON, mage.cards.o.Opt.class));
         cards.add(new SetCardInfo("Pack Leader", 29, Rarity.RARE, mage.cards.p.PackLeader.class));
         cards.add(new SetCardInfo("Palladium Myr", 234, Rarity.UNCOMMON, mage.cards.p.PalladiumMyr.class));
         cards.add(new SetCardInfo("Peer into the Abyss", 117, Rarity.RARE, mage.cards.p.PeerIntoTheAbyss.class));
         cards.add(new SetCardInfo("Pestilent Haze", 118, Rarity.UNCOMMON, mage.cards.p.PestilentHaze.class));
+        cards.add(new SetCardInfo("Pitchburn Devils", 156, Rarity.COMMON, mage.cards.p.PitchburnDevils.class));
         cards.add(new SetCardInfo("Plains", 260, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Portcullis Vine", 195, Rarity.COMMON, mage.cards.p.PortcullisVine.class));
         cards.add(new SetCardInfo("Predatory Wurm", 338, Rarity.UNCOMMON, mage.cards.p.PredatoryWurm.class));
         cards.add(new SetCardInfo("Pridemalkin", 196, Rarity.COMMON, mage.cards.p.Pridemalkin.class));
         cards.add(new SetCardInfo("Primal Might", 197, Rarity.RARE, mage.cards.p.PrimalMight.class));
+        cards.add(new SetCardInfo("Prismite", 235, Rarity.COMMON, mage.cards.p.Prismite.class));
         cards.add(new SetCardInfo("Pursued Whale", 60, Rarity.RARE, mage.cards.p.PursuedWhale.class));
         cards.add(new SetCardInfo("Quirion Dryad", 198, Rarity.UNCOMMON, mage.cards.q.QuirionDryad.class));
         cards.add(new SetCardInfo("Radiant Fountain", 248, Rarity.COMMON, mage.cards.r.RadiantFountain.class));
         cards.add(new SetCardInfo("Rain of Revelation", 61, Rarity.UNCOMMON, mage.cards.r.RainOfRevelation.class));
         cards.add(new SetCardInfo("Rambunctious Mutt", 30, Rarity.COMMON, mage.cards.r.RambunctiousMutt.class));
         cards.add(new SetCardInfo("Ranger's Guile", 199, Rarity.COMMON, mage.cards.r.RangersGuile.class));
+        cards.add(new SetCardInfo("Return to Nature", 200, Rarity.COMMON, mage.cards.r.ReturnToNature.class));
         cards.add(new SetCardInfo("Revitalize", 31, Rarity.COMMON, mage.cards.r.Revitalize.class));
         cards.add(new SetCardInfo("Rewind", 63, Rarity.UNCOMMON, mage.cards.r.Rewind.class));
         cards.add(new SetCardInfo("Riddleform", 64, Rarity.UNCOMMON, mage.cards.r.Riddleform.class));
         cards.add(new SetCardInfo("Rin and Seri, Inseparable", 278, Rarity.MYTHIC, mage.cards.r.RinAndSeriInseparable.class));
         cards.add(new SetCardInfo("Rousing Read", 67, Rarity.COMMON, mage.cards.r.RousingRead.class));
+        cards.add(new SetCardInfo("Rugged Highlands", 249, Rarity.COMMON, mage.cards.r.RuggedHighlands.class));
         cards.add(new SetCardInfo("Runed Halo", 32, Rarity.RARE, mage.cards.r.RunedHalo.class));
         cards.add(new SetCardInfo("Sabertooth Mauler", 202, Rarity.COMMON, mage.cards.s.SabertoothMauler.class));
         cards.add(new SetCardInfo("Scavenging Ooze", 204, Rarity.RARE, mage.cards.s.ScavengingOoze.class));
+        cards.add(new SetCardInfo("Scorching Dragonfire", 158, Rarity.COMMON, mage.cards.s.ScorchingDragonfire.class));
+        cards.add(new SetCardInfo("Scoured Barrens", 250, Rarity.COMMON, mage.cards.s.ScouredBarrens.class));
         cards.add(new SetCardInfo("Seasoned Hallowblade", 34, Rarity.UNCOMMON, mage.cards.s.SeasonedHallowblade.class));
         cards.add(new SetCardInfo("See the Truth", 69, Rarity.RARE, mage.cards.s.SeeTheTruth.class));
         cards.add(new SetCardInfo("Selfless Savior", 36, Rarity.UNCOMMON, mage.cards.s.SelflessSavior.class));
+        cards.add(new SetCardInfo("Setessan Training", 205, Rarity.COMMON, mage.cards.s.SetessanTraining.class));
         cards.add(new SetCardInfo("Shacklegeist", 70, Rarity.RARE, mage.cards.s.Shacklegeist.class));
         cards.add(new SetCardInfo("Shipwreck Dowser", 71, Rarity.UNCOMMON, mage.cards.s.ShipwreckDowser.class));
+        cards.add(new SetCardInfo("Shock", 159, Rarity.COMMON, mage.cards.s.Shock.class));
+        cards.add(new SetCardInfo("Short Sword", 236, Rarity.COMMON, mage.cards.s.ShortSword.class));
         cards.add(new SetCardInfo("Siege Striker", 37, Rarity.UNCOMMON, mage.cards.s.SiegeStriker.class));
         cards.add(new SetCardInfo("Sigiled Contender", 323, Rarity.UNCOMMON, mage.cards.s.SigiledContender.class));
+        cards.add(new SetCardInfo("Silent Dart", 237, Rarity.COMMON, mage.cards.s.SilentDart.class));
         cards.add(new SetCardInfo("Silversmote Ghoul", 122, Rarity.UNCOMMON, mage.cards.s.SilversmoteGhoul.class));
+        cards.add(new SetCardInfo("Skeleton Archer", 123, Rarity.COMMON, mage.cards.s.SkeletonArcher.class));
+        cards.add(new SetCardInfo("Skyscanner", 238, Rarity.COMMON, mage.cards.s.Skyscanner.class));
+        cards.add(new SetCardInfo("Snarespinner", 207, Rarity.COMMON, mage.cards.s.Snarespinner.class));
         cards.add(new SetCardInfo("Solemn Simulacrum", 239, Rarity.RARE, mage.cards.s.SolemnSimulacrum.class));
         cards.add(new SetCardInfo("Soul Sear", 160, Rarity.UNCOMMON, mage.cards.s.SoulSear.class));
         cards.add(new SetCardInfo("Sparkhunter Masticore", 240, Rarity.RARE, mage.cards.s.SparkhunterMasticore.class));
         cards.add(new SetCardInfo("Speaker of the Heavens", 38, Rarity.RARE, mage.cards.s.SpeakerOfTheHeavens.class));
+        cards.add(new SetCardInfo("Spellgorger Weird", 161, Rarity.COMMON, mage.cards.s.SpellgorgerWeird.class));
         cards.add(new SetCardInfo("Spined Megalodon", 72, Rarity.COMMON, mage.cards.s.SpinedMegalodon.class));
         cards.add(new SetCardInfo("Spirit of Malevolence", 331, Rarity.COMMON, mage.cards.s.SpiritOfMalevolence.class));
         cards.add(new SetCardInfo("Sporeweb Weaver", 208, Rarity.RARE, mage.cards.s.SporewebWeaver.class));
         cards.add(new SetCardInfo("Storm Caller", 335, Rarity.COMMON, mage.cards.s.StormCaller.class));
         cards.add(new SetCardInfo("Stormwing Entity", 73, Rarity.RARE, mage.cards.s.StormwingEntity.class));
         cards.add(new SetCardInfo("Sublime Epiphany", 74, Rarity.RARE, mage.cards.s.SublimeEpiphany.class));
+        cards.add(new SetCardInfo("Sure Strike", 163, Rarity.COMMON, mage.cards.s.SureStrike.class));
         cards.add(new SetCardInfo("Swamp", 266, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Swiftwater Cliffs", 251, Rarity.COMMON, mage.cards.s.SwiftwaterCliffs.class));
+        cards.add(new SetCardInfo("Tavern Swindler", 124, Rarity.UNCOMMON, mage.cards.t.TavernSwindler.class));
         cards.add(new SetCardInfo("Teferi's Ageless Insight", 76, Rarity.RARE, mage.cards.t.TeferisAgelessInsight.class));
         cards.add(new SetCardInfo("Teferi's Protege", 77, Rarity.COMMON, mage.cards.t.TeferisProtege.class));
         cards.add(new SetCardInfo("Teferi's Tutelage", 78, Rarity.UNCOMMON, mage.cards.t.TeferisTutelage.class));
@@ -187,23 +229,35 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Temple of Silence", 255, Rarity.RARE, mage.cards.t.TempleOfSilence.class));
         cards.add(new SetCardInfo("Temple of Triumph", 256, Rarity.RARE, mage.cards.t.TempleOfTriumph.class));
         cards.add(new SetCardInfo("Thieves' Guild Enforcer", 125, Rarity.RARE, mage.cards.t.ThievesGuildEnforcer.class));
+        cards.add(new SetCardInfo("Thornwood Falls", 257, Rarity.COMMON, mage.cards.t.ThornwoodFalls.class));
+        cards.add(new SetCardInfo("Thrashing Brontodon", 209, Rarity.UNCOMMON, mage.cards.t.ThrashingBrontodon.class));
+        cards.add(new SetCardInfo("Thrill of Possibility", 165, Rarity.COMMON, mage.cards.t.ThrillOfPossibility.class));
         cards.add(new SetCardInfo("Tide Skimmer", 79, Rarity.UNCOMMON, mage.cards.t.TideSkimmer.class));
+        cards.add(new SetCardInfo("Titanic Growth", 210, Rarity.COMMON, mage.cards.t.TitanicGrowth.class));
         cards.add(new SetCardInfo("Tolarian Kraken", 80, Rarity.UNCOMMON, mage.cards.t.TolarianKraken.class));
         cards.add(new SetCardInfo("Tormod's Crypt", 241, Rarity.UNCOMMON, mage.cards.t.TormodsCrypt.class));
         cards.add(new SetCardInfo("Traitorous Greed", 166, Rarity.UNCOMMON, mage.cards.t.TraitorousGreed.class));
+        cards.add(new SetCardInfo("Tranquil Cove", 258, Rarity.COMMON, mage.cards.t.TranquilCove.class));
         cards.add(new SetCardInfo("Trufflesnout", 212, Rarity.COMMON, mage.cards.t.Trufflesnout.class));
+        cards.add(new SetCardInfo("Turn to Slag", 168, Rarity.COMMON, mage.cards.t.TurnToSlag.class));
+        cards.add(new SetCardInfo("Turret Ogre", 169, Rarity.COMMON, mage.cards.t.TurretOgre.class));
         cards.add(new SetCardInfo("Ugin, the Spirit Dragon", 1, Rarity.MYTHIC, mage.cards.u.UginTheSpiritDragon.class));
         cards.add(new SetCardInfo("Unleash Fury", 170, Rarity.UNCOMMON, mage.cards.u.UnleashFury.class));
         cards.add(new SetCardInfo("Unsubstantiate", 82, Rarity.UNCOMMON, mage.cards.u.Unsubstantiate.class));
         cards.add(new SetCardInfo("Valorous Steed", 42, Rarity.COMMON, mage.cards.v.ValorousSteed.class));
         cards.add(new SetCardInfo("Village Rites", 126, Rarity.COMMON, mage.cards.v.VillageRites.class));
         cards.add(new SetCardInfo("Vito, Thorn of the Dusk Rose", 127, Rarity.RARE, mage.cards.v.VitoThornOfTheDuskRose.class));
+        cards.add(new SetCardInfo("Vodalian Arcanist", 83, Rarity.COMMON, mage.cards.v.VodalianArcanist.class));
         cards.add(new SetCardInfo("Volcanic Geyser", 171, Rarity.UNCOMMON, mage.cards.v.VolcanicGeyser.class));
         cards.add(new SetCardInfo("Volcanic Salvo", 172, Rarity.RARE, mage.cards.v.VolcanicSalvo.class));
         cards.add(new SetCardInfo("Vryn Wingmare", 43, Rarity.UNCOMMON, mage.cards.v.VrynWingmare.class));
         cards.add(new SetCardInfo("Waker of Waves", 84, Rarity.UNCOMMON, mage.cards.w.WakerOfWaves.class));
+        cards.add(new SetCardInfo("Walking Corpse", 128, Rarity.COMMON, mage.cards.w.WalkingCorpse.class));
+        cards.add(new SetCardInfo("Wall of Runes", 85, Rarity.COMMON, mage.cards.w.WallOfRunes.class));
         cards.add(new SetCardInfo("Warden of the Woods", 213, Rarity.UNCOMMON, mage.cards.w.WardenOfTheWoods.class));
         cards.add(new SetCardInfo("Wildwood Patrol", 339, Rarity.COMMON, mage.cards.w.WildwoodPatrol.class));
         cards.add(new SetCardInfo("Wildwood Scourge", 214, Rarity.UNCOMMON, mage.cards.w.WildwoodScourge.class));
+        cards.add(new SetCardInfo("Wind-Scarred Crag", 259, Rarity.COMMON, mage.cards.w.WindScarredCrag.class));
+        cards.add(new SetCardInfo("Wishcoin Crab", 86, Rarity.COMMON, mage.cards.w.WishcoinCrab.class));
     }
 }
diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt
index c8405a6902..48eb0ed176 100644
--- a/Utils/mtg-cards-data.txt
+++ b/Utils/mtg-cards-data.txt
@@ -37441,19 +37441,29 @@ Crystalline Giant|Ikoria: Lair of Behemoths|387|R|{3}|Artifact Creature - Giant|
 Ugin, the Spirit Dragon|Core Set 2021|1|M|{8}|Legendary Planeswalker - Ugin|7|+2: Ugin, the Spirit Dragon deals 3 damage to any target.$−X: Exile each permanent with converted mana cost X or less that's one or more colors.$−10: You gain 7 life, draw seven cards, then put up to seven permanent cards from your hand onto the battlefield.|
 Alpine Watchdog|Core Set 2021|2|C|{1}{W}|Creature - Dog|2|2|Vigilance|
 Angelic Ascension|Core Set 2021|3|U|{1}{W}|Instant|||Exile target creature or planeswalker. Its controller creates a 4/4 white Angel creature token with flying.|
+Anointed Chorister|Core Set 2021|4|C|{W}|Creature - Human Cleric|1|1|Lifelink${4}{W}: Anointed Chorister gets +3/+3 until end of turn.|
+Aven Gagglemaster|Core Set 2021|5|U|{3}{W}{W}|Creature - Bird Warrior|4|3|Flying$When Aven Gagglemaster enters the battlefield, you gain 2 life for each creature you control with flying.|
 Baneslayer Angel|Core Set 2021|6|M|{3}{W}{W}|Creature - Angel|5|5|Flying, first strike, lifelink, protection from Demons and from Dragons|
 Basri Ket|Core Set 2021|7|M|{1}{W}{W}|Legendary Planeswalker - Basri|3|+1: Put a +1/+1 counter on up to one target creature. It gains indestructible until end of turn.$−2: Whenever one or more nontoken creatures attack this turn, create that many 1/1 white Soldier creature tokens that are tapped and attacking.$−6: You get an emblem with "At the beginning of combat on your turn, create a 1/1 white Soldier creature token, then put a +1/+1 counter on each creature you control."|
 Basri's Acolyte|Core Set 2021|8|C|{2}{W}{W}|Creature - Cat Cleric|2|3|Lifelink$When Basri's Acolyte enters the battlefield, put a +1/+1 counter on each of up to two other target creatures you control.|
 Basri's Lieutenant|Core Set 2021|9|R|{3}{W}|Creature - Human Knight|3|4|Vigilance, protection from multicolored$When Basri's Lieutenant enters the battlefield, put a +1/+1 counter on target creature you control.$Whenever Basri's Lieutenant or another creature you control dies, if it had a +1/+1 counter on it, create a 2/2 white Knight creature token with vigilance.|
 Basri's Solidarity|Core Set 2021|10|U|{1}{W}|Sorcery|||Put a +1/+1 counter on each creature you control.|
 Celestial Enforcer|Core Set 2021|11|C|{2}{W}|Creature - Human Cleric|2|3|{1}{W}, {T}: Tap target creature. Activate this ability only if you control a creature with flying.|
+Concordia Pegasus|Core Set 2021|12|C|{1}{W}|Creature - Pegasus|1|3|Flying|
+Containment Priest|Core Set 2021|13|R|{1}{W}|Creature - Human Cleric|2|2|Flash$If a nontoken creature would enter the battlefield and it wasn't cast, exile it instead.|
+Daybreak Charger|Core Set 2021|14|C|{1}{W}|Creature - Unicorn|3|1|When Daybreak Charger enters the battlefield, target creature gets +2/+0 until end of turn.|
 Defiant Strike|Core Set 2021|15|C|{W}|Instant|||Target creature gets +1/+0 until end of turn.$Draw a card.|
+Dub|Core Set 2021|16|C|{2}{W}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +2/+2, has first strike, and is a Knight in addition to its other types.|
 Faith's Fetters|Core Set 2021|17|U|{3}{W}|Enchantment - Aura|||Enchant permanent$When Faith's Fetters enters the battlefield, you gain 4 life.$Enchanted permanent can't attack or block, and its activated abilities can't be activated unless they're mana abilities.|
 Falconer Adept|Core Set 2021|18|U|{3}{W}|Creature - Human Soldier|2|3|Whenever Falconer Adept attacks, create a 1/1 white Bird creature token with flying that's tapped and attacking.|
+Feat of Resistance|Core Set 2021|19|C|{1}{W}|Instant|||Put a +1/+1 counter on target creature you control. It gains protection from the color of your choice until end of turn.|
+Gale Swooper|Core Set 2021|20|C|{3}{W}|Creature - Griffin|3|2|Flying$When Gale Swooper enters the battlefield, target creature gains flying until end of turn.|
 Glorious Anthem|Core Set 2021|21|R|{1}{W}{W}|Enchantment|||Creatures you control get +1/+1.|
 Griffin Aerie|Core Set 2021|22|U|{1}{W}|Enchantment|||At the beginning of your end step, if you gained 3 or more life this turn, create a 2/2 white Griffin creature token with flying.|
 Idol of Endurance|Core Set 2021|23|R|{2}{W}|Artifact|||When Idol of Endurance enters the battlefield, exile all creature cards with converted mana cost 3 or less from your graveyard until Idol of Endurance leaves the battlefield.${1}{W}, {T}: Until end of turn, you may cast a creature spell from among the cards exiled with Idol of Endurance without paying its mana cost.|
+Legion's Judgment|Core Set 2021|24|C|{2}{W}|Sorcery|||Destroy target creature with power 4 or greater.|
 Light of Promise|Core Set 2021|25|U|{2}{W}|Enchantment - Aura|||Enchant creature$Enchanted creature has "Whenever you gain life, put that many +1/+1 counters on this creature."|
+Makeshift Battalion|Core Set 2021|26|C|{2}{W}|Creature - Human Soldier|3|2|Whenever Makeshift Battalion and at least two other creatures attack, put a +1/+1 counter on Makeshift Battalion.|
 Mangara, the Diplomat|Core Set 2021|27|M|{3}{W}|Legendary Creature - Human Cleric|2|4|Lifelink$Whenever an opponent attacks with creatures, if two or more of those creatures are attacking you and/or a planeswalker you control, draw a card.$Whenever an opponent casts their second spell each turn, draw a card.|
 Nine Lives|Core Set 2021|28|R|{1}{W}{W}|Enchantment|||Hexproof$If a source would deal damage to you, prevent that damage and put an incarnation counter on Nine Lives.$When there are nine or more incarnation counters on Nine Lives, exile it.$When Nine Lives leaves the battlefield, you lose the game.|
 Pack Leader|Core Set 2021|29|R|{1}{W}|Creature - Dog|2|2|Other Dogs you control get +1/+1.$Whenever Pack Leader attacks, prevent all combat damage that would be dealt this turn to Dogs you control.|
@@ -37462,26 +37472,38 @@ Revitalize|Core Set 2021|31|C|{1}{W}|Instant|||You gain 3 life.$Draw a card.|
 Runed Halo|Core Set 2021|32|R|{W}{W}|Enchantment|||As Runed Halo enters the battlefield, choose a card name.$You have protection from the chosen name.|
 Sanctum of Tranquil Light|Core Set 2021|33|U|{W}|Legendary Enchantment - Shrine|||{5}{W}: Tap target creature. This ability costs {1} less to activate for each Shrine you control.|
 Seasoned Hallowblade|Core Set 2021|34|U|{1}{W}|Creature - Human Warrior|3|1|Discard a card: Tap Seasoned Hallowblade. It gains indestructible until end of turn.|
+Secure the Scene|Core Set 2021|35|C|{4}{W}|Sorcery|||Exile target nonland permanent. Its controller creates a 1/1 white Soldier creature token.|
 Selfless Savior|Core Set 2021|36|U|{W}|Creature - Dog|1|1|Sacrifice Selfless Savior: Another target creature you control gains indestructible until end of turn.|
 Siege Striker|Core Set 2021|37|U|{2}{W}|Creature - Human Soldier|1|1|Double strike$Whenever Siege Striker attacks, you may tap any number of untapped creatures you control. Siege Striker gets +1/+1 until end of turn for each creature tapped this way.|
 Speaker of the Heavens|Core Set 2021|38|R|{W}|Creature - Human Cleric|1|1|Vigilance, lifelink${T}: Create a 4/4 white Angel creature token with flying. Activate this ability only if you have at least 7 more life than your starting life total and only any time you could cast a sorcery.|
+Staunch Shieldmate|Core Set 2021|39|C|{W}|Creature - Dwarf Soldier|1|3||
+Swift Response|Core Set 2021|40|C|{1}{W}|Instant|||Destroy target tapped creature.|
 Tempered Veteran|Core Set 2021|41|U|{1}{W}|Creature - Human Knight|1|2|{W}, {T}: Put a +1/+1 counter on target creature with a +1/+1 counter on it.${4}{W}{W}, {T}: Put a +1/+1 counter on target creature.|
 Valorous Steed|Core Set 2021|42|C|{4}{W}|Creature - Unicorn|3|3|Vigilance$When Valorous Steed enters the battlefield, create a 2/2 white Knight creature token with vigilance.|
 Vryn Wingmare|Core Set 2021|43|U|{2}{W}|Creature - Pegasus|2|1|Flying$Noncreature spells cost {1} more to cast.|
+Warded Battlements|Core Set 2021|44|C|{2}{W}|Creature - Wall|0|3|Defender$Attacking creatures you control get +1/+0.|
 Barrin, Tolarian Archmage|Core Set 2021|45|R|{1}{U}{U}|Legendary Creature - Human Wizard|2|2|When Barrin, Tolarian Archmage enters the battlefield, return up to one other target creature or planeswalker to its owner's hand.$At the beginning of your end step, if a permanent was put into your hand from the battlefield this turn, draw a card.|
+Cancel|Core Set 2021|46|C|{1}{U}{U}|Instant|||Counter target spell.|
+Capture Sphere|Core Set 2021|47|C|{3}{U}|Enchantment - Aura|||Flash$Enchant creature$When Capture Sphere enters the battlefield, tap enchanted creature.$Enchanted creature doesn't untap during its controller's untap step.|
 Discontinuity|Core Set 2021|48|M|{3}{U}{U}{U}|Instant|||As long as it's your turn, this spell costs {2}{U}{U} less to cast.$End the turn.|
 Enthralling Hold|Core Set 2021|49|U|{3}{U}{U}|Enchantment - Aura|||Enchant creature$You can't choose an untapped creature as this spell's target as you cast it.$You control enchanted creature.|
 Frantic Inventory|Core Set 2021|50|C|{1}{U}|Instant|||Draw a card, then draw cards equal to the number of cards named Frantic Inventory in your graveyard.|
+Frost Breath|Core Set 2021|51|C|{2}{U}|Instant|||Tap up to two target creatures. Those creatures don't untap during their controller's next untap step.|
 Ghostly Pilferer|Core Set 2021|52|R|{1}{U}|Creature - Spirit Rogue|2|1|Whenever Ghostly Pilferer becomes tapped, you may pay {2}. If you do, draw a card.$Whenever an opponent casts a spell from anywhere other than their hand, draw a card.$Discard a card: Ghostly Pilferer can't be blocked this turn.|
 Jeskai Elder|Core Set 2021|53|U|{1}{U}|Creature - Human Monk|1|2|Prowess$Whenever Jeskai Elder deals combat damage to a player, you may draw a card. If you do, discard a card.|
+Keen Glidemaster|Core Set 2021|54|C|{1}{U}|Creature - Human Soldier|2|1|{2}{U}: Target creature gains flying until end of turn.|
 Library Larcenist|Core Set 2021|55|C|{2}{U}|Creature - Merfolk Rogue|1|2|Whenever Library Larcenist attacks, draw a card.|
 Lofty Denial|Core Set 2021|56|C|{1}{U}|Instant|||Counter target spell unless its controller pays {1}. If you control a creature with flying, counter that spell unless its controller pays {4} instead.|
 Miscast|Core Set 2021|57|U|{U}|Instant|||Counter target instant or sorcery spell unless its controller pays {3}.|
 Mistral Singer|Core Set 2021|58|C|{2}{U}|Creature - Siren|2|2|Flying$Prowess|
+Opt|Core Set 2021|59|C|{U}|Instant|||Scry 1.$Draw a card.|
 Pursued Whale|Core Set 2021|60|R|{5}{U}{U}|Creature - Whale|8|8|When Pursued Whale enters the battlefield, each opponent creates a 1/1 red Pirate creature token with "This creature can't block" and "Creatures you control attack each combat if able."$Spells your opponents cast that target Pursued Whale cost {3} more to cast.|
 Rain of Revelation|Core Set 2021|61|U|{3}{U}|Instant|||Draw three cards, then discard a card.|
+Read the Tides|Core Set 2021|62|C|{5}{U}|Sorcery|||Choose one —$• Draw three cards.$• Return up to two target creatures to their owners' hands.|
 Rewind|Core Set 2021|63|U|{2}{U}{U}|Instant|||Counter target spell. Untap up to four lands.|
 Riddleform|Core Set 2021|64|U|{1}{U}|Enchantment|||Whenever you cast a noncreature spell, you may have Riddleform become a 3/3 Sphinx creature with flying in addition to its other types until end of turn.${2}{U}: Scry 1.|
+Roaming Ghostlight|Core Set 2021|65|C|{3}{U}{U}|Creature - Spirit|3|2|Flying$When Roaming Ghostlight enters the battlefield, return up to one target non-Spirit creature to its owner's hand.|
+Rookie Mistake|Core Set 2021|66|C|{U}|Instant|||Until end of turn, target creature gets +0/+2 and another target creature gets -2/-0.|
 Rousing Read|Core Set 2021|67|C|{2}{U}|Enchantment - Aura|||Enchant creature$When Rousing Read enters the battlefield, draw two cards, then discard a card.$Enchanted creature gets +1/+1 and has flying.|
 Sanctum of Calm Waters|Core Set 2021|68|U|{3}{U}|Legendary Enchantment - Shrine|||At the beginning of your precombat main phase, you may draw X cards, where X is the number of Shrines you control. If you do, discard a card.|
 See the Truth|Core Set 2021|69|R|{1}{U}|Sorcery|||Look at the top three cards of your library. Put one of those cards into your hand and the rest on the bottom of your library in any order. If this spell was cast from anywhere other than your hand, put each of those cards into your hand instead.|
@@ -37496,18 +37518,31 @@ Teferi's Protege|Core Set 2021|77|C|{2}{U}|Creature - Human Wizard|2|3|{1}{U}, {
 Teferi's Tutelage|Core Set 2021|78|U|{2}{U}|Enchantment|||When Teferi's Tutelage enters the battlefield, draw a card, then discard a card.$Whenever you draw a card, target opponent mills two cards.|
 Tide Skimmer|Core Set 2021|79|U|{3}{U}|Creature - Drake|2|3|Flying$Whenever you attack with two or more creatures with flying, draw a card.|
 Tolarian Kraken|Core Set 2021|80|U|{4}{U}{U}|Creature - Kraken|4|6|Whenever you draw a card, you may pay {1}. When you do, you may tap or untap target creature.|
+Tome Anima|Core Set 2021|81|C|{3}{U}|Creature - Spirit|3|3|Tome Anima can't be blocked as long as you've drawn two or more cards this turn.|
 Unsubstantiate|Core Set 2021|82|U|{1}{U}|Instant|||Return target spell or creature to its owner's hand.|
+Vodalian Arcanist|Core Set 2021|83|C|{1}{U}|Creature - Merfolk Wizard|1|3|{T}: Add {C}. Spend this mana only to cast an instant or sorcery spell.|
 Waker of Waves|Core Set 2021|84|U|{5}{U}{U}|Creature - Whale|7|7|Creatures your opponents control get -1/-0.${1}{U}, Discard Waker of Waves: Look at the top two cards of your library. Put one of them into your hand and the other into your graveyard.|
+Wall of Runes|Core Set 2021|85|C|{U}|Creature - Wall|0|4|Defender$When Wall of Runes enters the battlefield, scry 1.|
+Wishcoin Crab|Core Set 2021|86|C|{3}{U}|Creature - Crab|2|5||
+Alchemist's Gift|Core Set 2021|87|C|{B}|Instant|||Target creature gets +1/+1 and gains your choice of deathtouch or lifelink until end of turn.|
 Archfiend's Vessel|Core Set 2021|88|U|{B}|Creature - Human Cleric|1|1|Lifelink$When Archfiend's Vessel enters the battlefield, if it entered from your graveyard or you cast it from your graveyard, exile it. If you do, create a 5/5 black Demon creature token with flying.|
 Bad Deal|Core Set 2021|89|U|{4}{B}{B}|Sorcery|||You draw two cards and each opponent discards two cards. Each player loses 2 life.|
+Blood Glutton|Core Set 2021|90|C|{4}{B}|Creature - Vampire|4|3|Lifelink|
+Caged Zombie|Core Set 2021|91|C|{2}{B}|Creature - Zombie|2|3|{1}{B}, {T}: Each opponent loses 2 life. Activate this ability only if a creature died this turn.|
 Carrion Grub|Core Set 2021|92|U|{3}{B}|Creature - Insect|0|5|Carrion Grub gets +X/+0, where X is the greatest power among creature cards in your graveyard.$When Carrion Grub enters the battlefield, mill four cards.|
+Crypt Lurker|Core Set 2021|93|C|{3}{B}|Creature - Horror|3|4|When Crypt Lurker enters the battlefield, you may sacrifice a creature or discard a creature card. If you do, draw a card.|
+Deathbloom Thallid|Core Set 2021|94|C|{2}{B}|Creature - Fungus|3|2|When Deathbloom Thallid dies, create a 1/1 green Saproling creature token.|
+Demonic Embrace|Core Set 2021|95|R|{1}{B}{B}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +3/+1, has flying, and is a Demon in addition to its other types.$You may cast Demonic Embrace from your graveyard by paying 3 life and discarding a card in addition to paying its other costs.|
 Duress|Core Set 2021|96|C|{B}|Sorcery|||Target opponent reveals their hand. You choose a noncreature, nonland card from it. That player discards that card.|
 Eliminate|Core Set 2021|97|U|{1}{B}|Instant|||Destroy target creature or planeswalker with converted mana cost 3 or less.|
+Fetid Imp|Core Set 2021|98|C|{1}{B}|Creature - Imp|1|2|Flying${B}: Fetid Imp gains deathtouch until end of turn.|
 Finishing Blow|Core Set 2021|99|C|{4}{B}|Instant|||Destroy target creature or planeswalker.|
+Gloom Sower|Core Set 2021|100|C|{5}{B}{B}|Creature - Horror|8|6|Whenever Gloom Sower becomes blocked by a creature, that creature's controller loses 2 life and you gain 2 life.|
 Goremand|Core Set 2021|101|U|{4}{B}{B}|Creature - Demon|5|5|As an additional cost to cast this spell, sacrifice a creature.$Flying$Trample$When Goremand enters the battlefield, each opponent sacrifices a creature.|
 Grasp of Darkness|Core Set 2021|102|C|{B}{B}|Instant|||Target creature gets -4/-4 until end of turn.|
 Grim Tutor|Core Set 2021|103|M|{1}{B}{B}|Sorcery|||Search your library for a card and put that card into your hand, then shuffle your library. You lose 3 life.|
 Hooded Blightfang|Core Set 2021|104|R|{2}{B}|Creature - Snake|1|4|Deathtouch$Whenever a creature you control with deathtouch attacks, each opponent loses 1 life and you gain 1 life.$Whenever a creature you control with deathtouch deals damage to a planeswalker, destroy that planeswalker.|
+Infernal Scarring|Core Set 2021|105|C|{1}{B}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +2/+0 and has "When this creature dies, draw a card."|
 Kaervek, the Spiteful|Core Set 2021|106|R|{2}{B}{B}|Legendary Creature - Human Warlock|3|2|Other creatures get -1/-1.|
 Kitesail Freebooter|Core Set 2021|107|U|{1}{B}|Creature - Human Pirate|1|2|Flying$When Kitesail Freebooter enters the battlefield, target opponent reveals their hand. You choose a noncreature, nonland card from it. Exile that card until Kitesail Freebooter leaves the battlefield.|
 Liliana, Waker of the Dead|Core Set 2021|108|M|{2}{B}{B}|Legendary Planeswalker - Liliana|4|+1: Each player discards a card. Each opponent who can't loses 3 life.$−3: Target creature gets -X/-X until end of turn, where X is the number of cards in your graveyard.$−7: You get an emblem with "At the beginning of combat on your turn, put target creature card from a graveyard onto the battlefield under your control. It gains haste."|
@@ -37515,44 +37550,69 @@ Liliana's Devotee|Core Set 2021|109|U|{2}{B}|Creature - Human Warlock|2|3|Zombie
 Liliana's Standard Bearer|Core Set 2021|110|R|{2}{B}|Creature - Zombie Knight|3|1|Flash$When Liliana's Standard Bearer enters the battlefield, draw X cards, where X is the number of creatures that died under your control this turn.|
 Liliana's Steward|Core Set 2021|111|C|{B}|Creature - Zombie|1|2|{T}, Sacrifice Liliana's Steward: Target opponent discards a card. Activate this ability only any time you could cast a sorcery.|
 Malefic Scythe|Core Set 2021|112|U|{1}{B}|Artifact - Equipment|||Malefic Scythe enters the battlefield with a soul counter on it.$Equipped creature gets +1/+1 for each soul counter on Malefic Scythe.$Whenever equipped creature dies, put a soul counter on Malefic Scythe.$Equip {1}|
+Masked Blackguard|Core Set 2021|113|C|{1}{B}|Creature - Human Rogue|2|1|Flash${2}{B}: Masked Blackguard gets +1/+1 until end of turn.|
 Massacre Wurm|Core Set 2021|114|M|{3}{B}{B}{B}|Creature - Wurm|6|5|When Massacre Wurm enters the battlefield, creatures your opponents control get -2/-2 until end of turn.$Whenever a creature an opponent controls dies, that player loses 2 life.|
+Mind Rot|Core Set 2021|115|C|{2}{B}|Sorcery|||Target player discards two cards.|
 Necromentia|Core Set 2021|116|R|{1}{B}{B}|Sorcery|||Choose a card name other than a basic land card name. Search target opponent's graveyard, hand, and library for any number of cards with that name and exile them. That player shuffles their library, then creates a 2/2 black Zombie creature token for each card exiled from their hand this way.|
 Peer into the Abyss|Core Set 2021|117|R|{4}{B}{B}{B}|Sorcery|||Target player draws cards equal to half the number of cards in their library and loses half their life. Round up each time.|
 Pestilent Haze|Core Set 2021|118|U|{1}{B}{B}|Sorcery|||Choose one —$• All creatures get -2/-2 until end of turn.$• Remove two loyalty counters from each planeswalker.|
+Rise Again|Core Set 2021|119|C|{4}{B}|Sorcery|||Return target creature card from your graveyard to the battlefield.|
 Sanctum of Stone Fangs|Core Set 2021|120|U|{1}{B}|Legendary Enchantment - Shrine|||At the beginning of your precombat main phase, each opponent loses X life and you gain X life, where X is the number of Shrines you control.|
+Sanguine Indulgence|Core Set 2021|121|C|{3}{B}|Sorcery|||This spell costs {3} less to cast if you've gained 3 or more life this turn.$Return up to two target creature cards from your graveyard to your hand.|
 Silversmote Ghoul|Core Set 2021|122|U|{2}{B}|Creature - Zombie Vampire|3|1|At the beginning of your end step, if you gained 3 or more life this turn, return Silversmote Ghoul from your graveyard to the battlefield tapped.${1}{B}, Sacrifice Silversmote Ghoul: Draw a card.|
+Skeleton Archer|Core Set 2021|123|C|{3}{B}|Creature - Skeleton Archer|3|3|When Skeleton Archer enters the battlefield, it deals 1 damage to any target.|
+Tavern Swindler|Core Set 2021|124|U|{1}{B}|Creature - Human Rogue|2|2|{T}, Pay 3 life: Flip a coin. If you win the flip, you gain 6 life.|
 Thieves' Guild Enforcer|Core Set 2021|125|R|{B}|Creature - Human Rogue|1|1|Flash$Whenever Thieves' Guild Enforcer or another Rogue enters the battlefield under your control, each opponent mills two cards.$As long as an opponent has eight or more cards in their graveyard, Thieves' Guild Enforcer gets +2/+1 and has deathtouch.|
 Village Rites|Core Set 2021|126|C|{B}|Instant|||As an additional cost to cast this spell, sacrifice a creature.$Draw two cards.|
 Vito, Thorn of the Dusk Rose|Core Set 2021|127|R|{2}{B}|Legendary Creature - Vampire Cleric|1|3|Whenever you gain life, target opponent loses that much life.${3}{B}{B}: Creatures you control gain lifelink until end of turn.|
+Walking Corpse|Core Set 2021|128|C|{1}{B}|Creature - Zombie|2|2||
+Witch's Cauldron|Core Set 2021|129|U|{B}|Artifact|||{1}{B}, {T}, Sacrifice a creature: You gain 1 life and draw a card.|
 Battle-Rattle Shaman|Core Set 2021|130|U|{3}{R}|Creature - Goblin Shaman|2|2|At the beginning of combat on your turn, you may have target creature get +2/+0 until end of turn.|
 Bolt Hound|Core Set 2021|131|U|{2}{R}|Creature - Elemental Dog|2|2|Haste$Whenever Bolt Hound attacks, other creatures you control get +1/+0 until end of turn.|
+Bone Pit Brute|Core Set 2021|132|C|{4}{R}{R}|Creature - Cyclops|4|5|Menace$When Bone Pit Brute enters the battlefield, target creature gets +4/+0 until end of turn.|
 Brash Taunter|Core Set 2021|133|R|{4}{R}|Creature - Goblin|1|1|Indestructible$Whenever Brash Taunter is dealt damage, it deals that much damage to target opponent.${2}{R}, {T}: Brash Taunter fights another target creature.|
+Burn Bright|Core Set 2021|134|C|{2}{R}|Instant|||Creatures you control get +2/+0 until end of turn.|
 Chandra, Heart of Fire|Core Set 2021|135|M|{3}{R}{R}|Legendary Planeswalker - Chandra|5|+1: Discard your hand, then exile the top three cards of your library. Until end of turn, you may play cards exiled this way.$+1: Chandra, Heart of Fire deals 2 damage to any target.$−9: Search your graveyard and library for any number of red instant and/or sorcery spells, exile them, then shuffle your library. You may cast them this turn. Add six {R}.|
 Chandra's Incinerator|Core Set 2021|136|R|{5}{R}|Creature - Elemental|6|6|This spell costs {X} less to cast, where X is the total amount of noncombat damage dealt to your opponents this turn.$Trample$Whenever a source you control deals noncombat damage to an opponent, Chandra's Incinerator deals that much damage to target creature or planeswalker that player controls.|
 Chandra's Magmutt|Core Set 2021|137|C|{1}{R}|Creature - Elemental Dog|2|2|{T}: Chandra's Magmutt deals 1 damage to target player or planeswalker.|
 Chandra's Pyreling|Core Set 2021|138|U|{1}{R}|Creature - Elemental Lizard|1|3|Whenever a source you control deals noncombat damage to an opponent, Chandra's Pyreling gets +1/+0 and gains double strike until end of turn.|
 Conspicuous Snoop|Core Set 2021|139|R|{R}{R}|Creature - Goblin Rogue|2|2|Play with the top card of your library revealed.$You may cast Goblin spells from the top of your library.$As long as the top card of your library is a Goblin card, Conspicuous Snoop has all activated abilities of that card.|
+Crash Through|Core Set 2021|140|C|{R}|Sorcery|||Creatures you control gain trample until end of turn.$Draw a card.|
+Destructive Tampering|Core Set 2021|141|C|{2}{R}|Sorcery|||Choose one —$• Destroy target artifact.$• Creatures without flying can't block this turn.|
 Double Vision|Core Set 2021|142|R|{3}{R}{R}|Enchantment|||Whenever you cast your first instant or sorcery spell each turn, copy that spell. You may choose new targets for the copy.|
 Fiery Emancipation|Core Set 2021|143|M|{3}{R}{R}{R}|Enchantment|||If a source you control would deal damage to a permanent or player, it deals triple that damage to that permanent or player instead.|
 Furious Rise|Core Set 2021|144|U|{2}{R}|Enchantment|||At the beginning of your end step, if you control a creature with power 4 or greater, exile the top card of your library. You may play that card until you exile another card with Furious Rise.|
+Furor of the Bitten|Core Set 2021|145|C|{R}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +2/+2 and attacks each combat if able.|
 Gadrak, the Crown-Scourge|Core Set 2021|146|R|{2}{R}|Legendary Creature - Dragon|5|4|Flying$Gadrak, the Crown-Scourge can't attack unless you control four or more artifacts.$At the beginning of your end step, create a Treasure token for each nontoken creature that died this turn.|
 Goblin Arsonist|Core Set 2021|147|C|{R}|Creature - Goblin Shaman|1|1|When Goblin Arsonist dies, you may have it deal 1 damage to any target.|
+Goblin Wizardry|Core Set 2021|148|C|{3}{R}|Instant|||Create two 1/1 red Goblin Wizard creature tokens with prowess.|
 Havoc Jester|Core Set 2021|149|U|{4}{R}|Creature - Devil|5|5|Whenever you sacrifice a permanent, Havoc Jester deals 1 damage to any target.|
 Heartfire Immolator|Core Set 2021|150|U|{1}{R}|Creature - Human Wizard|2|2|Prowess${R}, Sacrifice Heartfire Immolator: It deals damage equal to its power to target creature or planeswalker.|
 Hellkite Punisher|Core Set 2021|151|U|{5}{R}{R}|Creature - Dragon|6|6|Flying${R}: Hellkite Punisher gets +1/+0 until end of turn.|
+Hobblefiend|Core Set 2021|152|C|{1}{R}|Creature - Devil|2|1|Trample${1}, Sacrifice another creature: Put a +1/+1 counter on Hobblefiend.|
 Igneous Cur|Core Set 2021|153|C|{1}{R}|Creature - Elemental Dog|1|2|{1}{R}: Igneous Cur gets +2/+0 until end of turn.|
 Kinetic Augur|Core Set 2021|154|U|{3}{R}|Creature - Human Shaman|*|4|Trample$Kinetic Augur's power is equal to the number of instant and sorcery cards in your graveyard.$When Kinetic Augur enters the battlefield, discard up to two cards, then draw that many cards.|
+Onakke Ogre|Core Set 2021|155|C|{2}{R}|Creature - Ogre Warrior|4|2||
+Pitchburn Devils|Core Set 2021|156|C|{4}{R}|Creature - Devil|3|3|When Pitchburn Devils dies, it deals 3 damage to any target.|
 Sanctum of Shattered Heights|Core Set 2021|157|U|{2}{R}|Legendary Enchantment - Shrine|||{1}, Discard a land card or Shrine card: Sanctum of Shattered Heights deals X damage to target creature or planeswalker, where X is the number of Shrines you control.|
+Scorching Dragonfire|Core Set 2021|158|C|{1}{R}|Instant|||Scorching Dragonfire deals 3 damage to target creature or planeswalker. If that creature or planeswalker would die this turn, exile it instead.|
+Shock|Core Set 2021|159|C|{R}|Instant|||Shock deals 2 damage to any target.|
 Soul Sear|Core Set 2021|160|U|{2}{R}|Instant|||Soul Sear deals 5 damage to target creature or planeswalker. That permanent loses indestructible until end of turn.|
+Spellgorger Weird|Core Set 2021|161|C|{2}{R}|Creature - Weird|2|2|Whenever you cast a noncreature spell, put a +1/+1 counter on Spellgorger Weird.|
 Subira, Tulzidi Caravanner|Core Set 2021|162|R|{2}{R}|Legendary Creature - Human Shaman|2|3|Haste${1}: Another target creature with power 2 or less can't be blocked this turn.${1}{R}, {T}, Discard your hand: Until end of turn, whenever a creature you control with power 2 or less deals combat damage to a player, draw a card.|
+Sure Strike|Core Set 2021|163|C|{1}{R}|Instant|||Target creature gets +3/+0 and gains first strike until end of turn.|
 Terror of the Peaks|Core Set 2021|164|M|{3}{R}{R}|Creature - Dragon|5|4|Flying$Spells your opponents cast that target Terror of the Peaks cost an additional 3 life to cast.$Whenever another creature enters the battlefield under your control, Terror of the Peaks deals damage equal to that creature's power to any taret.|
+Thrill of Possibility|Core Set 2021|165|C|{1}{R}|Instant|||As an additional cost to cast this spell, discard a card.$Draw two cards.|
 Traitorous Greed|Core Set 2021|166|U|{3}{R}|Sorcery|||Gain control of target creature until end of turn. Untap that creature. It gains haste until end of turn. Add two mana of any one color.|
 Transmogrify|Core Set 2021|167|R|{3}{R}|Sorcery|||Exile target creature. That creature's controller reveals cards from the top of their library until they reveal a creature card. That player puts that card onto the battlefield, then shuffles the rest into their library.|
+Turn to Slag|Core Set 2021|168|C|{3}{R}{R}|Sorcery|||Turn to Slag deals 5 damage to target creature. Destroy all Equipment attached to that creature.|
+Turret Ogre|Core Set 2021|169|C|{3}{R}|Creature - Ogre Warrior|4|3|Reach$When Turret Ogre enters the battlefield, if you control another creature with power 4 or greater, Turret Ogre deals 2 damage to each opponent.|
 Unleash Fury|Core Set 2021|170|U|{1}{R}|Instant|||Double the power of target creature until end of turn.|
 Volcanic Geyser|Core Set 2021|171|U|{X}{R}{R}|Instant|||Volcanic Geyser deals X damage to any target.|
 Volcanic Salvo|Core Set 2021|172|R|{10}{R}{R}|Sorcery|||This spell costs {X} less to cast, where X is the total power of creatures you control.$Volcanic Salvo deals 6 damage to each of up to two target creatures and/or planeswalkers.|
 Azusa, Lost but Seeking|Core Set 2021|173|R|{2}{G}|Legendary Creature - Human Monk|1|2|You may play two additional lands on each of your turns.|
 Burlfist Oak|Core Set 2021|174|U|{2}{G}{G}|Creature - Treefolk|2|3|Whenever you draw a card, Burlfist Oak gets +2/+2 until end of turn.|
+Canopy Stalker|Core Set 2021|175|U|{3}{G}|Creature - Cat|4|2|Canopy Stalker must be blocked if able.$When Canopy Stalker dies you gain 1 life for each creature that died this turn.|
 Colossal Dreadmaw|Core Set 2021|176|C|{4}{G}{G}|Creature - Dinosaur|6|6|Trample|
 Cultivate|Core Set 2021|177|U|{2}{G}|Sorcery|||Search your library for up to two basic land cards, reveal those cards, and put one onto the battlefield tapped and the other into your hand. Then shuffle your library.|
 Drowsing Tyrannodon|Core Set 2021|178|C|{1}{G}|Creature - Dinosaur|3|3|Defender$As long as you control a creature with power 4 or greater, Drowsing Tyrannodon can attack as though it didn't have defender.|
@@ -37564,18 +37624,31 @@ Garruk, Unleashed|Core Set 2021|183|M|{2}{G}{G}|Legendary Planeswalker - Garruk|
 Garruk's Gorehorn|Core Set 2021|184|C|{4}{G}|Creature - Beast|7|3||
 Garruk's Harbinger|Core Set 2021|185|R|{1}{G}{G}|Creature - Beast|4|3|Hexproof from Black$Whenever Garruk's Harbinger deals combat damage to a player or planeswalker, look at that many cards from the top of your library. You may reveal a creature card or Garruk planeswalker card from among them and put it into your hand. Put the rest on the bottom of your library in a random order.|
 Garruk's Uprising|Core Set 2021|186|U|{2}{G}|Enchantment|||When Garruk's Uprising enters the battlefield, if you control a creature with power 4 or greater, draw a card.$Creatures you control have trample.$Whenever a creature with power 4 or greater enters the battlefield under your control, draw a card.|
+Gnarled Sage|Core Set 2021|187|C|{3}{G}{G}|Creature - Treefolk Druid|4|4|Reach$As long as you've drawn two or more cards this turn, Gnarled Sage gets +0/+2 and has vigilance.|
 Heroic Intervention|Core Set 2021|188|R|{1}{G}|Instant|||Permanents you control gain hexproof and indestructible until end of turn.|
+Hunter's Edge|Core Set 2021|189|C|{3}{G}|Sorcery|||Put a +1/+1 counter on target creature you control. Then that creature deals damage equal to its power to target creature you don't control.|
 Invigorating Surge|Core Set 2021|190|U|{2}{G}|Instant|||Put a +1/+1 counter on target creature you control, then double the number of +1/+1 counters on that creature.|
 Jolrael, Mwonvuli Recluse|Core Set 2021|191|R|{1}{G}|Legendary Creature - Human Druid|1|2|Whenever you draw your second card each turn, create a 2/2 green Cat creature token.${4}{G}{G}: Until end of turn, creatures you control have base power and toughness X/X, where X is the number of cards in your hand.|
+Life Goes On|Core Set 2021|192|C|{G}|Instant|||You gain 4 life. If a creature died this turn, you gain 8 life instead.|
 Llanowar Visionary|Core Set 2021|193|C|{2}{G}|Creature - Elf Druid|2|2|When Llanowar Visionary enters the battlefield, draw a card.${T}: Add {G}.|
+Ornery Dilophosaur|Core Set 2021|194|C|{3}{G}|Creature - Dinosaur|2|2|Deathtouch$Whenever Ornery Dilophosaur attacks, if you control a creature with power 4 or greater, Ornery Dilophosaur gets +2/+2 until end of turn.|
+Portcullis Vine|Core Set 2021|195|C|{G}|Creature - Plant Wall|0|3|Defender${2}, {T}, Sacrifice a creature with defender: Draw a card.|
 Pridemalkin|Core Set 2021|196|C|{2}{G}|Creature - Cat|2|1|When Pridemalkin enters the battlefield, put a +1/+1 counter on target creature you control.$Each creature you control with a +1/+1 counter on it has trample.|
 Primal Might|Core Set 2021|197|R|{X}{G}|Sorcery|||Target creature you control gets +X/+X until end of turn. Then it fights up to one target creature you don't control.|
 Quirion Dryad|Core Set 2021|198|U|{1}{G}|Creature - Dryad|1|1|Whenever you cast a white, blue, black, or red spell, put a +1/+1 counter on Quirion Dryad.|
 Ranger's Guile|Core Set 2021|199|C|{G}|Instant|||Target creature you control gets +1/+1 and gains hexproof until end of turn.|
+Return to Nature|Core Set 2021|200|C|{1}{G}|Instant|||Choose one —$• Destroy target artifact.$• Destroy target enchantment.$• Exile target card from a graveyard.|
+Run Afoul|Core Set 2021|201|C|{G}|Instant|||Target opponent sacrifices a creature with flying.|
 Sabertooth Mauler|Core Set 2021|202|C|{3}{G}|Creature - Cat|3|3|At the beginning of your end step, if a creature died this turn, put a +1/+1 counter on Sabertooth Mauler and untap it.|
 Sanctum of Fruitful Harvest|Core Set 2021|203|U|{2}{G}|Legendary Enchantment - Shrine|||At the beginning of your precombat main phase, add X mana of any one color, where X is the number of Shrines you control.|
 Scavenging Ooze|Core Set 2021|204|R|{1}{G}|Creature - Ooze|2|2|{G}: Exile target card from a graveyard. If it was a creature card, put a +1/+1 counter on Scavenging Ooze and you gain 1 life.|
+Setessan Training|Core Set 2021|205|C|{1}{G}|Enchantment - Aura|||Enchant creature you control$When Setessan Training enters the battlefield, draw a card.$Enchanted creature gets +1/+0 and has trample.|
+Skyway Sniper|Core Set 2021|206|U|{G}|Creature - Elf Archer|1|2|Reach${2}{G}: Skyway Sniper deals 1 damage to target creature with flying.|
+Snarespinner|Core Set 2021|207|C|{1}{G}|Creature - Spider|1|3|Reach$Whenever Snarespinner blocks a creature with flying, Snarespinner gets +2/+0 until end of turn.|
 Sporeweb Weaver|Core Set 2021|208|R|{2}{G}|Creature - Spider|1|4|Reach, hexproof from blue$Whenever Sporeweb Weaver is dealt damage, you gain 1 life and create a 1/1 green Saproling creature token.|
+Thrashing Brontodon|Core Set 2021|209|U|{1}{G}{G}|Creature - Dinosaur|3|4|{1}, Sacrifice Thrashing Brontodon: Destroy target artifact or enchantment.|
+Titanic Growth|Core Set 2021|210|C|{1}{G}|Instant|||Target creature gets +4/+4 until end of turn.|
+Track Down|Core Set 2021|211|C|{1}{G}|Sorcery|||Scry 3, then reveal the top card of your library. If it's a creature or land card, draw a card.|
 Trufflesnout|Core Set 2021|212|C|{2}{G}|Creature - Boar|2|2|When Trufflesnout enters the battlefield, choose one —$• Put a +1/+1 counter on Trufflesnout.$• You gain 4 life.|
 Warden of the Woods|Core Set 2021|213|U|{4}{G}{G}|Creature - Treefolk|5|7|Vigilance$Whenever Warden of the Woods becomes the target of a spell or ability an opponent controls, you may draw two cards.|
 Wildwood Scourge|Core Set 2021|214|U|{X}{G}|Creature - Hydra|0|0|Wildwood Scourge enters the battlefield with X +1/+1 counters on it.$Whenever one or more +1/+1 counters are put on another non-Hydra creature you control, put a +1/+1 counter on Wildwood Scourge.|
@@ -37590,29 +37663,46 @@ Niambi, Esteemed Speaker|Core Set 2021|222|R|{W}{U}|Legendary Creature - Human C
 Obsessive Stitcher|Core Set 2021|223|U|{1}{U}{B}|Creature - Human Wizard|0|3|{T}: Draw a card, then discard a card.${2}{U}{B}, {T}, Sacrifice Obsessive Stitcher: Return target creature card from your graveyard to the battlefield.|
 Radha, Heart of Keld|Core Set 2021|224|R|{1}{R}{G}|Legendary Creature - Elf Warrior|3|3|As long as it's your turn, Radha, Heart of Keld has first strike.$You may look at the top card of your library any time, and you may play lands from the top of your library.${4}{R}{G}: Radha gets +X/+X until end of turn, where X is the number of lands you control.|
 Sanctum of All|Core Set 2021|225|R|{W}{U}{B}{R}{G}|Legendary Enchantment - Shrine|||At the beginning of your upkeep, you may search your library and/or graveyard for a Shrine card and put it onto the battlefield. If you search your library this way, shuffle it.$If an ability of another Shrine you control triggers while you control six or more Shrines, that ability triggers an additional time.|
+Twinblade Assassin|Core Set 2021|226|U|{3}{B}{G}|Creature - Elf Assassin|5|4|At the beginning of your end step, if a creature died this turn, draw a card.|
 Watcher of the Spheres|Core Set 2021|227|U|{W}{U}|Creature - Bird Wizard|2|2|Flying$Creature spells with flying you cast cost {1} less to cast.$Whenever another creature with flying enters the battlefield under your control, Watcher of the Spheres gets +1/+1 until end of turn.|
 Chromatic Orrery|Core Set 2021|228|M|{7}|Legendary Artifact|||You may spend mana as though it were mana of any color.${T}: Add {C}{C}{C}{C}{C}.${5}, {T}: Draw a card for each color among permanents you control.|
+Chrome Replicator|Core Set 2021|229|U|{5}|Artifact Creature - Construct|4|4|When Chrome Replicator enters the battlefield, if you control two or more nonland, nontoken permanents with the same name as one another, create a 4/4 colorless Construct artifact creature token.|
+Epitaph Golem|Core Set 2021|230|U|{5}|Artifact Creature - Golem|3|5|{2}: Put target card from your graveyard on the bottom of your library.|
+Forgotten Sentinel|Core Set 2021|231|C|{4}|Artifact Creature - Golem|4|3|Forgotten Sentinel enters the battlefield tapped.|
 Mazemind Tome|Core Set 2021|232|R|{2}|Artifact|||{T}, Put a page counter on Mazemind Tome: Scry 1.${2}, {T}, Put a page counter on Mazemind Tome: Draw a card.$When there are four or more page counters on Mazemind Tome, exile it. If you do, you gain 4 life.|
 Meteorite|Core Set 2021|233|U|{5}|Artifact|||When Meteorite enters the battlefield, it deals 2 damage to any target.${T}: Add one mana of any color.|
 Palladium Myr|Core Set 2021|234|U|{3}|Artifact Creature - Myr|2|2|{T}: Add {C}{C}.|
+Prismite|Core Set 2021|235|C|{2}|Artifact Creature - Golem|2|1|{2}: Add one mana of any color.|
+Short Sword|Core Set 2021|236|C|{1}|Artifact - Equipment|||Equipped creature gets +1/+1.$Equip {1}|
+Silent Dart|Core Set 2021|237|C|{1}|Artifact|||{4}, {T}, Sacrifice Silent Dart: It deals 3 damage to target creature.|
+Skyscanner|Core Set 2021|238|C|{3}|Artifact Creature - Thopter|1|1|Flying$When Skyscanner enters the battlefield, draw a card.|
 Solemn Simulacrum|Core Set 2021|239|R|{4}|Artifact Creature - Golem|2|2|When Solemn Simulacrum enters the battlefield, you may search your library for a basic land card, put that card onto the battlefield tapped, then shuffle your library.$When Solemn Simulacrum dies, you may draw a card.|
 Sparkhunter Masticore|Core Set 2021|240|R|{3}|Artifact Creature - Masticore|3|4|As an additional cost to cast this spell, discard a card.$Protection from planeswalkers${1}: Sparkhunter Masticore deals 1 damage to target planeswalker.${3}: Sparkhunter Masticore gains indestructible until end of turn.|
 Tormod's Crypt|Core Set 2021|241|U|{0}|Artifact|||{T}, Sacrifice Tormod's Crypt: Exile all cards from target player's graveyard.|
 Animal Sanctuary|Core Set 2021|242|R||Land|||{T}: Add {C}.${2}, {T}: Put a +1/+1 counter on target Bird, Cat, Dog, Goat, Ox, or Snake.|
+Bloodfell Caves|Core Set 2021|243|C||Land|||Bloodfell Caves enters the battlefield tapped.$When Bloodfell Caves enters the battlefield, you gain 1 life.${T}: Add {B} or {R}.|
+Blossoming Sands|Core Set 2021|244|C||Land|||Blossoming Sands enters the battlefield tapped.$When Blossoming Sands enters the battlefield, you gain 1 life.${T}: Add {G} or {W}.|
+Dismal Backwater|Core Set 2021|245|C||Land|||Dismal Backwater enters the battlefield tapped.$When Dismal Backwater enters the battlefield, you gain 1 life.${T}: Add {U} or {B}.|
 Fabled Passage|Core Set 2021|246|R||Land|||{T}, Sacrifice Fabled Passage: Search your library for a basic land card, put it onto the battlefield tapped, then shuffle your library. Then if you control four or more lands, untap that land.|
+Jungle Hollow|Core Set 2021|247|C||Land|||Jungle Hollow enters the battlefield tapped.$When Jungle Hollow enters the battlefield, you gain 1 life.${T}: Add {B} or {G}.|
 Radiant Fountain|Core Set 2021|248|C||Land|||When Radiant Fountain enters the battlefield, you gain 2 life.${T}: Add {C}.|
+Rugged Highlands|Core Set 2021|249|C||Land|||Rugged Highlands enters the battlefield tapped.$When Rugged Highlands enters the battlefield, you gain 1 life.${T}: Add {R} or {G}.|
+Scoured Barrens|Core Set 2021|250|C||Land|||Scoured Barrens enters the battlefield tapped.$When Scoured Barrens enters the battlefield, you gain 1 life.${T}: Add {W} or {B}.|
+Swiftwater Cliffs|Core Set 2021|251|C||Land|||Swiftwater Cliffs enters the battlefield tapped.$When Swiftwater Cliffs enters the battlefield, you gain 1 life.${T}: Add {U} or {R}.|
 Temple of Epiphany|Core Set 2021|252|R||Land|||Temple of Epiphany enters the battlefield tapped.$When Temple of Epiphany enters the battlefield, scry 1.${T}: Add {U} or {R}.|
 Temple of Malady|Core Set 2021|253|R||Land|||Temple of Malady enters the battlefield tapped.$When Temple of Malady enters the battlefield, scry 1.${T}: Add {B} or {G}.|
 Temple of Mystery|Core Set 2021|254|R||Land|||Temple of Mystery enters the battlefield tapped.$When Temple of Mystery enters the battlefield, scry 1.${T}: Add {G} or {U}.|
 Temple of Silence|Core Set 2021|255|R||Land|||Temple of Silence enters the battlefield tapped.$When Temple of Silence enters the battlefield, scry 1.${T}: Add {W} or {B}.|
 Temple of Triumph|Core Set 2021|256|R||Land|||Temple of Triumph enters the battlefield tapped.$When Temple of Triumph enters the battlefield, scry 1.${T}: Add {R} or {W}.|
+Thornwood Falls|Core Set 2021|257|C||Land|||Thornwood Falls enters the battlefield tapped.$When Thornwood Falls enters the battlefield, you gain 1 life.${T}: Add {G} or {U}.|
+Tranquil Cove|Core Set 2021|258|C||Land|||Tranquil Cove enters the battlefield tapped.$When Tranquil Cove enters the battlefield, you gain 1 life.${T}: Add {W} or {U}.|
+Wind-Scarred Crag|Core Set 2021|259|C||Land|||Wind-Scarred Crag enters the battlefield tapped.$When Wind-Scarred Crag enters the battlefield, you gain 1 life.${T}: Add {R} or {W}.|
 Plains|Core Set 2021|260|C||Basic Land - Plains|||({T}: Add {W}.)|
 Island|Core Set 2021|263|C||Basic Land - Island|||({T}: Add {U}.)|
 Swamp|Core Set 2021|266|C||Basic Land - Swamp|||({T}: Add {B}.)|
 Mountain|Core Set 2021|269|C||Basic Land - Mountain|||({T}: Add {R}.)|
 Forest|Core Set 2021|272|C||Basic Land - Forest|||({T}: Add {G}.)|
 Rin and Seri, Inseparable|Core Set 2021|278|M|{1}{R}{G}{W}|Legendary Creature - Dog Cat|4|4|Whenever you cast a Dog spell, create a 1/1 green Cat creature token.$Whenever you cast a Cat spell, create a 1/1 white Dog creature token.${R}{G}{W}, {T}: Rin and Seri, Inseparable deals damage to any target equal to the number of Dogs you control. You gain life equal to the number of Cats you control.|
-Containment Priest|Core Set 2021|314|R|{1}{W}|Creature - Human Cleric|2|2|Flash$If a nontoken creature would enter the battlefield and it wasn't cast, exile it instead.|
 Basri, Devoted Paladin|Core Set 2021|320|M|{4}{W}{W}|Legendary Planeswalker - Basri|4|+1: Put a +1/+1 counter on up to one target creature. It gains vigilance until end of turn.$−1: Whenever a creature attacks this turn, put a +1/+1 counter on it.$−6: Creatures you control get +2/+2 and gain flying until end of turn.|
 Adherent of Hope|Core Set 2021|321|C|{1}{W}|Creature - Human Soldier|2|1|At the beginning of combat on your turn, if you control a Basri planeswalker, put a +1/+1 counter on Adherent of Hope.|
 Basri's Aegis|Core Set 2021|322|R|{2}{W}{W}|Sorcery|||Put a +1/+1 counter on each of up to two target creatures. You may search your library and/or graveyard for a card named Basri, Devoted Paladin, reveal it, and put it into your hand. If you search your library this way, shuffle it.|

From f24412caf1511a12ec55f5b2af684cb60f1c5c5b Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 16 Jun 2020 18:13:25 -0400
Subject: [PATCH 313/586] Implemented Blood Glutton

---
 Mage.Sets/src/mage/cards/b/BloodGlutton.java | 36 ++++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java     |  1 +
 2 files changed, 37 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/b/BloodGlutton.java

diff --git a/Mage.Sets/src/mage/cards/b/BloodGlutton.java b/Mage.Sets/src/mage/cards/b/BloodGlutton.java
new file mode 100644
index 0000000000..0936f5a04e
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/b/BloodGlutton.java
@@ -0,0 +1,36 @@
+package mage.cards.b;
+
+import mage.MageInt;
+import mage.abilities.keyword.LifelinkAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class BloodGlutton extends CardImpl {
+
+    public BloodGlutton(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}");
+
+        this.subtype.add(SubType.VAMPIRE);
+        this.power = new MageInt(4);
+        this.toughness = new MageInt(3);
+
+        // Lifelink
+        this.addAbility(LifelinkAbility.getInstance());
+    }
+
+    private BloodGlutton(final BloodGlutton card) {
+        super(card);
+    }
+
+    @Override
+    public BloodGlutton copy() {
+        return new BloodGlutton(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index fcd5d40f26..fb43934092 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -49,6 +49,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Basri's Solidarity", 10, Rarity.UNCOMMON, mage.cards.b.BasrisSolidarity.class));
         cards.add(new SetCardInfo("Basri, Devoted Paladin", 320, Rarity.MYTHIC, mage.cards.b.BasriDevotedPaladin.class));
         cards.add(new SetCardInfo("Battle-Rattle Shaman", 130, Rarity.UNCOMMON, mage.cards.b.BattleRattleShaman.class));
+        cards.add(new SetCardInfo("Blood Glutton", 90, Rarity.COMMON, mage.cards.b.BloodGlutton.class));
         cards.add(new SetCardInfo("Bloodfell Caves", 243, Rarity.COMMON, mage.cards.b.BloodfellCaves.class));
         cards.add(new SetCardInfo("Blossoming Sands", 244, Rarity.COMMON, mage.cards.b.BlossomingSands.class));
         cards.add(new SetCardInfo("Bolt Hound", 131, Rarity.UNCOMMON, mage.cards.b.BoltHound.class));

From a982e838c83b04485e4b9aad190495ea62c6ecfb Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 16 Jun 2020 18:14:58 -0400
Subject: [PATCH 314/586] Implemented Blood Pit Brute

---
 Mage.Sets/src/mage/cards/b/BonePitBrute.java | 48 ++++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java     |  1 +
 2 files changed, 49 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/b/BonePitBrute.java

diff --git a/Mage.Sets/src/mage/cards/b/BonePitBrute.java b/Mage.Sets/src/mage/cards/b/BonePitBrute.java
new file mode 100644
index 0000000000..33c81ee740
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/b/BonePitBrute.java
@@ -0,0 +1,48 @@
+package mage.cards.b;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.effects.common.continuous.BoostTargetEffect;
+import mage.abilities.keyword.MenaceAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+import mage.target.common.TargetCreaturePermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class BonePitBrute extends CardImpl {
+
+    public BonePitBrute(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}{R}");
+
+        this.subtype.add(SubType.CYCLOPS);
+        this.power = new MageInt(4);
+        this.toughness = new MageInt(5);
+
+        // Menace
+        this.addAbility(new MenaceAbility());
+
+        // When Bone Pit Brute enters the battlefield, target creature gets +4/+0 until end of turn.
+        Ability ability = new EntersBattlefieldTriggeredAbility(
+                new BoostTargetEffect(4, 0, Duration.EndOfTurn)
+        );
+        ability.addTarget(new TargetCreaturePermanent());
+        this.addAbility(ability);
+    }
+
+    private BonePitBrute(final BonePitBrute card) {
+        super(card);
+    }
+
+    @Override
+    public BonePitBrute copy() {
+        return new BonePitBrute(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index fb43934092..811aa068d0 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -53,6 +53,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Bloodfell Caves", 243, Rarity.COMMON, mage.cards.b.BloodfellCaves.class));
         cards.add(new SetCardInfo("Blossoming Sands", 244, Rarity.COMMON, mage.cards.b.BlossomingSands.class));
         cards.add(new SetCardInfo("Bolt Hound", 131, Rarity.UNCOMMON, mage.cards.b.BoltHound.class));
+        cards.add(new SetCardInfo("Bone Pit Brute", 132, Rarity.COMMON, mage.cards.b.BonePitBrute.class));
         cards.add(new SetCardInfo("Brash Taunter", 133, Rarity.RARE, mage.cards.b.BrashTaunter.class));
         cards.add(new SetCardInfo("Burlfist Oak", 174, Rarity.UNCOMMON, mage.cards.b.BurlfistOak.class));
         cards.add(new SetCardInfo("Burn Bright", 134, Rarity.COMMON, mage.cards.b.BurnBright.class));

From 71dce18df9180c96f1165af2997afffdc4ba5980 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 16 Jun 2020 18:17:02 -0400
Subject: [PATCH 315/586] Implemented Caged Zombie

---
 Mage.Sets/src/mage/cards/c/CagedZombie.java | 48 +++++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java    |  1 +
 2 files changed, 49 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/c/CagedZombie.java

diff --git a/Mage.Sets/src/mage/cards/c/CagedZombie.java b/Mage.Sets/src/mage/cards/c/CagedZombie.java
new file mode 100644
index 0000000000..a505d648ad
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/c/CagedZombie.java
@@ -0,0 +1,48 @@
+package mage.cards.c;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.condition.common.MorbidCondition;
+import mage.abilities.costs.common.TapSourceCost;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.decorator.ConditionalActivatedAbility;
+import mage.abilities.effects.common.LoseLifeOpponentsEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.Zone;
+import mage.watchers.common.MorbidWatcher;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class CagedZombie extends CardImpl {
+
+    public CagedZombie(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}");
+
+        this.subtype.add(SubType.ZOMBIE);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(3);
+
+        // {1}{B}, {T}: Each opponent loses 2 life. Activate this ability only if a creature died this turn.
+        Ability ability = new ConditionalActivatedAbility(
+                Zone.BATTLEFIELD, new LoseLifeOpponentsEffect(2),
+                new ManaCostsImpl("{1}{B}"), MorbidCondition.instance
+        );
+        ability.addCost(new TapSourceCost());
+        this.addAbility(ability, new MorbidWatcher());
+    }
+
+    private CagedZombie(final CagedZombie card) {
+        super(card);
+    }
+
+    @Override
+    public CagedZombie copy() {
+        return new CagedZombie(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 811aa068d0..89ee09ac29 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -57,6 +57,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Brash Taunter", 133, Rarity.RARE, mage.cards.b.BrashTaunter.class));
         cards.add(new SetCardInfo("Burlfist Oak", 174, Rarity.UNCOMMON, mage.cards.b.BurlfistOak.class));
         cards.add(new SetCardInfo("Burn Bright", 134, Rarity.COMMON, mage.cards.b.BurnBright.class));
+        cards.add(new SetCardInfo("Caged Zombie", 91, Rarity.COMMON, mage.cards.c.CagedZombie.class));
         cards.add(new SetCardInfo("Cancel", 46, Rarity.COMMON, mage.cards.c.Cancel.class));
         cards.add(new SetCardInfo("Capture Sphere", 47, Rarity.COMMON, mage.cards.c.CaptureSphere.class));
         cards.add(new SetCardInfo("Carrion Grub", 92, Rarity.UNCOMMON, mage.cards.c.CarrionGrub.class));

From dd10125d1c4267a5a1dfe41f1f17c73045d8987c Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 16 Jun 2020 18:39:39 -0400
Subject: [PATCH 316/586] Implemented Chrome Replicator

---
 .../src/mage/cards/c/ChromeReplicator.java    | 80 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 81 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/c/ChromeReplicator.java

diff --git a/Mage.Sets/src/mage/cards/c/ChromeReplicator.java b/Mage.Sets/src/mage/cards/c/ChromeReplicator.java
new file mode 100644
index 0000000000..712eef718e
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/c/ChromeReplicator.java
@@ -0,0 +1,80 @@
+package mage.cards.c;
+
+import mage.MageInt;
+import mage.MageObject;
+import mage.abilities.Ability;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.condition.Condition;
+import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
+import mage.abilities.effects.common.CreateTokenEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.filter.FilterPermanent;
+import mage.filter.common.FilterControlledPermanent;
+import mage.filter.predicate.Predicates;
+import mage.filter.predicate.permanent.TokenPredicate;
+import mage.game.Game;
+import mage.game.permanent.token.RetrofitterFoundryToken;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class ChromeReplicator extends CardImpl {
+
+    public ChromeReplicator(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{5}");
+
+        this.subtype.add(SubType.CONSTRUCT);
+        this.power = new MageInt(4);
+        this.toughness = new MageInt(4);
+
+        // When Chrome Replicator enters the battlefield, if you control two or more nonland, nontoken permanents with the same name as one another, create a 4/4 colorless Construct artifact creature token.
+        this.addAbility(new ConditionalInterveningIfTriggeredAbility(
+                new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new RetrofitterFoundryToken())),
+                ChromeReplicatorCondition.instance, "When {this} enters the battlefield, " +
+                "if you control two or more nonland, nontoken permanents with the same name as one another, " +
+                "create a 4/4 colorless Construct artifact creature token."
+        ));
+    }
+
+    private ChromeReplicator(final ChromeReplicator card) {
+        super(card);
+    }
+
+    @Override
+    public ChromeReplicator copy() {
+        return new ChromeReplicator(this);
+    }
+}
+
+enum ChromeReplicatorCondition implements Condition {
+    instance;
+    private static final FilterPermanent filter = new FilterControlledPermanent();
+
+    static {
+        filter.add(Predicates.not(CardType.LAND.getPredicate()));
+        filter.add(Predicates.not(TokenPredicate.instance));
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Map<String, Integer> nameMap = new HashMap<>();
+        return game
+                .getBattlefield()
+                .getActivePermanents(
+                        filter, source.getControllerId(), source.getSourceId(), game
+                ).stream()
+                .filter(Objects::nonNull)
+                .map(MageObject::getName)
+                .filter(Objects::nonNull)
+                .filter(s -> !"".equals(s))
+                .anyMatch(s -> nameMap.compute(s, (x, i) -> i == null ? 1 : Integer.sum(i, 1)) >= 2);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 89ee09ac29..d6f7af9128 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -69,6 +69,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Chandra, Flame's Catalyst", 332, Rarity.MYTHIC, mage.cards.c.ChandraFlamesCatalyst.class));
         cards.add(new SetCardInfo("Chandra, Heart of Fire", 135, Rarity.MYTHIC, mage.cards.c.ChandraHeartOfFire.class));
         cards.add(new SetCardInfo("Chromatic Orrery", 228, Rarity.MYTHIC, mage.cards.c.ChromaticOrrery.class));
+        cards.add(new SetCardInfo("Chrome Replicator", 229, Rarity.UNCOMMON, mage.cards.c.ChromeReplicator.class));
         cards.add(new SetCardInfo("Colossal Dreadmaw", 176, Rarity.COMMON, mage.cards.c.ColossalDreadmaw.class));
         cards.add(new SetCardInfo("Conclave Mentor", 216, Rarity.UNCOMMON, mage.cards.c.ConclaveMentor.class));
         cards.add(new SetCardInfo("Concordia Pegasus", 12, Rarity.COMMON, mage.cards.c.ConcordiaPegasus.class));

From c1ed1c4d0df85eb0fc5e2d4cb8a85f047b2dbb15 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 16 Jun 2020 18:43:44 -0400
Subject: [PATCH 317/586] Implemented Crypt Lurker

---
 Mage.Sets/src/mage/cards/c/CryptLurker.java | 52 +++++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java    |  1 +
 2 files changed, 53 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/c/CryptLurker.java

diff --git a/Mage.Sets/src/mage/cards/c/CryptLurker.java b/Mage.Sets/src/mage/cards/c/CryptLurker.java
new file mode 100644
index 0000000000..153d25dbef
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/c/CryptLurker.java
@@ -0,0 +1,52 @@
+package mage.cards.c;
+
+import mage.MageInt;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.costs.OrCost;
+import mage.abilities.costs.common.DiscardTargetCost;
+import mage.abilities.costs.common.SacrificeTargetCost;
+import mage.abilities.effects.common.DoIfCostPaid;
+import mage.abilities.effects.common.DrawCardSourceControllerEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.filter.StaticFilters;
+import mage.target.common.TargetCardInHand;
+import mage.target.common.TargetControlledPermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class CryptLurker extends CardImpl {
+
+    public CryptLurker(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}");
+
+        this.subtype.add(SubType.HORROR);
+        this.power = new MageInt(3);
+        this.toughness = new MageInt(4);
+
+        // When Crypt Lurker enters the battlefield, you may sacrifice a creature or discard a creature card. If you do, draw a card.
+        this.addAbility(new EntersBattlefieldTriggeredAbility(new DoIfCostPaid(
+                new DrawCardSourceControllerEffect(1),
+                new OrCost(
+                        new SacrificeTargetCost(new TargetControlledPermanent(
+                                StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT
+                        )), new DiscardTargetCost(new TargetCardInHand(StaticFilters.FILTER_CARD_CREATURE_A)),
+                        "sacrifice a creature or discard a creature card"
+                )
+        )));
+    }
+
+    private CryptLurker(final CryptLurker card) {
+        super(card);
+    }
+
+    @Override
+    public CryptLurker copy() {
+        return new CryptLurker(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index d6f7af9128..ad1910dea2 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -75,6 +75,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Concordia Pegasus", 12, Rarity.COMMON, mage.cards.c.ConcordiaPegasus.class));
         cards.add(new SetCardInfo("Containment Priest", 13, Rarity.RARE, mage.cards.c.ContainmentPriest.class));
         cards.add(new SetCardInfo("Crash Through", 140, Rarity.COMMON, mage.cards.c.CrashThrough.class));
+        cards.add(new SetCardInfo("Crypt Lurker", 93, Rarity.COMMON, mage.cards.c.CryptLurker.class));
         cards.add(new SetCardInfo("Cultivate", 177, Rarity.UNCOMMON, mage.cards.c.Cultivate.class));
         cards.add(new SetCardInfo("Deathbloom Thallid", 94, Rarity.COMMON, mage.cards.d.DeathbloomThallid.class));
         cards.add(new SetCardInfo("Defiant Strike", 15, Rarity.COMMON, mage.cards.d.DefiantStrike.class));

From e31add8e0b144de8276d540adfd5fd4d1cd0f1de Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 16 Jun 2020 18:44:39 -0400
Subject: [PATCH 318/586] Implemented Daybreak Charger

---
 .../src/mage/cards/d/DaybreakCharger.java     | 44 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 45 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/d/DaybreakCharger.java

diff --git a/Mage.Sets/src/mage/cards/d/DaybreakCharger.java b/Mage.Sets/src/mage/cards/d/DaybreakCharger.java
new file mode 100644
index 0000000000..21bca3ec70
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/d/DaybreakCharger.java
@@ -0,0 +1,44 @@
+package mage.cards.d;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.effects.common.continuous.BoostTargetEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+import mage.target.common.TargetCreaturePermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class DaybreakCharger extends CardImpl {
+
+    public DaybreakCharger(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}");
+
+        this.subtype.add(SubType.UNICORN);
+        this.power = new MageInt(3);
+        this.toughness = new MageInt(1);
+
+        // When Daybreak Charger enters the battlefield, target creature gets +2/+0 until end of turn.
+        Ability ability = new EntersBattlefieldTriggeredAbility(
+                new BoostTargetEffect(2, 0, Duration.EndOfTurn)
+        );
+        ability.addTarget(new TargetCreaturePermanent());
+        this.addAbility(ability);
+    }
+
+    private DaybreakCharger(final DaybreakCharger card) {
+        super(card);
+    }
+
+    @Override
+    public DaybreakCharger copy() {
+        return new DaybreakCharger(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index ad1910dea2..745a8325ad 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -77,6 +77,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Crash Through", 140, Rarity.COMMON, mage.cards.c.CrashThrough.class));
         cards.add(new SetCardInfo("Crypt Lurker", 93, Rarity.COMMON, mage.cards.c.CryptLurker.class));
         cards.add(new SetCardInfo("Cultivate", 177, Rarity.UNCOMMON, mage.cards.c.Cultivate.class));
+        cards.add(new SetCardInfo("Daybreak Charger", 14, Rarity.COMMON, mage.cards.d.DaybreakCharger.class));
         cards.add(new SetCardInfo("Deathbloom Thallid", 94, Rarity.COMMON, mage.cards.d.DeathbloomThallid.class));
         cards.add(new SetCardInfo("Defiant Strike", 15, Rarity.COMMON, mage.cards.d.DefiantStrike.class));
         cards.add(new SetCardInfo("Destructive Tampering", 141, Rarity.COMMON, mage.cards.d.DestructiveTampering.class));

From 367f754cd42f9944bdaa8f4188ec56eddd502e5e Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 16 Jun 2020 18:45:23 -0400
Subject: [PATCH 319/586] Implemented Forgotten Sentinel

---
 .../src/mage/cards/f/ForgottenSentinel.java   | 36 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 37 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/f/ForgottenSentinel.java

diff --git a/Mage.Sets/src/mage/cards/f/ForgottenSentinel.java b/Mage.Sets/src/mage/cards/f/ForgottenSentinel.java
new file mode 100644
index 0000000000..cfdcd62362
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/f/ForgottenSentinel.java
@@ -0,0 +1,36 @@
+package mage.cards.f;
+
+import mage.MageInt;
+import mage.abilities.common.EntersBattlefieldTappedAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class ForgottenSentinel extends CardImpl {
+
+    public ForgottenSentinel(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{4}");
+
+        this.subtype.add(SubType.GOLEM);
+        this.power = new MageInt(4);
+        this.toughness = new MageInt(3);
+
+        // Forgotten Sentinel enters the battlefield tapped.
+        this.addAbility(new EntersBattlefieldTappedAbility());
+    }
+
+    private ForgottenSentinel(final ForgottenSentinel card) {
+        super(card);
+    }
+
+    @Override
+    public ForgottenSentinel copy() {
+        return new ForgottenSentinel(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 745a8325ad..7e759fc960 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -102,6 +102,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Fiery Emancipation", 143, Rarity.MYTHIC, mage.cards.f.FieryEmancipation.class));
         cards.add(new SetCardInfo("Finishing Blow", 99, Rarity.COMMON, mage.cards.f.FinishingBlow.class));
         cards.add(new SetCardInfo("Forest", 272, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Forgotten Sentinel", 231, Rarity.COMMON, mage.cards.f.ForgottenSentinel.class));
         cards.add(new SetCardInfo("Frantic Inventory", 50, Rarity.COMMON, mage.cards.f.FranticInventory.class));
         cards.add(new SetCardInfo("Frost Breath", 51, Rarity.COMMON, mage.cards.f.FrostBreath.class));
         cards.add(new SetCardInfo("Fungal Rebirth", 182, Rarity.UNCOMMON, mage.cards.f.FungalRebirth.class));

From fdcc0fbc551b44981b7db88be676495f395064e6 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 16 Jun 2020 18:46:45 -0400
Subject: [PATCH 320/586] Implemented Gale Swooper

---
 Mage.Sets/src/mage/cards/g/GaleSwooper.java | 48 +++++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java    |  1 +
 2 files changed, 49 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/g/GaleSwooper.java

diff --git a/Mage.Sets/src/mage/cards/g/GaleSwooper.java b/Mage.Sets/src/mage/cards/g/GaleSwooper.java
new file mode 100644
index 0000000000..14130beddf
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/g/GaleSwooper.java
@@ -0,0 +1,48 @@
+package mage.cards.g;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
+import mage.abilities.keyword.FlyingAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+import mage.target.common.TargetCreaturePermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class GaleSwooper extends CardImpl {
+
+    public GaleSwooper(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}");
+
+        this.subtype.add(SubType.GRIFFIN);
+        this.power = new MageInt(3);
+        this.toughness = new MageInt(2);
+
+        // Flying
+        this.addAbility(FlyingAbility.getInstance());
+
+        // When Gale Swooper enters the battlefield, target creature gains flying until end of turn.
+        Ability ability = new EntersBattlefieldTriggeredAbility(
+                new GainAbilityTargetEffect(FlyingAbility.getInstance(), Duration.EndOfTurn)
+        );
+        ability.addTarget(new TargetCreaturePermanent());
+        this.addAbility(ability);
+    }
+
+    private GaleSwooper(final GaleSwooper card) {
+        super(card);
+    }
+
+    @Override
+    public GaleSwooper copy() {
+        return new GaleSwooper(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 7e759fc960..2a4528c4eb 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -109,6 +109,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Furious Rise", 144, Rarity.UNCOMMON, mage.cards.f.FuriousRise.class));
         cards.add(new SetCardInfo("Furor of the Bitten", 145, Rarity.COMMON, mage.cards.f.FurorOfTheBitten.class));
         cards.add(new SetCardInfo("Gadrak, the Crown-Scourge", 146, Rarity.RARE, mage.cards.g.GadrakTheCrownScourge.class));
+        cards.add(new SetCardInfo("Gale Swooper", 20, Rarity.COMMON, mage.cards.g.GaleSwooper.class));
         cards.add(new SetCardInfo("Garruk's Gorehorn", 184, Rarity.COMMON, mage.cards.g.GarruksGorehorn.class));
         cards.add(new SetCardInfo("Garruk's Warsteed", 337, Rarity.RARE, mage.cards.g.GarruksWarsteed.class));
         cards.add(new SetCardInfo("Garruk, Savage Herald", 336, Rarity.MYTHIC, mage.cards.g.GarrukSavageHerald.class));

From d9674538520ce268fca3bf5bb0098c8bec7317a5 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 16 Jun 2020 19:05:59 -0400
Subject: [PATCH 321/586] Implemented Keen Glidemaster

---
 .../src/mage/cards/k/KeenGlidemaster.java     | 47 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 48 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/k/KeenGlidemaster.java

diff --git a/Mage.Sets/src/mage/cards/k/KeenGlidemaster.java b/Mage.Sets/src/mage/cards/k/KeenGlidemaster.java
new file mode 100644
index 0000000000..9205f83928
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/k/KeenGlidemaster.java
@@ -0,0 +1,47 @@
+package mage.cards.k;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
+import mage.abilities.keyword.FlyingAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+import mage.target.common.TargetCreaturePermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class KeenGlidemaster extends CardImpl {
+
+    public KeenGlidemaster(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}");
+
+        this.subtype.add(SubType.HUMAN);
+        this.subtype.add(SubType.SOLDIER);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(1);
+
+        // {2}{U}: Target creature gains flying until end of turn.
+        Ability ability = new SimpleActivatedAbility(new GainAbilityTargetEffect(
+                FlyingAbility.getInstance(), Duration.EndOfTurn
+        ), new ManaCostsImpl("{2}{U}"));
+        ability.addTarget(new TargetCreaturePermanent());
+        this.addAbility(ability);
+    }
+
+    private KeenGlidemaster(final KeenGlidemaster card) {
+        super(card);
+    }
+
+    @Override
+    public KeenGlidemaster copy() {
+        return new KeenGlidemaster(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 2a4528c4eb..d780841fe7 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -134,6 +134,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Jolrael, Mwonvuli Recluse", 191, Rarity.RARE, mage.cards.j.JolraelMwonvuliRecluse.class));
         cards.add(new SetCardInfo("Jungle Hollow", 247, Rarity.COMMON, mage.cards.j.JungleHollow.class));
         cards.add(new SetCardInfo("Kaervek, the Spiteful", 106, Rarity.RARE, mage.cards.k.KaervekTheSpiteful.class));
+        cards.add(new SetCardInfo("Keen Glidemaster", 54, Rarity.COMMON, mage.cards.k.KeenGlidemaster.class));
         cards.add(new SetCardInfo("Keral Keep Disciples", 334, Rarity.UNCOMMON, mage.cards.k.KeralKeepDisciples.class));
         cards.add(new SetCardInfo("Kitesail Freebooter", 107, Rarity.UNCOMMON, mage.cards.k.KitesailFreebooter.class));
         cards.add(new SetCardInfo("Legion's Judgment", 24, Rarity.COMMON, mage.cards.l.LegionsJudgment.class));

From 5b403c53aba6130807479cb1b00c109e7edd890f Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 16 Jun 2020 19:07:08 -0400
Subject: [PATCH 322/586] Implemented Masked Blackguard

---
 .../src/mage/cards/m/MaskedBlackguard.java    | 46 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 47 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/m/MaskedBlackguard.java

diff --git a/Mage.Sets/src/mage/cards/m/MaskedBlackguard.java b/Mage.Sets/src/mage/cards/m/MaskedBlackguard.java
new file mode 100644
index 0000000000..345956e6ce
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/m/MaskedBlackguard.java
@@ -0,0 +1,46 @@
+package mage.cards.m;
+
+import mage.MageInt;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.effects.common.continuous.BoostSourceEffect;
+import mage.abilities.keyword.FlashAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class MaskedBlackguard extends CardImpl {
+
+    public MaskedBlackguard(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}");
+
+        this.subtype.add(SubType.HUMAN);
+        this.subtype.add(SubType.ROGUE);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(1);
+
+        // Flash
+        this.addAbility(FlashAbility.getInstance());
+
+        // {2}{B}: Masked Blackguard gets +1/+1 until end of turn.
+        this.addAbility(new SimpleActivatedAbility(
+                new BoostSourceEffect(1, 1, Duration.EndOfTurn), new ManaCostsImpl("{2}{B}")
+        ));
+    }
+
+    private MaskedBlackguard(final MaskedBlackguard card) {
+        super(card);
+    }
+
+    @Override
+    public MaskedBlackguard copy() {
+        return new MaskedBlackguard(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index d780841fe7..3c75b4245e 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -154,6 +154,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Makeshift Battalion", 26, Rarity.COMMON, mage.cards.m.MakeshiftBattalion.class));
         cards.add(new SetCardInfo("Malefic Scythe", 112, Rarity.UNCOMMON, mage.cards.m.MaleficScythe.class));
         cards.add(new SetCardInfo("Mangara, the Diplomat", 27, Rarity.MYTHIC, mage.cards.m.MangaraTheDiplomat.class));
+        cards.add(new SetCardInfo("Masked Blackguard", 113, Rarity.COMMON, mage.cards.m.MaskedBlackguard.class));
         cards.add(new SetCardInfo("Massacre Wurm", 114, Rarity.MYTHIC, mage.cards.m.MassacreWurm.class));
         cards.add(new SetCardInfo("Mazemind Tome", 232, Rarity.RARE, mage.cards.m.MazemindTome.class));
         cards.add(new SetCardInfo("Meteorite", 233, Rarity.UNCOMMON, mage.cards.m.Meteorite.class));

From e45bc23f4ef01b24ea3f62cf31ab38a5e1797e08 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 16 Jun 2020 19:08:11 -0400
Subject: [PATCH 323/586] Implemented Rise Again

---
 Mage.Sets/src/mage/cards/r/RiseAgain.java | 33 +++++++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java  |  1 +
 2 files changed, 34 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/r/RiseAgain.java

diff --git a/Mage.Sets/src/mage/cards/r/RiseAgain.java b/Mage.Sets/src/mage/cards/r/RiseAgain.java
new file mode 100644
index 0000000000..a87bc57396
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/r/RiseAgain.java
@@ -0,0 +1,33 @@
+package mage.cards.r;
+
+import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.filter.StaticFilters;
+import mage.target.common.TargetCardInYourGraveyard;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class RiseAgain extends CardImpl {
+
+    public RiseAgain(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{B}");
+
+        // Return target creature card from your graveyard to the battlefield.
+        this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect());
+        this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD));
+    }
+
+    private RiseAgain(final RiseAgain card) {
+        super(card);
+    }
+
+    @Override
+    public RiseAgain copy() {
+        return new RiseAgain(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 3c75b4245e..592b02677e 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -190,6 +190,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Rewind", 63, Rarity.UNCOMMON, mage.cards.r.Rewind.class));
         cards.add(new SetCardInfo("Riddleform", 64, Rarity.UNCOMMON, mage.cards.r.Riddleform.class));
         cards.add(new SetCardInfo("Rin and Seri, Inseparable", 278, Rarity.MYTHIC, mage.cards.r.RinAndSeriInseparable.class));
+        cards.add(new SetCardInfo("Rise Again", 119, Rarity.COMMON, mage.cards.r.RiseAgain.class));
         cards.add(new SetCardInfo("Rousing Read", 67, Rarity.COMMON, mage.cards.r.RousingRead.class));
         cards.add(new SetCardInfo("Rugged Highlands", 249, Rarity.COMMON, mage.cards.r.RuggedHighlands.class));
         cards.add(new SetCardInfo("Runed Halo", 32, Rarity.RARE, mage.cards.r.RunedHalo.class));

From 7cde1ad7340c1020ba007516a8714d85d9b1982e Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 16 Jun 2020 19:09:28 -0400
Subject: [PATCH 324/586] Implemented Staunch Shieldmate

---
 .../src/mage/cards/s/StaunchShieldmate.java   | 33 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 34 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/s/StaunchShieldmate.java

diff --git a/Mage.Sets/src/mage/cards/s/StaunchShieldmate.java b/Mage.Sets/src/mage/cards/s/StaunchShieldmate.java
new file mode 100644
index 0000000000..68f108e0ed
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/StaunchShieldmate.java
@@ -0,0 +1,33 @@
+package mage.cards.s;
+
+import mage.MageInt;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class StaunchShieldmate extends CardImpl {
+
+    public StaunchShieldmate(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}");
+
+        this.subtype.add(SubType.DWARF);
+        this.subtype.add(SubType.SOLDIER);
+        this.power = new MageInt(1);
+        this.toughness = new MageInt(3);
+    }
+
+    private StaunchShieldmate(final StaunchShieldmate card) {
+        super(card);
+    }
+
+    @Override
+    public StaunchShieldmate copy() {
+        return new StaunchShieldmate(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 592b02677e..bf2826d0f1 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -221,6 +221,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Spined Megalodon", 72, Rarity.COMMON, mage.cards.s.SpinedMegalodon.class));
         cards.add(new SetCardInfo("Spirit of Malevolence", 331, Rarity.COMMON, mage.cards.s.SpiritOfMalevolence.class));
         cards.add(new SetCardInfo("Sporeweb Weaver", 208, Rarity.RARE, mage.cards.s.SporewebWeaver.class));
+        cards.add(new SetCardInfo("Staunch Shieldmate", 39, Rarity.COMMON, mage.cards.s.StaunchShieldmate.class));
         cards.add(new SetCardInfo("Storm Caller", 335, Rarity.COMMON, mage.cards.s.StormCaller.class));
         cards.add(new SetCardInfo("Stormwing Entity", 73, Rarity.RARE, mage.cards.s.StormwingEntity.class));
         cards.add(new SetCardInfo("Sublime Epiphany", 74, Rarity.RARE, mage.cards.s.SublimeEpiphany.class));

From b84fd81281e3b4e85519756581422694d2522de8 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 16 Jun 2020 19:11:26 -0400
Subject: [PATCH 325/586] Implemented Swift Response

---
 Mage.Sets/src/mage/cards/s/SwiftResponse.java | 41 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 42 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/s/SwiftResponse.java

diff --git a/Mage.Sets/src/mage/cards/s/SwiftResponse.java b/Mage.Sets/src/mage/cards/s/SwiftResponse.java
new file mode 100644
index 0000000000..1deee7a4ba
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/SwiftResponse.java
@@ -0,0 +1,41 @@
+package mage.cards.s;
+
+import mage.abilities.effects.common.DestroyTargetEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.filter.FilterPermanent;
+import mage.filter.common.FilterCreaturePermanent;
+import mage.filter.predicate.permanent.TappedPredicate;
+import mage.target.TargetPermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class SwiftResponse extends CardImpl {
+
+    private static final FilterPermanent filter = new FilterCreaturePermanent("tapped permanent");
+
+    static {
+        filter.add(TappedPredicate.instance);
+    }
+
+    public SwiftResponse(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}");
+
+        // Destroy target tapped creature.
+        this.getSpellAbility().addEffect(new DestroyTargetEffect());
+        this.getSpellAbility().addTarget(new TargetPermanent(filter));
+    }
+
+    private SwiftResponse(final SwiftResponse card) {
+        super(card);
+    }
+
+    @Override
+    public SwiftResponse copy() {
+        return new SwiftResponse(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index bf2826d0f1..d62539c43a 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -227,6 +227,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Sublime Epiphany", 74, Rarity.RARE, mage.cards.s.SublimeEpiphany.class));
         cards.add(new SetCardInfo("Sure Strike", 163, Rarity.COMMON, mage.cards.s.SureStrike.class));
         cards.add(new SetCardInfo("Swamp", 266, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Swift Response", 40, Rarity.COMMON, mage.cards.s.SwiftResponse.class));
         cards.add(new SetCardInfo("Swiftwater Cliffs", 251, Rarity.COMMON, mage.cards.s.SwiftwaterCliffs.class));
         cards.add(new SetCardInfo("Tavern Swindler", 124, Rarity.UNCOMMON, mage.cards.t.TavernSwindler.class));
         cards.add(new SetCardInfo("Teferi's Ageless Insight", 76, Rarity.RARE, mage.cards.t.TeferisAgelessInsight.class));

From 794a0509b8c7e386cf266c95eb1338fbe5d14c3c Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 16 Jun 2020 19:15:05 -0400
Subject: [PATCH 326/586] Implemented Twinblade Assassin

---
 .../src/mage/cards/t/TwinbladeAssassin.java   | 47 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 48 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/t/TwinbladeAssassin.java

diff --git a/Mage.Sets/src/mage/cards/t/TwinbladeAssassin.java b/Mage.Sets/src/mage/cards/t/TwinbladeAssassin.java
new file mode 100644
index 0000000000..e96a3c1008
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/t/TwinbladeAssassin.java
@@ -0,0 +1,47 @@
+package mage.cards.t;
+
+import mage.MageInt;
+import mage.abilities.common.BeginningOfEndStepTriggeredAbility;
+import mage.abilities.condition.common.MorbidCondition;
+import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
+import mage.abilities.effects.common.DrawCardSourceControllerEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.TargetController;
+import mage.watchers.common.MorbidWatcher;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class TwinbladeAssassin extends CardImpl {
+
+    public TwinbladeAssassin(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{G}");
+
+        this.subtype.add(SubType.ELF);
+        this.subtype.add(SubType.ASSASSIN);
+        this.power = new MageInt(5);
+        this.toughness = new MageInt(4);
+
+        // At the beginning of your end step, if a creature died this turn, draw a card.
+        this.addAbility(new ConditionalInterveningIfTriggeredAbility(
+                new BeginningOfEndStepTriggeredAbility(
+                        new DrawCardSourceControllerEffect(1), TargetController.YOU, false
+                ), MorbidCondition.instance, "At the beginning of your end step, " +
+                "if a creature died this turn, draw a card."
+        ), new MorbidWatcher());
+    }
+
+    private TwinbladeAssassin(final TwinbladeAssassin card) {
+        super(card);
+    }
+
+    @Override
+    public TwinbladeAssassin copy() {
+        return new TwinbladeAssassin(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index d62539c43a..bad2e935f7 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -254,6 +254,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Trufflesnout", 212, Rarity.COMMON, mage.cards.t.Trufflesnout.class));
         cards.add(new SetCardInfo("Turn to Slag", 168, Rarity.COMMON, mage.cards.t.TurnToSlag.class));
         cards.add(new SetCardInfo("Turret Ogre", 169, Rarity.COMMON, mage.cards.t.TurretOgre.class));
+        cards.add(new SetCardInfo("Twinblade Assassin", 226, Rarity.UNCOMMON, mage.cards.t.TwinbladeAssassin.class));
         cards.add(new SetCardInfo("Ugin, the Spirit Dragon", 1, Rarity.MYTHIC, mage.cards.u.UginTheSpiritDragon.class));
         cards.add(new SetCardInfo("Unleash Fury", 170, Rarity.UNCOMMON, mage.cards.u.UnleashFury.class));
         cards.add(new SetCardInfo("Unsubstantiate", 82, Rarity.UNCOMMON, mage.cards.u.Unsubstantiate.class));

From 3b1bc61952ad6b6593ba0e095fe518453ba3c8b5 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 16 Jun 2020 19:16:38 -0400
Subject: [PATCH 327/586] Implemented Warded Battlements

---
 .../src/mage/cards/w/WardedBattlements.java   | 46 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 47 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/w/WardedBattlements.java

diff --git a/Mage.Sets/src/mage/cards/w/WardedBattlements.java b/Mage.Sets/src/mage/cards/w/WardedBattlements.java
new file mode 100644
index 0000000000..f803f264b6
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/w/WardedBattlements.java
@@ -0,0 +1,46 @@
+package mage.cards.w;
+
+import mage.MageInt;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.effects.common.continuous.BoostControlledEffect;
+import mage.abilities.keyword.DefenderAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+import mage.filter.StaticFilters;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class WardedBattlements extends CardImpl {
+
+    public WardedBattlements(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}");
+
+        this.subtype.add(SubType.WALL);
+        this.power = new MageInt(0);
+        this.toughness = new MageInt(3);
+
+        // Defender
+        this.addAbility(DefenderAbility.getInstance());
+
+        // Attacking creatures you control get +1/+0.
+        this.addAbility(new SimpleStaticAbility(new BoostControlledEffect(
+                1, 1, Duration.WhileOnBattlefield,
+                StaticFilters.FILTER_ATTACKING_CREATURES
+        )));
+    }
+
+    private WardedBattlements(final WardedBattlements card) {
+        super(card);
+    }
+
+    @Override
+    public WardedBattlements copy() {
+        return new WardedBattlements(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index bad2e935f7..3741a9ca34 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -268,6 +268,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Waker of Waves", 84, Rarity.UNCOMMON, mage.cards.w.WakerOfWaves.class));
         cards.add(new SetCardInfo("Walking Corpse", 128, Rarity.COMMON, mage.cards.w.WalkingCorpse.class));
         cards.add(new SetCardInfo("Wall of Runes", 85, Rarity.COMMON, mage.cards.w.WallOfRunes.class));
+        cards.add(new SetCardInfo("Warded Battlements", 44, Rarity.COMMON, mage.cards.w.WardedBattlements.class));
         cards.add(new SetCardInfo("Warden of the Woods", 213, Rarity.UNCOMMON, mage.cards.w.WardenOfTheWoods.class));
         cards.add(new SetCardInfo("Wildwood Patrol", 339, Rarity.COMMON, mage.cards.w.WildwoodPatrol.class));
         cards.add(new SetCardInfo("Wildwood Scourge", 214, Rarity.UNCOMMON, mage.cards.w.WildwoodScourge.class));

From 1eb7f57543dcf6fb42a0ff4b835d4b73a210b287 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 16 Jun 2020 19:18:18 -0400
Subject: [PATCH 328/586] Implemented Read the Tides

---
 Mage.Sets/src/mage/cards/r/ReadTheTides.java | 39 ++++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java     |  1 +
 2 files changed, 40 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/r/ReadTheTides.java

diff --git a/Mage.Sets/src/mage/cards/r/ReadTheTides.java b/Mage.Sets/src/mage/cards/r/ReadTheTides.java
new file mode 100644
index 0000000000..d3073cfd94
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/r/ReadTheTides.java
@@ -0,0 +1,39 @@
+package mage.cards.r;
+
+import mage.abilities.Mode;
+import mage.abilities.effects.common.DrawCardSourceControllerEffect;
+import mage.abilities.effects.common.ReturnToHandTargetEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.target.common.TargetCreaturePermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class ReadTheTides extends CardImpl {
+
+    public ReadTheTides(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{5}{U}");
+
+        // Choose one —
+        // • Draw three cards.
+        this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(3));
+
+        // • Return up to two target creatures to their owners' hands.
+        Mode mode = new Mode(new ReturnToHandTargetEffect());
+        mode.addTarget(new TargetCreaturePermanent(0, 1));
+        this.getSpellAbility().addMode(mode);
+    }
+
+    private ReadTheTides(final ReadTheTides card) {
+        super(card);
+    }
+
+    @Override
+    public ReadTheTides copy() {
+        return new ReadTheTides(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 3741a9ca34..bfc903e893 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -185,6 +185,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Rain of Revelation", 61, Rarity.UNCOMMON, mage.cards.r.RainOfRevelation.class));
         cards.add(new SetCardInfo("Rambunctious Mutt", 30, Rarity.COMMON, mage.cards.r.RambunctiousMutt.class));
         cards.add(new SetCardInfo("Ranger's Guile", 199, Rarity.COMMON, mage.cards.r.RangersGuile.class));
+        cards.add(new SetCardInfo("Read the Tides", 62, Rarity.COMMON, mage.cards.r.ReadTheTides.class));
         cards.add(new SetCardInfo("Return to Nature", 200, Rarity.COMMON, mage.cards.r.ReturnToNature.class));
         cards.add(new SetCardInfo("Revitalize", 31, Rarity.COMMON, mage.cards.r.Revitalize.class));
         cards.add(new SetCardInfo("Rewind", 63, Rarity.UNCOMMON, mage.cards.r.Rewind.class));

From d0d028148442e9ebecf2ac2ad7d934ba6383532b Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 16 Jun 2020 19:20:30 -0400
Subject: [PATCH 329/586] Implemented Witch's Cauldron

---
 .../src/mage/cards/w/WitchsCauldron.java      | 42 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 43 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/w/WitchsCauldron.java

diff --git a/Mage.Sets/src/mage/cards/w/WitchsCauldron.java b/Mage.Sets/src/mage/cards/w/WitchsCauldron.java
new file mode 100644
index 0000000000..dec55cffba
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/w/WitchsCauldron.java
@@ -0,0 +1,42 @@
+package mage.cards.w;
+
+import mage.abilities.Ability;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.common.SacrificeTargetCost;
+import mage.abilities.costs.common.TapSourceCost;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.effects.common.DrawCardSourceControllerEffect;
+import mage.abilities.effects.common.GainLifeEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.filter.StaticFilters;
+import mage.target.common.TargetControlledPermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class WitchsCauldron extends CardImpl {
+
+    public WitchsCauldron(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{B}");
+
+        // {1}{B}, {T}, Sacrifice a creature: You gain 1 life and draw a card.
+        Ability ability = new SimpleActivatedAbility(new GainLifeEffect(1), new ManaCostsImpl("{1}{B}"));
+        ability.addCost(new TapSourceCost());
+        ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT)));
+        ability.addEffect(new DrawCardSourceControllerEffect(1).concatBy("and"));
+        this.addAbility(ability);
+    }
+
+    private WitchsCauldron(final WitchsCauldron card) {
+        super(card);
+    }
+
+    @Override
+    public WitchsCauldron copy() {
+        return new WitchsCauldron(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index bfc903e893..ef77c3819d 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -275,5 +275,6 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Wildwood Scourge", 214, Rarity.UNCOMMON, mage.cards.w.WildwoodScourge.class));
         cards.add(new SetCardInfo("Wind-Scarred Crag", 259, Rarity.COMMON, mage.cards.w.WindScarredCrag.class));
         cards.add(new SetCardInfo("Wishcoin Crab", 86, Rarity.COMMON, mage.cards.w.WishcoinCrab.class));
+        cards.add(new SetCardInfo("Witch's Cauldron", 129, Rarity.UNCOMMON, mage.cards.w.WitchsCauldron.class));
     }
 }

From 5a0d99ee6ffdb3872a44cadca52b56e7dad11c3f Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 16 Jun 2020 19:24:29 -0400
Subject: [PATCH 330/586] Implemented Roaming Ghostlight

---
 .../src/mage/cards/r/RoamingGhostlight.java   | 54 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 55 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/r/RoamingGhostlight.java

diff --git a/Mage.Sets/src/mage/cards/r/RoamingGhostlight.java b/Mage.Sets/src/mage/cards/r/RoamingGhostlight.java
new file mode 100644
index 0000000000..75f7bf1158
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/r/RoamingGhostlight.java
@@ -0,0 +1,54 @@
+package mage.cards.r;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.effects.common.ReturnToHandTargetEffect;
+import mage.abilities.keyword.FlyingAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.filter.FilterPermanent;
+import mage.filter.common.FilterCreaturePermanent;
+import mage.filter.predicate.Predicates;
+import mage.target.TargetPermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class RoamingGhostlight extends CardImpl {
+
+    private static final FilterPermanent filter = new FilterCreaturePermanent("non-Spirit creature");
+
+    static {
+        filter.add(Predicates.not(SubType.SPIRIT.getPredicate()));
+    }
+
+    public RoamingGhostlight(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{U}");
+
+        this.subtype.add(SubType.SPIRIT);
+        this.power = new MageInt(3);
+        this.toughness = new MageInt(2);
+
+        // Flying
+        this.addAbility(FlyingAbility.getInstance());
+
+        // When Roaming Ghostlight enters the battlefield, return up to one target non-Spirit creature to its owner's hand.
+        Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnToHandTargetEffect());
+        ability.addTarget(new TargetPermanent(0, 1, filter, false));
+        this.addAbility(ability);
+    }
+
+    private RoamingGhostlight(final RoamingGhostlight card) {
+        super(card);
+    }
+
+    @Override
+    public RoamingGhostlight copy() {
+        return new RoamingGhostlight(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index ef77c3819d..0d37ec8a4a 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -192,6 +192,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Riddleform", 64, Rarity.UNCOMMON, mage.cards.r.Riddleform.class));
         cards.add(new SetCardInfo("Rin and Seri, Inseparable", 278, Rarity.MYTHIC, mage.cards.r.RinAndSeriInseparable.class));
         cards.add(new SetCardInfo("Rise Again", 119, Rarity.COMMON, mage.cards.r.RiseAgain.class));
+        cards.add(new SetCardInfo("Roaming Ghostlight", 65, Rarity.COMMON, mage.cards.r.RoamingGhostlight.class));
         cards.add(new SetCardInfo("Rousing Read", 67, Rarity.COMMON, mage.cards.r.RousingRead.class));
         cards.add(new SetCardInfo("Rugged Highlands", 249, Rarity.COMMON, mage.cards.r.RuggedHighlands.class));
         cards.add(new SetCardInfo("Runed Halo", 32, Rarity.RARE, mage.cards.r.RunedHalo.class));

From 6804216ddca77006e1318b8b2502e17143be7479 Mon Sep 17 00:00:00 2001
From: htrajan <htrajan2002@yahoo.com>
Date: Tue, 16 Jun 2020 18:09:26 -0700
Subject: [PATCH 331/586] Implement garruk cards (#6650)

* Implement Garruk's Harbinger

* inline ability

* Implement Garruk's Uprising

* Implement Garruk, Unleashed

* fix Garruk, Unleashed
---
 .../src/mage/cards/g/GarrukUnleashed.java     | 71 +++++++++++++++
 .../src/mage/cards/g/GarruksHarbinger.java    | 86 +++++++++++++++++++
 .../src/mage/cards/g/GarruksUprising.java     | 65 ++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  3 +
 .../java/mage/verify/VerifyCardDataTest.java  |  4 +-
 ...eginningOfYourEndStepTriggeredAbility.java |  4 +
 .../emblems/GarrukUnleashedEmblem.java        | 21 +++++
 7 files changed, 252 insertions(+), 2 deletions(-)
 create mode 100644 Mage.Sets/src/mage/cards/g/GarrukUnleashed.java
 create mode 100644 Mage.Sets/src/mage/cards/g/GarruksHarbinger.java
 create mode 100644 Mage.Sets/src/mage/cards/g/GarruksUprising.java
 create mode 100644 Mage/src/main/java/mage/game/command/emblems/GarrukUnleashedEmblem.java

diff --git a/Mage.Sets/src/mage/cards/g/GarrukUnleashed.java b/Mage.Sets/src/mage/cards/g/GarrukUnleashed.java
new file mode 100644
index 0000000000..44e56a1345
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/g/GarrukUnleashed.java
@@ -0,0 +1,71 @@
+package mage.cards.g;
+
+import mage.abilities.LoyaltyAbility;
+import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
+import mage.abilities.condition.common.OpponentControlsMoreCondition;
+import mage.abilities.decorator.ConditionalOneShotEffect;
+import mage.abilities.effects.Effect;
+import mage.abilities.effects.common.CreateTokenEffect;
+import mage.abilities.effects.common.GetEmblemEffect;
+import mage.abilities.effects.common.continuous.BoostTargetEffect;
+import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
+import mage.abilities.effects.common.counter.AddCountersSourceEffect;
+import mage.abilities.keyword.TrampleAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+import mage.constants.SuperType;
+import mage.counters.CounterType;
+import mage.filter.common.FilterCreaturePermanent;
+import mage.game.command.emblems.GarrukUnleashedEmblem;
+import mage.game.permanent.token.BeastToken;
+import mage.target.common.TargetCreaturePermanent;
+
+import java.util.UUID;
+
+/**
+ *
+ * @author htrajan
+ */
+public final class GarrukUnleashed extends CardImpl {
+
+    public GarrukUnleashed(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{2}{G}{G}");
+        
+        this.addSuperType(SuperType.LEGENDARY);
+        this.subtype.add(SubType.GARRUK);
+        this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4));
+
+        // +1: Up to one target creature gets +3/+3 and gains trample until end of turn.
+        Effect effect = new BoostTargetEffect(3, 3, Duration.EndOfTurn)
+            .setText("up to one target creature gets +3/+3");
+        LoyaltyAbility ability = new LoyaltyAbility(effect, 1);
+        effect = new GainAbilityTargetEffect(TrampleAbility.getInstance(), Duration.EndOfTurn)
+            .setText("and gains trample until end of turn");
+        ability.addEffect(effect);
+        ability.addTarget(new TargetCreaturePermanent(0, 1));
+        this.addAbility(ability);
+
+        // −2: Create a 3/3 green Beast creature token. Then if an opponent controls more creatures than you, put a loyalty counter on Garruk, Unleashed.
+        ability = new LoyaltyAbility(new CreateTokenEffect(new BeastToken()), -2);
+        ability.addEffect(new ConditionalOneShotEffect(
+                new AddCountersSourceEffect(CounterType.LOYALTY.createInstance()),
+                new OpponentControlsMoreCondition(new FilterCreaturePermanent()))
+                .setText("Then if an opponent controls more creatures than you, put a loyalty counter on {this}"));
+        this.addAbility(ability);
+
+        // −7: You get an emblem with "At the beginning of your end step, you may search your library for a creature card, put it onto the battlefield, then shuffle your library."
+        this.addAbility(new LoyaltyAbility(new GetEmblemEffect(new GarrukUnleashedEmblem()), -7));
+    }
+
+    private GarrukUnleashed(final GarrukUnleashed card) {
+        super(card);
+    }
+
+    @Override
+    public GarrukUnleashed copy() {
+        return new GarrukUnleashed(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/cards/g/GarruksHarbinger.java b/Mage.Sets/src/mage/cards/g/GarruksHarbinger.java
new file mode 100644
index 0000000000..1d21b265a2
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/g/GarruksHarbinger.java
@@ -0,0 +1,86 @@
+package mage.cards.g;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
+import mage.abilities.dynamicvalue.common.StaticValue;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.LookLibraryAndPickControllerEffect;
+import mage.abilities.keyword.HexproofFromBlackAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Outcome;
+import mage.constants.SubType;
+import mage.filter.FilterCard;
+import mage.filter.predicate.Predicates;
+import mage.game.Game;
+
+import java.util.UUID;
+
+/**
+ *
+ * @author htrajan
+ */
+public final class GarruksHarbinger extends CardImpl {
+
+    public GarruksHarbinger(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}{G}");
+        
+        this.subtype.add(SubType.BEAST);
+        this.power = new MageInt(4);
+        this.toughness = new MageInt(3);
+
+        // Hexproof from Black
+        this.addAbility(HexproofFromBlackAbility.getInstance());
+
+        // Whenever Garruk's Harbinger deals combat damage to a player or planeswalker, look at that many cards from the top of your library. You may reveal a creature card or Garruk planeswalker card from among them and put it into your hand. Put the rest on the bottom of your library in a random order.
+        this.addAbility(
+                new DealsCombatDamageToAPlayerTriggeredAbility(new GarruksHarbingerEffect(), false, true)
+                        .setOrPlaneswalker(true)
+        );
+    }
+
+    private GarruksHarbinger(final GarruksHarbinger card) {
+        super(card);
+    }
+
+    @Override
+    public GarruksHarbinger copy() {
+        return new GarruksHarbinger(this);
+    }
+}
+
+class GarruksHarbingerEffect extends OneShotEffect {
+
+    private static final FilterCard filter = new FilterCard("a creature or Garruk planeswalker card");
+
+    static {
+        filter.add(Predicates.or(CardType.CREATURE.getPredicate(), Predicates.and(CardType.PLANESWALKER.getPredicate(), SubType.GARRUK.getPredicate())));
+    }
+
+    GarruksHarbingerEffect() {
+        super(Outcome.Benefit);
+        staticText = "look at that many cards from the top of your library. You may reveal a creature card or Garruk planeswalker card from among them and put it into your hand. Put the rest on the bottom of your library in a random order";
+    }
+
+    private GarruksHarbingerEffect(GarruksHarbingerEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public GarruksHarbingerEffect copy() {
+        return new GarruksHarbingerEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Integer damage = (Integer) getValue("damage");
+        if (damage != null) {
+            LookLibraryAndPickControllerEffect effect = new LookLibraryAndPickControllerEffect(StaticValue.get(damage), false, StaticValue.get(1), filter, false);
+            effect.setBackInRandomOrder(true);
+            return effect.apply(game, source);
+        }
+        return false;
+    }
+}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/g/GarruksUprising.java b/Mage.Sets/src/mage/cards/g/GarruksUprising.java
new file mode 100644
index 0000000000..4ed2994409
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/g/GarruksUprising.java
@@ -0,0 +1,65 @@
+package mage.cards.g;
+
+import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.condition.common.FerociousCondition;
+import mage.abilities.decorator.ConditionalOneShotEffect;
+import mage.abilities.effects.common.DrawCardSourceControllerEffect;
+import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
+import mage.abilities.hint.common.FerociousHint;
+import mage.abilities.keyword.TrampleAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.ComparisonType;
+import mage.constants.Duration;
+import mage.constants.Zone;
+import mage.filter.FilterPermanent;
+import mage.filter.StaticFilters;
+import mage.filter.common.FilterCreaturePermanent;
+import mage.filter.predicate.mageobject.PowerPredicate;
+
+import java.util.UUID;
+
+/**
+ *
+ * @author htrajan
+ */
+public final class GarruksUprising extends CardImpl {
+
+    private static final FilterPermanent filter = new FilterCreaturePermanent("a creature with power 4 or greater");
+
+    static {
+        filter.add(new PowerPredicate(ComparisonType.MORE_THAN, 3));
+    }
+
+    public GarruksUprising(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}");
+
+        // When Garruk's Uprising enters the battlefield, if you control a creature with power 4 or greater, draw a card.
+        this.addAbility(new EntersBattlefieldTriggeredAbility(new ConditionalOneShotEffect(
+                new DrawCardSourceControllerEffect(1), FerociousCondition.instance))
+            .addHint(FerociousHint.instance));
+
+        // Creatures you control have trample.
+        this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect(
+                TrampleAbility.getInstance(), Duration.WhileOnBattlefield,
+                StaticFilters.FILTER_PERMANENT_CREATURES
+        )));
+
+        // Whenever a creature with power 4 or greater enters the battlefield under your control, draw a card.
+        this.addAbility(new EntersBattlefieldControlledTriggeredAbility(
+                Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), filter, false
+        ));
+    }
+
+    private GarruksUprising(final GarruksUprising card) {
+        super(card);
+    }
+
+    @Override
+    public GarruksUprising copy() {
+        return new GarruksUprising(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 0d37ec8a4a..593b5394b5 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -111,8 +111,11 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Gadrak, the Crown-Scourge", 146, Rarity.RARE, mage.cards.g.GadrakTheCrownScourge.class));
         cards.add(new SetCardInfo("Gale Swooper", 20, Rarity.COMMON, mage.cards.g.GaleSwooper.class));
         cards.add(new SetCardInfo("Garruk's Gorehorn", 184, Rarity.COMMON, mage.cards.g.GarruksGorehorn.class));
+        cards.add(new SetCardInfo("Garruk's Harbinger", 185, Rarity.RARE, mage.cards.g.GarruksHarbinger.class));
+        cards.add(new SetCardInfo("Garruk's Uprising", 186, Rarity.UNCOMMON, mage.cards.g.GarruksUprising.class));
         cards.add(new SetCardInfo("Garruk's Warsteed", 337, Rarity.RARE, mage.cards.g.GarruksWarsteed.class));
         cards.add(new SetCardInfo("Garruk, Savage Herald", 336, Rarity.MYTHIC, mage.cards.g.GarrukSavageHerald.class));
+        cards.add(new SetCardInfo("Garruk, Unleashed", 183, Rarity.MYTHIC, mage.cards.g.GarrukUnleashed.class));
         cards.add(new SetCardInfo("Ghostly Pilferer", 52, Rarity.RARE, mage.cards.g.GhostlyPilferer.class));
         cards.add(new SetCardInfo("Glorious Anthem", 21, Rarity.RARE, mage.cards.g.GloriousAnthem.class));
         cards.add(new SetCardInfo("Goblin Arsonist", 147, Rarity.COMMON, mage.cards.g.GoblinArsonist.class));
diff --git a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java
index d8fcf90517..f352a25b8b 100644
--- a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java
+++ b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java
@@ -985,9 +985,9 @@ public class VerifyCardDataTest {
             System.out.println();
             System.out.println(card.getName() + " " + card.getManaCost().getText());
             if (card instanceof SplitCard) {
-                card.getAbilities().getRules(card.getName()).stream().forEach(System.out::println);
+                card.getAbilities().getRules(card.getName()).forEach(System.out::println);
             } else {
-                card.getRules().stream().forEach(System.out::println);
+                card.getRules().forEach(System.out::println);
             }
         });
     }
diff --git a/Mage/src/main/java/mage/abilities/common/BeginningOfYourEndStepTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BeginningOfYourEndStepTriggeredAbility.java
index eb221d6ebb..04854e58b0 100644
--- a/Mage/src/main/java/mage/abilities/common/BeginningOfYourEndStepTriggeredAbility.java
+++ b/Mage/src/main/java/mage/abilities/common/BeginningOfYourEndStepTriggeredAbility.java
@@ -15,6 +15,10 @@ public class BeginningOfYourEndStepTriggeredAbility extends TriggeredAbilityImpl
         super(Zone.BATTLEFIELD, effect, optional);
     }
 
+    public BeginningOfYourEndStepTriggeredAbility(Zone zone, Effect effect, boolean optional) {
+        super(zone, effect, optional);
+    }
+
     public BeginningOfYourEndStepTriggeredAbility(final BeginningOfYourEndStepTriggeredAbility ability) {
         super(ability);
     }
diff --git a/Mage/src/main/java/mage/game/command/emblems/GarrukUnleashedEmblem.java b/Mage/src/main/java/mage/game/command/emblems/GarrukUnleashedEmblem.java
new file mode 100644
index 0000000000..50c394c315
--- /dev/null
+++ b/Mage/src/main/java/mage/game/command/emblems/GarrukUnleashedEmblem.java
@@ -0,0 +1,21 @@
+package mage.game.command.emblems;
+
+import mage.abilities.common.BeginningOfYourEndStepTriggeredAbility;
+import mage.abilities.effects.Effect;
+import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect;
+import mage.constants.Outcome;
+import mage.constants.Zone;
+import mage.filter.StaticFilters;
+import mage.game.command.Emblem;
+import mage.target.common.TargetCardInLibrary;
+
+public class GarrukUnleashedEmblem extends Emblem {
+
+    // At the beginning of your end step, you may search your library for a creature card, put it onto the battlefield, then shuffle your library.
+    public GarrukUnleashedEmblem() {
+        this.setName("Emblem Garruk");
+        Effect effect = new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_CREATURE), false, true, Outcome.PutCreatureInPlay)
+            .setText("search your library for a creature card, put it onto the battlefield, then shuffle your library");
+        this.getAbilities().add(new BeginningOfYourEndStepTriggeredAbility(Zone.COMMAND, effect, true));
+    }
+}

From 03a46ffec1ee56a86daaa9e0e1f402c458ad98b7 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 16 Jun 2020 22:03:14 -0400
Subject: [PATCH 332/586] fixed Ghostly Pilferer triggered ability

---
 .../src/mage/cards/g/GhostlyPilferer.java     |  8 +--
 Mage.Sets/src/mage/cards/h/Hollowsage.java    | 62 +++----------------
 Mage.Sets/src/mage/cards/k/KeyToTheCity.java  | 53 +++-------------
 .../abilities/keyword/InspiredAbility.java    | 61 ++++++++++--------
 Utils/mtg-cards-data.txt                      |  2 +-
 5 files changed, 58 insertions(+), 128 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/g/GhostlyPilferer.java b/Mage.Sets/src/mage/cards/g/GhostlyPilferer.java
index f583333542..68ffecfd52 100644
--- a/Mage.Sets/src/mage/cards/g/GhostlyPilferer.java
+++ b/Mage.Sets/src/mage/cards/g/GhostlyPilferer.java
@@ -2,13 +2,13 @@ package mage.cards.g;
 
 import mage.MageInt;
 import mage.abilities.TriggeredAbilityImpl;
-import mage.abilities.common.BecomesTappedSourceTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.common.DiscardCardCost;
 import mage.abilities.costs.mana.GenericManaCost;
 import mage.abilities.effects.common.DoIfCostPaid;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.abilities.effects.common.combat.CantBeBlockedSourceEffect;
+import mage.abilities.keyword.InspiredAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
@@ -34,10 +34,10 @@ public final class GhostlyPilferer extends CardImpl {
         this.power = new MageInt(2);
         this.toughness = new MageInt(1);
 
-        // Whenever Ghostly Pilferer becomes tapped, you may pay {2}. If you do, draw a card.
-        this.addAbility(new BecomesTappedSourceTriggeredAbility(new DoIfCostPaid(
+        // Whenever Ghostly Pilferer becomes untapped, you may pay {2}. If you do, draw a card.
+        this.addAbility(new InspiredAbility(new DoIfCostPaid(
                 new DrawCardSourceControllerEffect(1), new GenericManaCost(2)
-        )));
+        ), false, false));
 
         // Whenever an opponent casts a spell from anywhere other than their hand, draw a card.
         this.addAbility(new GhostlyPilfererTriggeredAbility());
diff --git a/Mage.Sets/src/mage/cards/h/Hollowsage.java b/Mage.Sets/src/mage/cards/h/Hollowsage.java
index d8d2aa755e..5ccc5a6d99 100644
--- a/Mage.Sets/src/mage/cards/h/Hollowsage.java
+++ b/Mage.Sets/src/mage/cards/h/Hollowsage.java
@@ -1,84 +1,42 @@
-
 package mage.cards.h;
 
-import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.TriggeredAbility;
-import mage.abilities.TriggeredAbilityImpl;
-import mage.abilities.effects.Effect;
+import mage.abilities.Ability;
 import mage.abilities.effects.common.discard.DiscardTargetEffect;
+import mage.abilities.keyword.InspiredAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.SubType;
-import mage.constants.Zone;
-import mage.game.Game;
-import mage.game.events.GameEvent;
-import mage.game.events.GameEvent.EventType;
 import mage.target.TargetPlayer;
 
+import java.util.UUID;
+
 /**
- *
  * @author jeffwadsworth
  */
 public final class Hollowsage extends CardImpl {
-    
+
     public Hollowsage(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}");
         this.subtype.add(SubType.MERFOLK);
         this.subtype.add(SubType.WIZARD);
-        
+
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
 
         // Whenever Hollowsage becomes untapped, you may have target player discard a card.
-        TriggeredAbility ability = new BecomesUntappedTriggeredAbility(new DiscardTargetEffect(1), true);
+        Ability ability = new InspiredAbility(new DiscardTargetEffect(1), true, false);
         ability.addTarget(new TargetPlayer());
         this.addAbility(ability);
-        
     }
-    
+
     public Hollowsage(final Hollowsage card) {
         super(card);
     }
-    
+
     @Override
     public Hollowsage copy() {
         return new Hollowsage(this);
     }
 }
-
-class BecomesUntappedTriggeredAbility extends TriggeredAbilityImpl {
-    
-    public BecomesUntappedTriggeredAbility(Effect effect, boolean isOptional) {
-        super(Zone.BATTLEFIELD, effect, isOptional);
-    }
-
-    public BecomesUntappedTriggeredAbility(Effect effect) {
-        super(Zone.BATTLEFIELD, effect);
-    }
-
-    public BecomesUntappedTriggeredAbility(final BecomesUntappedTriggeredAbility ability) {
-        super(ability);
-    }
-
-    @Override
-    public BecomesUntappedTriggeredAbility copy() {
-        return new BecomesUntappedTriggeredAbility(this);
-    }
-
-    @Override
-    public boolean checkEventType(GameEvent event, Game game) {
-        return event.getType() == EventType.UNTAPPED;
-    }
-
-    @Override
-    public boolean checkTrigger(GameEvent event, Game game) {
-        return event.getTargetId().equals(sourceId);
-    }
-
-    @Override
-    public String getRule() {
-        return "When {this} becomes untapped, " + super.getRule();
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/k/KeyToTheCity.java b/Mage.Sets/src/mage/cards/k/KeyToTheCity.java
index 1fce751780..909321cf6c 100644
--- a/Mage.Sets/src/mage/cards/k/KeyToTheCity.java
+++ b/Mage.Sets/src/mage/cards/k/KeyToTheCity.java
@@ -1,9 +1,6 @@
-
 package mage.cards.k;
 
-import java.util.UUID;
 import mage.abilities.Ability;
-import mage.abilities.TriggeredAbilityImpl;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.common.DiscardCardCost;
 import mage.abilities.costs.common.TapSourceCost;
@@ -11,35 +8,35 @@ import mage.abilities.costs.mana.GenericManaCost;
 import mage.abilities.effects.common.DoIfCostPaid;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.abilities.effects.common.combat.CantBeBlockedTargetEffect;
+import mage.abilities.keyword.InspiredAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.Zone;
-import mage.game.Game;
-import mage.game.events.GameEvent;
-import mage.game.events.GameEvent.EventType;
 import mage.target.common.TargetCreaturePermanent;
 
+import java.util.UUID;
+
 /**
- *
  * @author emerald000
  */
 public final class KeyToTheCity extends CardImpl {
 
     public KeyToTheCity(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{2}");
+        super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}");
 
         // {T}, Discard a card: Up to one target creature can't be blocked this turn.
-        Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CantBeBlockedTargetEffect(), new TapSourceCost());
+        Ability ability = new SimpleActivatedAbility(new CantBeBlockedTargetEffect(), new TapSourceCost());
         ability.addCost(new DiscardCardCost());
         ability.addTarget(new TargetCreaturePermanent(0, 1));
         this.addAbility(ability);
 
         // Whenever Key to the City becomes untapped, you may pay {2}. If you do, draw a card.
-        this.addAbility(new KeyToTheCityTriggeredAbility());
+        this.addAbility(new InspiredAbility(new DoIfCostPaid(
+                new DrawCardSourceControllerEffect(1), new GenericManaCost(2)
+        ), false, false));
     }
 
-    public KeyToTheCity(final KeyToTheCity card) {
+    private KeyToTheCity(final KeyToTheCity card) {
         super(card);
     }
 
@@ -48,35 +45,3 @@ public final class KeyToTheCity extends CardImpl {
         return new KeyToTheCity(this);
     }
 }
-
-class KeyToTheCityTriggeredAbility extends TriggeredAbilityImpl{
-
-    KeyToTheCityTriggeredAbility() {
-        super(Zone.BATTLEFIELD, new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new GenericManaCost(2)));
-    }
-
-    KeyToTheCityTriggeredAbility(final KeyToTheCityTriggeredAbility ability) {
-        super(ability);
-    }
-
-    @Override
-    public KeyToTheCityTriggeredAbility copy() {
-        return new KeyToTheCityTriggeredAbility(this);
-    }
-
-    @Override
-    public boolean checkEventType(GameEvent event, Game game) {
-        return event.getType() == EventType.UNTAPPED;
-    }
-
-    @Override
-    public boolean checkTrigger(GameEvent event, Game game) {
-        return event.getTargetId().equals(this.getSourceId());
-    }
-
-    @Override
-    public String getRule() {
-        return "Whenever Key to the City becomes untapped, you may pay {2}. If you do, draw a card.";
-    }
-
-}
diff --git a/Mage/src/main/java/mage/abilities/keyword/InspiredAbility.java b/Mage/src/main/java/mage/abilities/keyword/InspiredAbility.java
index 5ac808f7da..3e20c1da54 100644
--- a/Mage/src/main/java/mage/abilities/keyword/InspiredAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/InspiredAbility.java
@@ -1,29 +1,29 @@
 /*
-    * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
-    *
-    * Redistribution and use in source and binary forms, with or without modification, are
-    * permitted provided that the following conditions are met:
-    *
-    *    1. Redistributions of source code must retain the above copyright notice, this list of
-    *       conditions and the following disclaimer.
-    *
-    *    2. Redistributions in binary form must reproduce the above copyright notice, this list
-    *       of conditions and the following disclaimer in the documentation and/or other materials
-    *       provided with the distribution.
-    *
-    * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
-    * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
-    * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
-    * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-    * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-    * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
-    * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-    * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-    * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-    *
-    * The views and conclusions contained in the software and documentation are those of the
-    * authors and should not be interpreted as representing official policies, either expressed
-    * or implied, of BetaSteward_at_googlemail.com.
+ * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ *    1. Redistributions of source code must retain the above copyright notice, this list of
+ *       conditions and the following disclaimer.
+ *
+ *    2. Redistributions in binary form must reproduce the above copyright notice, this list
+ *       of conditions and the following disclaimer in the documentation and/or other materials
+ *       provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of BetaSteward_at_googlemail.com.
  */
 package mage.abilities.keyword;
 
@@ -37,21 +37,28 @@ import mage.game.events.GameEvent.EventType;
 /**
  * Inspired ability word
  *
- *
  * @author LevelX2
  */
 public class InspiredAbility extends TriggeredAbilityImpl {
 
+    private final boolean isInspired;
+
     public InspiredAbility(Effect effect) {
         this(effect, false);
     }
 
     public InspiredAbility(Effect effect, boolean optional) {
+        this(effect, optional, true);
+    }
+
+    public InspiredAbility(Effect effect, boolean optional, boolean isInspired) {
         super(Zone.BATTLEFIELD, effect, optional);
+        this.isInspired = isInspired;
     }
 
     public InspiredAbility(final InspiredAbility ability) {
         super(ability);
+        this.isInspired = ability.isInspired;
     }
 
     @Override
@@ -71,6 +78,6 @@ public class InspiredAbility extends TriggeredAbilityImpl {
 
     @Override
     public String getRule() {
-        return "<i>Inspired</i> &mdash; Whenever {this} becomes untapped, " + super.getRule();
+        return (isInspired ? "<i>Inspired</i> &mdash; " : "") + "Whenever {this} becomes untapped, " + super.getRule();
     }
 }
diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt
index 48eb0ed176..87900618f4 100644
--- a/Utils/mtg-cards-data.txt
+++ b/Utils/mtg-cards-data.txt
@@ -37489,7 +37489,7 @@ Discontinuity|Core Set 2021|48|M|{3}{U}{U}{U}|Instant|||As long as it's your tur
 Enthralling Hold|Core Set 2021|49|U|{3}{U}{U}|Enchantment - Aura|||Enchant creature$You can't choose an untapped creature as this spell's target as you cast it.$You control enchanted creature.|
 Frantic Inventory|Core Set 2021|50|C|{1}{U}|Instant|||Draw a card, then draw cards equal to the number of cards named Frantic Inventory in your graveyard.|
 Frost Breath|Core Set 2021|51|C|{2}{U}|Instant|||Tap up to two target creatures. Those creatures don't untap during their controller's next untap step.|
-Ghostly Pilferer|Core Set 2021|52|R|{1}{U}|Creature - Spirit Rogue|2|1|Whenever Ghostly Pilferer becomes tapped, you may pay {2}. If you do, draw a card.$Whenever an opponent casts a spell from anywhere other than their hand, draw a card.$Discard a card: Ghostly Pilferer can't be blocked this turn.|
+Ghostly Pilferer|Core Set 2021|52|R|{1}{U}|Creature - Spirit Rogue|2|1|Whenever Ghostly Pilferer becomes untapped, you may pay {2}. If you do, draw a card.$Whenever an opponent casts a spell from anywhere other than their hand, draw a card.$Discard a card: Ghostly Pilferer can't be blocked this turn.|
 Jeskai Elder|Core Set 2021|53|U|{1}{U}|Creature - Human Monk|1|2|Prowess$Whenever Jeskai Elder deals combat damage to a player, you may draw a card. If you do, discard a card.|
 Keen Glidemaster|Core Set 2021|54|C|{1}{U}|Creature - Human Soldier|2|1|{2}{U}: Target creature gains flying until end of turn.|
 Library Larcenist|Core Set 2021|55|C|{2}{U}|Creature - Merfolk Rogue|1|2|Whenever Library Larcenist attacks, draw a card.|

From bc8734834968cdded528f03cc689c708ae871a7f Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 16 Jun 2020 22:07:41 -0400
Subject: [PATCH 333/586] Implemented Skyway Sniper

---
 Mage.Sets/src/mage/cards/s/SkywaySniper.java | 57 ++++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java     |  1 +
 2 files changed, 58 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/s/SkywaySniper.java

diff --git a/Mage.Sets/src/mage/cards/s/SkywaySniper.java b/Mage.Sets/src/mage/cards/s/SkywaySniper.java
new file mode 100644
index 0000000000..362fae650a
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/SkywaySniper.java
@@ -0,0 +1,57 @@
+package mage.cards.s;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.effects.common.DamageTargetEffect;
+import mage.abilities.keyword.FlyingAbility;
+import mage.abilities.keyword.ReachAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.filter.FilterPermanent;
+import mage.filter.common.FilterCreaturePermanent;
+import mage.filter.predicate.mageobject.AbilityPredicate;
+import mage.target.TargetPermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class SkywaySniper extends CardImpl {
+
+    private static final FilterPermanent filter = new FilterCreaturePermanent("creature with flying");
+
+    static {
+        filter.add(new AbilityPredicate(FlyingAbility.class));
+    }
+
+    public SkywaySniper(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}");
+
+        this.subtype.add(SubType.ELF);
+        this.subtype.add(SubType.ARCHER);
+        this.power = new MageInt(1);
+        this.toughness = new MageInt(2);
+
+        // Reach
+        this.addAbility(ReachAbility.getInstance());
+
+        // {2}{G}: Skyway Sniper deals 1 damage to target creature with flying.
+        Ability ability = new SimpleActivatedAbility(new DamageTargetEffect(1), new ManaCostsImpl("{2}{G}"));
+        ability.addTarget(new TargetPermanent(filter));
+        this.addAbility(ability);
+    }
+
+    private SkywaySniper(final SkywaySniper card) {
+        super(card);
+    }
+
+    @Override
+    public SkywaySniper copy() {
+        return new SkywaySniper(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 593b5394b5..df4e0b800e 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -217,6 +217,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Silversmote Ghoul", 122, Rarity.UNCOMMON, mage.cards.s.SilversmoteGhoul.class));
         cards.add(new SetCardInfo("Skeleton Archer", 123, Rarity.COMMON, mage.cards.s.SkeletonArcher.class));
         cards.add(new SetCardInfo("Skyscanner", 238, Rarity.COMMON, mage.cards.s.Skyscanner.class));
+        cards.add(new SetCardInfo("Skyway Sniper", 206, Rarity.UNCOMMON, mage.cards.s.SkywaySniper.class));
         cards.add(new SetCardInfo("Snarespinner", 207, Rarity.COMMON, mage.cards.s.Snarespinner.class));
         cards.add(new SetCardInfo("Solemn Simulacrum", 239, Rarity.RARE, mage.cards.s.SolemnSimulacrum.class));
         cards.add(new SetCardInfo("Soul Sear", 160, Rarity.UNCOMMON, mage.cards.s.SoulSear.class));

From e3a1fee1ea15ffadab3701a5789f6d220e88cb76 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 16 Jun 2020 22:11:04 -0400
Subject: [PATCH 334/586] Implemented Hunter's Edge

---
 Mage.Sets/src/mage/cards/d/DomrisAmbush.java | 35 ------------------
 Mage.Sets/src/mage/cards/h/HuntersEdge.java  | 38 ++++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java     |  1 +
 3 files changed, 39 insertions(+), 35 deletions(-)
 create mode 100644 Mage.Sets/src/mage/cards/h/HuntersEdge.java

diff --git a/Mage.Sets/src/mage/cards/d/DomrisAmbush.java b/Mage.Sets/src/mage/cards/d/DomrisAmbush.java
index b598f9442e..99eeb781a0 100644
--- a/Mage.Sets/src/mage/cards/d/DomrisAmbush.java
+++ b/Mage.Sets/src/mage/cards/d/DomrisAmbush.java
@@ -1,19 +1,14 @@
 package mage.cards.d;
 
-import mage.abilities.Ability;
-import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.DamageWithPowerFromOneToAnotherTargetEffect;
 import mage.abilities.effects.common.counter.AddCountersTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.Outcome;
 import mage.constants.TargetController;
 import mage.counters.CounterType;
 import mage.filter.FilterPermanent;
 import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent;
-import mage.game.Game;
-import mage.game.permanent.Permanent;
 import mage.target.TargetPermanent;
 import mage.target.common.TargetControlledCreaturePermanent;
 
@@ -52,33 +47,3 @@ public final class DomrisAmbush extends CardImpl {
         return new DomrisAmbush(this);
     }
 }
-
-class DomrisAmbushEffect extends OneShotEffect {
-
-    DomrisAmbushEffect() {
-        super(Outcome.Benefit);
-        staticText = "Put a +1/+1 counter on target creature you control. " +
-                "Then that creature deals damage equal to its power " +
-                "to target creature or planeswalker you don't control.";
-    }
-
-    private DomrisAmbushEffect(final DomrisAmbushEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public DomrisAmbushEffect copy() {
-        return new DomrisAmbushEffect(this);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        Permanent permanent = game.getPermanent(source.getFirstTarget());
-        if (permanent == null) {
-            return false;
-        }
-        permanent.addCounters(CounterType.P1P1.createInstance(), source, game);
-        game.getState().processAction(game);
-        return new DamageWithPowerFromOneToAnotherTargetEffect().apply(game, source);
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/h/HuntersEdge.java b/Mage.Sets/src/mage/cards/h/HuntersEdge.java
new file mode 100644
index 0000000000..919602bf5c
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/h/HuntersEdge.java
@@ -0,0 +1,38 @@
+package mage.cards.h;
+
+import mage.abilities.effects.common.DamageWithPowerFromOneToAnotherTargetEffect;
+import mage.abilities.effects.common.counter.AddCountersTargetEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.counters.CounterType;
+import mage.filter.StaticFilters;
+import mage.target.TargetPermanent;
+import mage.target.common.TargetControlledCreaturePermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class HuntersEdge extends CardImpl {
+
+    public HuntersEdge(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{G}");
+
+        // Put a +1/+1 counter on target creature you control. Then that creature deals damage equal to its power to target creature you don't control.
+        this.getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.P1P1.createInstance()));
+        this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent());
+        this.getSpellAbility().addEffect(new DamageWithPowerFromOneToAnotherTargetEffect("that creature").concatBy("Then"));
+        this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL));
+    }
+
+    private HuntersEdge(final HuntersEdge card) {
+        super(card);
+    }
+
+    @Override
+    public HuntersEdge copy() {
+        return new HuntersEdge(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index df4e0b800e..e196ae0464 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -128,6 +128,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Hellkite Punisher", 151, Rarity.UNCOMMON, mage.cards.h.HellkitePunisher.class));
         cards.add(new SetCardInfo("Heroic Intervention", 188, Rarity.RARE, mage.cards.h.HeroicIntervention.class));
         cards.add(new SetCardInfo("Historian of Zhalfir", 325, Rarity.UNCOMMON, mage.cards.h.HistorianOfZhalfir.class));
+        cards.add(new SetCardInfo("Hunter's Edge", 189, Rarity.COMMON, mage.cards.h.HuntersEdge.class));
         cards.add(new SetCardInfo("Igneous Cur", 153, Rarity.COMMON, mage.cards.i.IgneousCur.class));
         cards.add(new SetCardInfo("Indulging Patrician", 219, Rarity.UNCOMMON, mage.cards.i.IndulgingPatrician.class));
         cards.add(new SetCardInfo("Infernal Scarring", 105, Rarity.COMMON, mage.cards.i.InfernalScarring.class));

From 3839b0c6655709616c4d1d6ab06122761b56a9ae Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 16 Jun 2020 22:14:57 -0400
Subject: [PATCH 335/586] Implemented Goblin Wizardry

---
 .../src/mage/cards/g/GoblinWizardry.java      | 31 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 .../permanent/token/GoblinWizardToken.java    | 31 +++++++++++++++++++
 3 files changed, 63 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/g/GoblinWizardry.java
 create mode 100644 Mage/src/main/java/mage/game/permanent/token/GoblinWizardToken.java

diff --git a/Mage.Sets/src/mage/cards/g/GoblinWizardry.java b/Mage.Sets/src/mage/cards/g/GoblinWizardry.java
new file mode 100644
index 0000000000..c24367aeac
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/g/GoblinWizardry.java
@@ -0,0 +1,31 @@
+package mage.cards.g;
+
+import mage.abilities.effects.common.CreateTokenEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.game.permanent.token.GoblinWizardToken;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class GoblinWizardry extends CardImpl {
+
+    public GoblinWizardry(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{R}");
+
+        // Create two 1/1 red Goblin Wizard creature tokens with prowess.
+        this.getSpellAbility().addEffect(new CreateTokenEffect(new GoblinWizardToken(), 2));
+    }
+
+    private GoblinWizardry(final GoblinWizardry card) {
+        super(card);
+    }
+
+    @Override
+    public GoblinWizardry copy() {
+        return new GoblinWizardry(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index e196ae0464..c2106adb39 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -119,6 +119,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Ghostly Pilferer", 52, Rarity.RARE, mage.cards.g.GhostlyPilferer.class));
         cards.add(new SetCardInfo("Glorious Anthem", 21, Rarity.RARE, mage.cards.g.GloriousAnthem.class));
         cards.add(new SetCardInfo("Goblin Arsonist", 147, Rarity.COMMON, mage.cards.g.GoblinArsonist.class));
+        cards.add(new SetCardInfo("Goblin Wizardry", 148, Rarity.COMMON, mage.cards.g.GoblinWizardry.class));
         cards.add(new SetCardInfo("Goremand", 101, Rarity.UNCOMMON, mage.cards.g.Goremand.class));
         cards.add(new SetCardInfo("Grasp of Darkness", 102, Rarity.COMMON, mage.cards.g.GraspOfDarkness.class));
         cards.add(new SetCardInfo("Griffin Aerie", 22, Rarity.UNCOMMON, mage.cards.g.GriffinAerie.class));
diff --git a/Mage/src/main/java/mage/game/permanent/token/GoblinWizardToken.java b/Mage/src/main/java/mage/game/permanent/token/GoblinWizardToken.java
new file mode 100644
index 0000000000..04089b6217
--- /dev/null
+++ b/Mage/src/main/java/mage/game/permanent/token/GoblinWizardToken.java
@@ -0,0 +1,31 @@
+package mage.game.permanent.token;
+
+import mage.MageInt;
+import mage.abilities.keyword.ProwessAbility;
+import mage.constants.CardType;
+import mage.constants.SubType;
+
+/**
+ * @author TheElk801
+ */
+public final class GoblinWizardToken extends TokenImpl {
+
+    public GoblinWizardToken() {
+        super("Goblin Wizard", "1/1 red Goblin Wizard creature token with prowess");
+        cardType.add(CardType.CREATURE);
+        subtype.add(SubType.GOBLIN);
+        subtype.add(SubType.WIZARD);
+        color.setRed(true);
+        power = new MageInt(1);
+        toughness = new MageInt(1);
+        this.addAbility(new ProwessAbility());
+    }
+
+    private GoblinWizardToken(final GoblinWizardToken token) {
+        super(token);
+    }
+
+    public GoblinWizardToken copy() {
+        return new GoblinWizardToken(this);
+    }
+}

From c9cac3f87b8263700e4ce8c143ad30fc84fb3244 Mon Sep 17 00:00:00 2001
From: arcox <10953229+arcox@users.noreply.github.com>
Date: Wed, 17 Jun 2020 12:07:17 +0000
Subject: [PATCH 336/586] Implement Jangling Automaton (#6653)

* Implement Weatherlight 151 - Jangling Automaton

* Cleanup Jangling Automaton

* Revert line endings on WTH set file

* Change getDefenderId to getDefendingPlayerId
---
 .../src/mage/cards/j/JanglingAutomaton.java   | 72 +++++++++++++++++++
 Mage.Sets/src/mage/sets/Weatherlight.java     |  3 +-
 2 files changed, 74 insertions(+), 1 deletion(-)
 create mode 100644 Mage.Sets/src/mage/cards/j/JanglingAutomaton.java

diff --git a/Mage.Sets/src/mage/cards/j/JanglingAutomaton.java b/Mage.Sets/src/mage/cards/j/JanglingAutomaton.java
new file mode 100644
index 0000000000..ce649f6691
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/j/JanglingAutomaton.java
@@ -0,0 +1,72 @@
+package mage.cards.j;
+
+import java.util.UUID;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.AttacksTriggeredAbility;
+import mage.abilities.effects.OneShotEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.Outcome;
+import mage.filter.common.FilterCreaturePermanent;
+import mage.filter.predicate.permanent.ControllerIdPredicate;
+import mage.game.Game;
+import mage.game.permanent.Permanent;
+
+/**
+ * @author arcox
+ */
+public final class JanglingAutomaton extends CardImpl {
+    public JanglingAutomaton(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}");
+        this.subtype.add(SubType.CONSTRUCT);
+
+        this.power = new MageInt(3);
+        this.toughness = new MageInt(2);
+
+        // Whenever Jangling Automaton attacks, untap all creatures defending player controls.
+        this.addAbility(new AttacksTriggeredAbility(new JanglingAutomatonEffect(), false));
+    }
+
+    public JanglingAutomaton(final JanglingAutomaton card) {
+        super(card);
+    }
+
+    @Override
+    public JanglingAutomaton copy() {
+        return new JanglingAutomaton(this);
+    }
+}
+
+class JanglingAutomatonEffect extends OneShotEffect {
+    public JanglingAutomatonEffect() {
+        super(Outcome.Untap);
+        this.staticText = "untap all creatures defending player controls";
+    }
+
+    public JanglingAutomatonEffect(final JanglingAutomatonEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public JanglingAutomatonEffect copy() {
+        return new JanglingAutomatonEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        UUID defenderId = game.getCombat().getDefendingPlayerId(source.getSourceId(), game);
+        if (defenderId != null) {
+            FilterCreaturePermanent filter = new FilterCreaturePermanent();
+            filter.add(new ControllerIdPredicate(defenderId));
+            for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) {
+                permanent.untap(game);
+            }
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/Weatherlight.java b/Mage.Sets/src/mage/sets/Weatherlight.java
index f93876f43d..b69ac02313 100644
--- a/Mage.Sets/src/mage/sets/Weatherlight.java
+++ b/Mage.Sets/src/mage/sets/Weatherlight.java
@@ -38,7 +38,7 @@ public final class Weatherlight extends ExpansionSet {
         cards.add(new SetCardInfo("Alms", 3, Rarity.COMMON, mage.cards.a.Alms.class));
         cards.add(new SetCardInfo("Ancestral Knowledge", 32, Rarity.RARE, mage.cards.a.AncestralKnowledge.class));
         cards.add(new SetCardInfo("Angelic Renewal", 4, Rarity.COMMON, mage.cards.a.AngelicRenewal.class));
-        cards.add(new SetCardInfo("Apathy", 33, Rarity.COMMON, mage.cards.a.Apathy.class));
+        cards.add(new SetCardInfo("Apathy", 33, Rarity.COMMON, mage.cards.a.Apathy.class));
         cards.add(new SetCardInfo("Arctic Wolves", 118, Rarity.UNCOMMON, mage.cards.a.ArcticWolves.class));
         cards.add(new SetCardInfo("Ardent Militia", 5, Rarity.COMMON, mage.cards.a.ArdentMilitia.class));
         cards.add(new SetCardInfo("Argivian Find", 6, Rarity.UNCOMMON, mage.cards.a.ArgivianFind.class));
@@ -108,6 +108,7 @@ public final class Weatherlight extends ExpansionSet {
         cards.add(new SetCardInfo("Infernal Tribute", 73, Rarity.RARE, mage.cards.i.InfernalTribute.class));
         cards.add(new SetCardInfo("Inner Sanctum", 18, Rarity.RARE, mage.cards.i.InnerSanctum.class));
         cards.add(new SetCardInfo("Jabari's Banner", 150, Rarity.UNCOMMON, mage.cards.j.JabarisBanner.class));
+        cards.add(new SetCardInfo("Jangling Automaton", 151, Rarity.COMMON, mage.cards.j.JanglingAutomaton.class));
         cards.add(new SetCardInfo("Kithkin Armor", 19, Rarity.COMMON, mage.cards.k.KithkinArmor.class));
         cards.add(new SetCardInfo("Lava Hounds", 109, Rarity.UNCOMMON, mage.cards.l.LavaHounds.class));
         cards.add(new SetCardInfo("Lava Storm", 110, Rarity.COMMON, mage.cards.l.LavaStorm.class));

From 14f415518db23870ca915d818d57ee18af4448a1 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Wed, 17 Jun 2020 08:19:09 -0400
Subject: [PATCH 337/586] Implemented Alchemist's Gift

---
 .../src/mage/cards/a/AlchemistsGift.java      | 73 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 74 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/a/AlchemistsGift.java

diff --git a/Mage.Sets/src/mage/cards/a/AlchemistsGift.java b/Mage.Sets/src/mage/cards/a/AlchemistsGift.java
new file mode 100644
index 0000000000..52653791ad
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/a/AlchemistsGift.java
@@ -0,0 +1,73 @@
+package mage.cards.a;
+
+import mage.abilities.Ability;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.continuous.BoostSourceEffect;
+import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
+import mage.abilities.keyword.DeathtouchAbility;
+import mage.abilities.keyword.LifelinkAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.Outcome;
+import mage.game.Game;
+import mage.players.Player;
+import mage.target.common.TargetCreaturePermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class AlchemistsGift extends CardImpl {
+
+    public AlchemistsGift(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}");
+
+        // Target creature gets +1/+1 and gains your choice of deathtouch or lifelink until end of turn.
+        this.getSpellAbility().addEffect(new AlchemistsGiftEffect());
+        this.getSpellAbility().addTarget(new TargetCreaturePermanent());
+    }
+
+    private AlchemistsGift(final AlchemistsGift card) {
+        super(card);
+    }
+
+    @Override
+    public AlchemistsGift copy() {
+        return new AlchemistsGift(this);
+    }
+}
+
+class AlchemistsGiftEffect extends OneShotEffect {
+
+    AlchemistsGiftEffect() {
+        super(Outcome.Benefit);
+        staticText = "Target creature gets +1/+1 and gains your choice of deathtouch or lifelink until end of turn.";
+    }
+
+    private AlchemistsGiftEffect(final AlchemistsGiftEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public AlchemistsGiftEffect copy() {
+        return new AlchemistsGiftEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Player player = game.getPlayer(source.getControllerId());
+        if (player == null) {
+            return false;
+        }
+        Ability ability = player.chooseUse(
+                outcome, "Deathtouch or lifelink?", null,
+                "Deathtouch", "Lifelink", source, game
+        ) ? DeathtouchAbility.getInstance() : LifelinkAbility.getInstance();
+        game.addEffect(new BoostSourceEffect(1, 1, Duration.EndOfTurn), ability);
+        game.addEffect(new GainAbilitySourceEffect(ability, Duration.EndOfTurn), ability);
+        return true;
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index c2106adb39..5f07ff7383 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -34,6 +34,7 @@ public final class CoreSet2021 extends ExpansionSet {
         this.maxCardNumberInBooster = 274;
 
         cards.add(new SetCardInfo("Adherent of Hope", 321, Rarity.COMMON, mage.cards.a.AdherentOfHope.class));
+        cards.add(new SetCardInfo("Alchemist's Gift", 87, Rarity.COMMON, mage.cards.a.AlchemistsGift.class));
         cards.add(new SetCardInfo("Alpine Houndmaster", 215, Rarity.UNCOMMON, mage.cards.a.AlpineHoundmaster.class));
         cards.add(new SetCardInfo("Alpine Watchdog", 2, Rarity.COMMON, mage.cards.a.AlpineWatchdog.class));
         cards.add(new SetCardInfo("Angelic Ascension", 3, Rarity.UNCOMMON, mage.cards.a.AngelicAscension.class));

From 52fe274f023d90f0562d51161888012ea422e9d9 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Wed, 17 Jun 2020 08:26:02 -0400
Subject: [PATCH 338/586] Implemented Anointed Chorister

---
 .../src/mage/cards/a/AnointedChorister.java   | 46 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 47 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/a/AnointedChorister.java

diff --git a/Mage.Sets/src/mage/cards/a/AnointedChorister.java b/Mage.Sets/src/mage/cards/a/AnointedChorister.java
new file mode 100644
index 0000000000..c4761f699a
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/a/AnointedChorister.java
@@ -0,0 +1,46 @@
+package mage.cards.a;
+
+import mage.MageInt;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.effects.common.continuous.BoostSourceEffect;
+import mage.abilities.keyword.LifelinkAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class AnointedChorister extends CardImpl {
+
+    public AnointedChorister(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}");
+
+        this.subtype.add(SubType.HUMAN);
+        this.subtype.add(SubType.CLERIC);
+        this.power = new MageInt(1);
+        this.toughness = new MageInt(1);
+
+        // Lifelink
+        this.addAbility(LifelinkAbility.getInstance());
+
+        // {4}{W}: Anointed Chorister gets +3/+3 until end of turn.
+        this.addAbility(new SimpleActivatedAbility(
+                new BoostSourceEffect(3, 3, Duration.EndOfTurn), new ManaCostsImpl("{4}{W}")
+        ));
+    }
+
+    private AnointedChorister(final AnointedChorister card) {
+        super(card);
+    }
+
+    @Override
+    public AnointedChorister copy() {
+        return new AnointedChorister(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 5f07ff7383..2c5ba14039 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -39,6 +39,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Alpine Watchdog", 2, Rarity.COMMON, mage.cards.a.AlpineWatchdog.class));
         cards.add(new SetCardInfo("Angelic Ascension", 3, Rarity.UNCOMMON, mage.cards.a.AngelicAscension.class));
         cards.add(new SetCardInfo("Animal Sanctuary", 242, Rarity.RARE, mage.cards.a.AnimalSanctuary.class));
+        cards.add(new SetCardInfo("Anointed Chorister", 4, Rarity.COMMON, mage.cards.a.AnointedChorister.class));
         cards.add(new SetCardInfo("Azusa, Lost but Seeking", 173, Rarity.RARE, mage.cards.a.AzusaLostButSeeking.class));
         cards.add(new SetCardInfo("Bad Deal", 89, Rarity.UNCOMMON, mage.cards.b.BadDeal.class));
         cards.add(new SetCardInfo("Baneslayer Angel", 6, Rarity.MYTHIC, mage.cards.b.BaneslayerAngel.class));

From cc3a3f660eff09baa56936c109fed0f79bc64fc0 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Wed, 17 Jun 2020 08:38:38 -0400
Subject: [PATCH 339/586] Implemented Canopy Stalker

---
 Mage.Sets/src/mage/cards/c/CanopyStalker.java | 47 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 48 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/c/CanopyStalker.java

diff --git a/Mage.Sets/src/mage/cards/c/CanopyStalker.java b/Mage.Sets/src/mage/cards/c/CanopyStalker.java
new file mode 100644
index 0000000000..3383889e4b
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/c/CanopyStalker.java
@@ -0,0 +1,47 @@
+package mage.cards.c;
+
+import mage.MageInt;
+import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.dynamicvalue.common.CreaturesDiedThisTurnCount;
+import mage.abilities.effects.common.GainLifeEffect;
+import mage.abilities.effects.common.combat.MustBeBlockedByAtLeastOneSourceEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.watchers.common.CreaturesDiedWatcher;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class CanopyStalker extends CardImpl {
+
+    public CanopyStalker(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}");
+
+        this.subtype.add(SubType.CAT);
+        this.power = new MageInt(4);
+        this.toughness = new MageInt(2);
+
+        // Canopy Stalker must be blocked if able.
+        this.addAbility(new SimpleStaticAbility(new MustBeBlockedByAtLeastOneSourceEffect()));
+
+        // When Canopy Stalker dies, you gain 1 life for each creature that died this turn.
+        this.addAbility(new DiesTriggeredAbility(
+                new GainLifeEffect(CreaturesDiedThisTurnCount.instance)
+                        .setText("you gain 1 life for each creature that died this turn")
+        ), new CreaturesDiedWatcher());
+    }
+
+    private CanopyStalker(final CanopyStalker card) {
+        super(card);
+    }
+
+    @Override
+    public CanopyStalker copy() {
+        return new CanopyStalker(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 2c5ba14039..1127226d4b 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -61,6 +61,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Burn Bright", 134, Rarity.COMMON, mage.cards.b.BurnBright.class));
         cards.add(new SetCardInfo("Caged Zombie", 91, Rarity.COMMON, mage.cards.c.CagedZombie.class));
         cards.add(new SetCardInfo("Cancel", 46, Rarity.COMMON, mage.cards.c.Cancel.class));
+        cards.add(new SetCardInfo("Canopy Stalker", 175, Rarity.UNCOMMON, mage.cards.c.CanopyStalker.class));
         cards.add(new SetCardInfo("Capture Sphere", 47, Rarity.COMMON, mage.cards.c.CaptureSphere.class));
         cards.add(new SetCardInfo("Carrion Grub", 92, Rarity.UNCOMMON, mage.cards.c.CarrionGrub.class));
         cards.add(new SetCardInfo("Celestial Enforcer", 11, Rarity.COMMON, mage.cards.c.CelestialEnforcer.class));

From 269acb2d3debcfe1968a8f51ed6c544b923d9c18 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Wed, 17 Jun 2020 08:50:23 -0400
Subject: [PATCH 340/586] Implemented Hobblefiend

---
 Mage.Sets/src/mage/cards/h/Hobblefiend.java | 53 +++++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java    |  1 +
 2 files changed, 54 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/h/Hobblefiend.java

diff --git a/Mage.Sets/src/mage/cards/h/Hobblefiend.java b/Mage.Sets/src/mage/cards/h/Hobblefiend.java
new file mode 100644
index 0000000000..7f81332b25
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/h/Hobblefiend.java
@@ -0,0 +1,53 @@
+package mage.cards.h;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.common.SacrificeTargetCost;
+import mage.abilities.costs.mana.GenericManaCost;
+import mage.abilities.effects.common.counter.AddCountersSourceEffect;
+import mage.abilities.keyword.TrampleAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.counters.CounterType;
+import mage.filter.StaticFilters;
+import mage.target.common.TargetControlledPermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class Hobblefiend extends CardImpl {
+
+    public Hobblefiend(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}");
+
+        this.subtype.add(SubType.DEVIL);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(1);
+
+        // Trample
+        this.addAbility(TrampleAbility.getInstance());
+
+        // {1}, Sacrifice another creature: Put a +1/+1 counter on Hobblefiend.
+        Ability ability = new SimpleActivatedAbility(
+                new AddCountersSourceEffect(CounterType.P1P1.createInstance()), new GenericManaCost(1)
+        );
+        ability.addCost(new SacrificeTargetCost(
+                new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE)
+        ));
+        this.addAbility(ability);
+    }
+
+    private Hobblefiend(final Hobblefiend card) {
+        super(card);
+    }
+
+    @Override
+    public Hobblefiend copy() {
+        return new Hobblefiend(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 1127226d4b..5dee9f04c0 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -132,6 +132,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Hellkite Punisher", 151, Rarity.UNCOMMON, mage.cards.h.HellkitePunisher.class));
         cards.add(new SetCardInfo("Heroic Intervention", 188, Rarity.RARE, mage.cards.h.HeroicIntervention.class));
         cards.add(new SetCardInfo("Historian of Zhalfir", 325, Rarity.UNCOMMON, mage.cards.h.HistorianOfZhalfir.class));
+        cards.add(new SetCardInfo("Hobblefiend", 152, Rarity.COMMON, mage.cards.h.Hobblefiend.class));
         cards.add(new SetCardInfo("Hunter's Edge", 189, Rarity.COMMON, mage.cards.h.HuntersEdge.class));
         cards.add(new SetCardInfo("Igneous Cur", 153, Rarity.COMMON, mage.cards.i.IgneousCur.class));
         cards.add(new SetCardInfo("Indulging Patrician", 219, Rarity.UNCOMMON, mage.cards.i.IndulgingPatrician.class));

From 5e5483242624ef19538af66d8c50ab4b58d195f2 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Wed, 17 Jun 2020 08:54:52 -0400
Subject: [PATCH 341/586] added Jumpstart

---
 Mage.Sets/src/mage/sets/Jumpstart.java | 44 ++++++++++++++++++++++++++
 Utils/known-sets.txt                   |  1 +
 Utils/mtg-cards-data.txt               | 23 ++++++++++++++
 Utils/mtg-sets-data.txt                |  1 +
 4 files changed, 69 insertions(+)
 create mode 100644 Mage.Sets/src/mage/sets/Jumpstart.java

diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
new file mode 100644
index 0000000000..59f5c905de
--- /dev/null
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -0,0 +1,44 @@
+package mage.sets;
+
+import mage.cards.ExpansionSet;
+import mage.constants.Rarity;
+import mage.constants.SetType;
+
+/**
+ * @author TheElk801
+ */
+public final class Jumpstart extends ExpansionSet {
+
+    private static final Jumpstart instance = new Jumpstart();
+
+    public static Jumpstart getInstance() {
+        return instance;
+    }
+
+    private Jumpstart() {
+        super("Jumpstart", "JMP", ExpansionSet.buildDate(2020, 7, 17), SetType.SUPPLEMENTAL);
+        this.blockName = "Jumpstart";
+        this.hasBasicLands = true;
+
+        cards.add(new SetCardInfo("Alloy Myr", 457, Rarity.COMMON, mage.cards.a.AlloyMyr.class));
+        cards.add(new SetCardInfo("Auger Spree", 449, Rarity.COMMON, mage.cards.a.AugerSpree.class));
+        cards.add(new SetCardInfo("Bone Picker", 212, Rarity.UNCOMMON, mage.cards.b.BonePicker.class));
+        cards.add(new SetCardInfo("Chamber Sentry", 461, Rarity.RARE, mage.cards.c.ChamberSentry.class));
+        cards.add(new SetCardInfo("Dinrova Horror", 450, Rarity.UNCOMMON, mage.cards.d.DinrovaHorror.class));
+        cards.add(new SetCardInfo("Dutiful Attendant", 226, Rarity.COMMON, mage.cards.d.DutifulAttendant.class));
+        cards.add(new SetCardInfo("Fusion Elemental", 451, Rarity.UNCOMMON, mage.cards.f.FusionElemental.class));
+        cards.add(new SetCardInfo("Ghoulcaller's Accomplice", 237, Rarity.COMMON, mage.cards.g.GhoulcallersAccomplice.class));
+        cards.add(new SetCardInfo("Innocent Blood", 244, Rarity.COMMON, mage.cards.i.InnocentBlood.class));
+        cards.add(new SetCardInfo("Ironroot Warlord", 452, Rarity.UNCOMMON, mage.cards.i.IronrootWarlord.class));
+        cards.add(new SetCardInfo("Lawmage's Binding", 453, Rarity.COMMON, mage.cards.l.LawmagesBinding.class));
+        cards.add(new SetCardInfo("Maelstrom Archangel", 454, Rarity.MYTHIC, mage.cards.m.MaelstromArchangel.class));
+        cards.add(new SetCardInfo("Mirrodin's Core", 492, Rarity.UNCOMMON, mage.cards.m.MirrodinsCore.class));
+        cards.add(new SetCardInfo("Prophetic Prism", 478, Rarity.COMMON, mage.cards.p.PropheticPrism.class));
+        cards.add(new SetCardInfo("Raging Regisaur", 455, Rarity.UNCOMMON, mage.cards.r.RagingRegisaur.class));
+        cards.add(new SetCardInfo("Rupture Spire", 495, Rarity.COMMON, mage.cards.r.RuptureSpire.class));
+        cards.add(new SetCardInfo("Shambling Goblin", 277, Rarity.COMMON, mage.cards.s.ShamblingGoblin.class));
+        cards.add(new SetCardInfo("Skittering Surveyor", 486, Rarity.COMMON, mage.cards.s.SkitteringSurveyor.class));
+        cards.add(new SetCardInfo("Swamp", 54, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Terramorphic Expanse", 78, Rarity.COMMON, mage.cards.t.TerramorphicExpanse.class));
+    }
+}
diff --git a/Utils/known-sets.txt b/Utils/known-sets.txt
index 2bcba03814..d2726adaee 100644
--- a/Utils/known-sets.txt
+++ b/Utils/known-sets.txt
@@ -114,6 +114,7 @@ Ixalan|Ixalan|
 Journey into Nyx|JourneyIntoNyx|
 Judge Promo|JudgePromo|
 Judgment|Judgment|
+Jumpstart|Jumpstart|
 Kaladesh|Kaladesh|
 Khans of Tarkir|KhansOfTarkir|
 Launch Party|LaunchParty|
diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt
index 87900618f4..f70c7e2060 100644
--- a/Utils/mtg-cards-data.txt
+++ b/Utils/mtg-cards-data.txt
@@ -37723,3 +37723,26 @@ Garruk, Savage Herald|Core Set 2021|336|M|{4}{G}{G}|Legendary Planeswalker - Gar
 Garruk's Warsteed|Core Set 2021|337|R|{3}{G}{G}|Creature - Rhino|3|5|Vigilance$When Garruk's Warsteed enters the battlefield, you may search your library and/or graveyard for a card named Garruk, Savage Herald, reveal it, and put it into your hand. If you search your library this way, shuffle it.|
 Predatory Wurm|Core Set 2021|338|U|{3}{G}|Creature - Wurm|4|4|Vigilance$Predatory Wurm gets +2/+2 as long as you control a Garruk planeswalker.|
 Wildwood Patrol|Core Set 2021|339|C|{2}{G}|Creature - Centaur Scout|4|2|Trample|
+Kels, Fight Fixer|Jumpstart|15|R|{2}{B}{B}|Legendary Creature - Azra Warlock|4|3|Menace$Whenever you sacrifice a creature, you may pay {U/B}. If you do, draw a card.${1}, Sacrifice a creature: Kels, Fight Fixer gains indestructible until end of turn.|
+Nocturnal Feeder|Jumpstart|16|C|{2}{B}|Creature - Vampire Rogue|2|1|Fying$When Nocturnal Feeder dies, each opponent loses 2 life and you gain 2 life.|
+Thriving Moor|Jumpstart|37|C||Land|||Thriving Moor enters the battlefield tapped.$As Thriving Moor enters the battlefield, choose a color other than black.${T}: Add {B} or one mana of the chosen color.|
+Swamp|Jumpstart|54|C||Basic Land - Swamp|||({T}: Add {B}.)|
+Terramorphic Expanse|Jumpstart|78|C||Land|||{T}, Sacrifice Terramorphic Expanse: Search your library for a basic land card, put it onto the battlefield tapped, then shuffle your library.|
+Bone Picker|Jumpstart|212|U|{3}{B}|Creature - Bird|3|2|This spell costs {3} less to cast if a creature died this turn.$Flying, deathtouch|
+Dutiful Attendant|Jumpstart|226|C|{2}{B}|Creature - Human Warrior|1|2|When Dutiful Attendant dies, return another target creature card from your graveyard to your hand.|
+Ghoulcaller's Accomplice|Jumpstart|237|C|{1}{B}|Creature - Human Rogue|2|2|{3}{B}, Exile Ghoulcaller's Accomplice from your graveyard: Create a 2/2 black Zombie creature token. Activate this ability only any time you could cast a sorcery.|
+Innocent Blood|Jumpstart|244|C|{B}|Sorcery|||Each player sacrifices a creature.|
+Shambling Goblin|Jumpstart|277|C|{B}|Creature - Zombie Goblin|1|1|When Shambling Goblin dies, target creature an opponent controls gets -1/-1 until end of turn.|
+Auger Spree|Jumpstart|449|C|{1}{B}{R}|Instant|||Target creature gets +4/-4 until end of turn.|
+Dinrova Horror|Jumpstart|450|U|{4}{U}{B}|Creature - Horror|4|4|When Dinrova Horror enters the battlefield, return target permanent to its owner's hand, then that player discards a card.|
+Fusion Elemental|Jumpstart|451|U|{W}{U}{B}{R}{G}|Creature - Elemental|8|8||
+Ironroot Warlord|Jumpstart|452|U|{1}{G}{W}|Creature - Treefolk Soldier|*|5|Ironroot Warlord's power is equal to the number of creatures you control.${3}{G}{W}: Create a 1/1 white Soldier creature token.|
+Lawmage's Binding|Jumpstart|453|C|{1}{W}{U}|Enchantment - Aura|||Flash$Enchant creature$Enchanted creature can't attack or block, and its activated abilities can't be activated.|
+Maelstrom Archangel|Jumpstart|454|M|{W}{U}{B}{R}{G}|Creature - Angel|5|5|Flying$Whenever Maelstrom Archangel deals combat damage to a player, you may cast a nonland card from your hand without paying its mana cost.|
+Raging Regisaur|Jumpstart|455|U|{2}{R}{G}|Creature - Dinosaur|4|4|Whenever Raging Regisaur attacks, it deals 1 damage to any target.|
+Alloy Myr|Jumpstart|457|C|{3}|Artifact Creature - Myr|2|2|{T}: Add one mana of any color.|
+Chamber Sentry|Jumpstart|461|R|{X}|Artifact Creature - Construct|0|0|Chamber Sentry enters the battlefield with a +1/+1 counter on it for each color of mana spent to cast it.${X}, {T}, Remove X +1/+1 counters from Chamber Sentry: It deals X damage to any target.${W}{U}{B}{R}{G}: Return Chamber Sentry from your graveyard to your hand.|
+Prophetic Prism|Jumpstart|478|C|{2}|Artifact|||When Prophetic Prism enters the battlefield, draw a card.${1}, {T}: Add one mana of any color.|
+Skittering Surveyor|Jumpstart|486|C|{3}|Artifact Creature - Construct|1|2|When Skittering Surveyor enters the battlefield, you may search your library for a basic land card, reveal it, put it into your hand, then shuffle your library.|
+Mirrodin's Core|Jumpstart|492|U||Land|||{T}: Add {C}.${T}: Put a charge counter on Mirrodin's Core.${T}, Remove a charge counter from Mirrodin's Core: Add one mana of any color.|
+Rupture Spire|Jumpstart|495|C||Land|||Rupture Spire enters the battlefield tapped.$When Rupture Spire enters the battlefield, sacrifice it unless you pay {1}.${T}: Add one mana of any color.|
diff --git a/Utils/mtg-sets-data.txt b/Utils/mtg-sets-data.txt
index 0d8b90c562..6653e08590 100644
--- a/Utils/mtg-sets-data.txt
+++ b/Utils/mtg-sets-data.txt
@@ -116,6 +116,7 @@ Innistrad|ISD|
 Journey into Nyx|JOU|
 Judge Promo|JR|
 Judgment|JUD|
+Jumpstart|JMP|
 Kaladesh|KLD|
 Khans of Tarkir|KTK|
 Limited Edition Alpha|LEA|

From 8bae8a911bc205229e1a3c9104b2ed95dec57c22 Mon Sep 17 00:00:00 2001
From: arcox <10953229+arcox@users.noreply.github.com>
Date: Wed, 17 Jun 2020 14:15:16 +0000
Subject: [PATCH 342/586] Implement Choking Vines from Weatherlight set (#6655)

---
 Mage.Sets/src/mage/cards/c/ChokingVines.java | 93 ++++++++++++++++++++
 Mage.Sets/src/mage/cards/f/FogPatch.java     |  2 +-
 Mage.Sets/src/mage/sets/Weatherlight.java    |  1 +
 3 files changed, 95 insertions(+), 1 deletion(-)
 create mode 100644 Mage.Sets/src/mage/cards/c/ChokingVines.java

diff --git a/Mage.Sets/src/mage/cards/c/ChokingVines.java b/Mage.Sets/src/mage/cards/c/ChokingVines.java
new file mode 100644
index 0000000000..285e78f3b1
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/c/ChokingVines.java
@@ -0,0 +1,93 @@
+package mage.cards.c;
+
+import java.util.UUID;
+
+import mage.abilities.Ability;
+import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.DamageTargetEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Outcome;
+import mage.constants.PhaseStep;
+import mage.game.Game;
+import mage.game.combat.CombatGroup;
+import mage.players.Player;
+import mage.target.Target;
+import mage.target.Targets;
+import mage.target.common.TargetCreaturePermanent;
+import mage.target.targetadjustment.TargetAdjuster;
+
+import static mage.filter.StaticFilters.FILTER_ATTACKING_CREATURES;
+
+/**
+ * @author arcox
+ */
+public final class ChokingVines extends CardImpl {
+
+    public ChokingVines(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{X}{G}");
+
+        // Cast only during the declare blockers step.
+        this.addAbility(new CastOnlyDuringPhaseStepSourceAbility(null, PhaseStep.DECLARE_BLOCKERS, null, "Cast this spell only during the declare blockers step"));
+
+        // X target attacking creatures become blocked. Choking Vines deals 1 damage to each of those creatures.
+        this.getSpellAbility().addEffect(new ChokingVinesEffect());
+        this.getSpellAbility().addEffect(new DamageTargetEffect(1));
+        this.getSpellAbility().setTargetAdjuster(ChokingVinesAdjuster.instance);
+    }
+
+    public ChokingVines(final ChokingVines card) {
+        super(card);
+    }
+
+    @Override
+    public ChokingVines copy() {
+        return new ChokingVines(this);
+    }
+}
+
+enum ChokingVinesAdjuster implements TargetAdjuster {
+    instance;
+
+    @Override
+    public void adjustTargets(Ability ability, Game game) {
+        ability.getTargets().clear();
+        int x = ability.getManaCostsToPay().getX();
+        ability.addTarget(new TargetCreaturePermanent(x, x, FILTER_ATTACKING_CREATURES, false));
+    }
+}
+
+class ChokingVinesEffect extends OneShotEffect {
+
+    public ChokingVinesEffect() {
+        super(Outcome.Benefit);
+        this.staticText = "X target attacking creatures become blocked";
+    }
+
+    public ChokingVinesEffect(final ChokingVinesEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public ChokingVinesEffect copy() {
+        return new ChokingVinesEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Player controller = game.getPlayer(source.getControllerId());
+        Targets targets = source.getTargets();
+        if (controller != null && !targets.isEmpty()) {
+            for (Target target : targets) {
+                CombatGroup combatGroup = game.getCombat().findGroup(target.getFirstTarget());
+                if (combatGroup != null) {
+                    combatGroup.setBlocked(true, game);
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/Mage.Sets/src/mage/cards/f/FogPatch.java b/Mage.Sets/src/mage/cards/f/FogPatch.java
index 7c62a88119..7e054f6c07 100644
--- a/Mage.Sets/src/mage/cards/f/FogPatch.java
+++ b/Mage.Sets/src/mage/cards/f/FogPatch.java
@@ -66,7 +66,7 @@ class FogPatchEffect extends OneShotEffect {
                 if (attacker != null) {
                     CombatGroup combatGroup = game.getCombat().findGroup(attacker.getId());
                     if (combatGroup != null) {
-                        combatGroup.setBlocked(true);
+                        combatGroup.setBlocked(true, game);
                     }
                 }
             }
diff --git a/Mage.Sets/src/mage/sets/Weatherlight.java b/Mage.Sets/src/mage/sets/Weatherlight.java
index b69ac02313..1bcc090721 100644
--- a/Mage.Sets/src/mage/sets/Weatherlight.java
+++ b/Mage.Sets/src/mage/sets/Weatherlight.java
@@ -62,6 +62,7 @@ public final class Weatherlight extends ExpansionSet {
         cards.add(new SetCardInfo("Buried Alive", 63, Rarity.UNCOMMON, mage.cards.b.BuriedAlive.class));
         cards.add(new SetCardInfo("Call of the Wild", 122, Rarity.RARE, mage.cards.c.CallOfTheWild.class));
         cards.add(new SetCardInfo("Chimeric Sphere", 148, Rarity.UNCOMMON, mage.cards.c.ChimericSphere.class));
+        cards.add(new SetCardInfo("Choking Vines", 123, Rarity.COMMON, mage.cards.c.ChokingVines.class));
         cards.add(new SetCardInfo("Cinder Giant", 93, Rarity.UNCOMMON, mage.cards.c.CinderGiant.class));
         cards.add(new SetCardInfo("Cinder Wall", 94, Rarity.COMMON, mage.cards.c.CinderWall.class));
         cards.add(new SetCardInfo("Cloud Djinn", 36, Rarity.UNCOMMON, mage.cards.c.CloudDjinn.class));

From bb8e132b77baa6ee0f52381f64fc2921a4a3fc63 Mon Sep 17 00:00:00 2001
From: arcox <10953229+arcox@users.noreply.github.com>
Date: Wed, 17 Jun 2020 14:16:04 +0000
Subject: [PATCH 343/586] Implement Coils of the Medusa (#6649)

---
 .../src/mage/cards/c/CoilsOfTheMedusa.java    | 104 ++++++++++++++++++
 Mage.Sets/src/mage/sets/Weatherlight.java     |   1 +
 2 files changed, 105 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/c/CoilsOfTheMedusa.java

diff --git a/Mage.Sets/src/mage/cards/c/CoilsOfTheMedusa.java b/Mage.Sets/src/mage/cards/c/CoilsOfTheMedusa.java
new file mode 100644
index 0000000000..fe0bbe9c31
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/c/CoilsOfTheMedusa.java
@@ -0,0 +1,104 @@
+package mage.cards.c;
+
+import mage.abilities.Ability;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.costs.common.SacrificeSourceCost;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.AttachEffect;
+import mage.abilities.effects.common.continuous.BoostEnchantedEffect;
+import mage.abilities.keyword.DefenderAbility;
+import mage.abilities.keyword.EnchantAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.*;
+import mage.game.Game;
+import mage.game.combat.CombatGroup;
+import mage.game.permanent.Permanent;
+import mage.target.TargetPermanent;
+import mage.target.common.TargetCreaturePermanent;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+/**
+ *
+ * @author arcox
+ */
+public final class CoilsOfTheMedusa extends CardImpl {
+    public CoilsOfTheMedusa(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}");
+        this.subtype.add(SubType.AURA);
+
+        // Enchant creature
+        TargetPermanent auraTarget = new TargetCreaturePermanent();
+        this.getSpellAbility().addTarget(auraTarget);
+        this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature));
+        this.addAbility(new EnchantAbility(auraTarget.getTargetName()));
+
+        // Enchanted creature gets +1/-1.
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(1, -1, Duration.WhileOnBattlefield)));
+
+        // Sacrifice Coils of the Medusa: Destroy all non-Wall creatures blocking enchanted creature.
+        this.addAbility(new SimpleActivatedAbility(
+                Zone.BATTLEFIELD,
+                new CoilsOfTheMedusaDestroyEffect(),
+                new SacrificeSourceCost())
+        );
+    }
+
+    public CoilsOfTheMedusa(final CoilsOfTheMedusa card) {
+        super(card);
+    }
+
+    @Override
+    public CoilsOfTheMedusa copy() {
+        return new CoilsOfTheMedusa(this);
+    }
+}
+
+class CoilsOfTheMedusaDestroyEffect extends OneShotEffect {
+    public CoilsOfTheMedusaDestroyEffect() {
+        super(Outcome.DestroyPermanent);
+        this.staticText = "Destroy all non-Wall creatures blocking enchanted creature.";
+    }
+
+    public CoilsOfTheMedusaDestroyEffect(final CoilsOfTheMedusaDestroyEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public CoilsOfTheMedusaDestroyEffect copy() {
+        return new CoilsOfTheMedusaDestroyEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Permanent coils = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD);
+        List<UUID> blockers = new ArrayList<>();
+
+        if (coils != null && coils.getAttachedTo() != null) {
+            // grab all creatures blocking enchanted creature
+            Permanent enchantedCreature = game.getPermanent(coils.getAttachedTo());
+            if (enchantedCreature != null && enchantedCreature.isBlocked(game)) {
+                for (CombatGroup group : game.getCombat().getGroups()) {
+                    if (group.getAttackers().contains(enchantedCreature.getId())) {
+                        blockers = group.getBlockers();
+                        break;
+                    }
+                }
+
+                // filter out defenders, destroying the rest
+                while (!blockers.isEmpty()) {
+                    Permanent blocker = game.getPermanent(blockers.remove(0));
+                    if (!blocker.hasAbility(DefenderAbility.getInstance(), game)) {
+                        blocker.destroy(source.getSourceId(), game, false);
+                    }
+                }
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/Weatherlight.java b/Mage.Sets/src/mage/sets/Weatherlight.java
index 1bcc090721..5f906dddc8 100644
--- a/Mage.Sets/src/mage/sets/Weatherlight.java
+++ b/Mage.Sets/src/mage/sets/Weatherlight.java
@@ -66,6 +66,7 @@ public final class Weatherlight extends ExpansionSet {
         cards.add(new SetCardInfo("Cinder Giant", 93, Rarity.UNCOMMON, mage.cards.c.CinderGiant.class));
         cards.add(new SetCardInfo("Cinder Wall", 94, Rarity.COMMON, mage.cards.c.CinderWall.class));
         cards.add(new SetCardInfo("Cloud Djinn", 36, Rarity.UNCOMMON, mage.cards.c.CloudDjinn.class));
+        cards.add(new SetCardInfo("Coils of the Medusa", 65, Rarity.COMMON, mage.cards.c.CoilsOfTheMedusa.class));
         cards.add(new SetCardInfo("Cone of Flame", 95, Rarity.UNCOMMON, mage.cards.c.ConeOfFlame.class));
         cards.add(new SetCardInfo("Debt of Loyalty", 11, Rarity.RARE, mage.cards.d.DebtOfLoyalty.class));
         cards.add(new SetCardInfo("Dense Foliage", 124, Rarity.RARE, mage.cards.d.DenseFoliage.class));

From 055458e9a8d834f152bd2edd59578b524a5ac56a Mon Sep 17 00:00:00 2001
From: arcox <10953229+arcox@users.noreply.github.com>
Date: Wed, 17 Jun 2020 14:49:40 +0000
Subject: [PATCH 344/586] Fix Preacher interaction with tokens (#6625)

---
 Mage.Sets/src/mage/cards/p/Preacher.java | 25 ++++++++++--------------
 1 file changed, 10 insertions(+), 15 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/p/Preacher.java b/Mage.Sets/src/mage/cards/p/Preacher.java
index cda4db79a2..5b9769746e 100644
--- a/Mage.Sets/src/mage/cards/p/Preacher.java
+++ b/Mage.Sets/src/mage/cards/p/Preacher.java
@@ -74,22 +74,17 @@ class PreacherEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Permanent sourcePermanent = game.getPermanent(source.getSourceId());
+        Permanent targetPermanent = game.getPermanent(source.getFirstTarget());
         Player controller = game.getPlayer(source.getControllerId());
-        if (controller != null
-                && sourcePermanent != null) {
-            Permanent targetPermanent = game.getPermanent(source.getFirstTarget());
-            if (targetPermanent != null) {
-                SourceTappedCondition sourceTappedCondition = SourceTappedCondition.instance;
-                SourceHasRemainedInSameZoneCondition conditionSourceSameZone = new SourceHasRemainedInSameZoneCondition(sourcePermanent.getId());
-                SourceHasRemainedInSameZoneCondition conditionTargetSameZone = new SourceHasRemainedInSameZoneCondition(targetPermanent.getId());
-                ConditionalContinuousEffect effect = new ConditionalContinuousEffect(
-                        new GainControlTargetEffect(Duration.Custom),
-                        new CompoundCondition(sourceTappedCondition, new CompoundCondition(conditionSourceSameZone, conditionTargetSameZone)),
-                        "Gain control of target creature of an opponent's choice that they control for as long as {this} remains tapped");
-                effect.setTargetPointer(new FixedTarget(targetPermanent.getId()));
-                game.addEffect(effect, source);
-                return true;
-            }
+        if (controller != null && sourcePermanent != null && targetPermanent != null) {
+            SourceTappedCondition sourceTappedCondition = SourceTappedCondition.instance;
+            ConditionalContinuousEffect effect = new ConditionalContinuousEffect(
+                    new GainControlTargetEffect(Duration.Custom),
+                    sourceTappedCondition,
+                    "Gain control of target creature of an opponent's choice that they control for as long as {this} remains tapped");
+            effect.setTargetPointer(new FixedTarget(targetPermanent.getId()));
+            game.addEffect(effect, source);
+            return true;
         }
         return false;
     }

From 2f68db70863d9b48b48f7e70ad1ec48ae38e12cc Mon Sep 17 00:00:00 2001
From: Thomas ARBLAY <arblay.thomas@gmail.com>
Date: Wed, 17 Jun 2020 16:58:35 +0200
Subject: [PATCH 345/586] [RFR] Correction of the text of some cards (#6602)

* Fix some cards

* Fix Clot Sliver

* Fix WarrenScourgeElf

* Fix Olivia Voldaren

* Fix Domri Rade Emblem

* Fix Broken Ambitions Missing text

Co-authored-by: Thomas ARBLAY <thomas.arblay@smile.fr>
---
 .../src/mage/cards/b/BrokenAmbitions.java     |  6 +++++-
 Mage.Sets/src/mage/cards/c/ClotSliver.java    |  5 +++--
 Mage.Sets/src/mage/cards/h/HoldTheGates.java  |  5 +++--
 .../src/mage/cards/i/IlhargTheRazeBoar.java   |  2 +-
 .../cards/l/LilianaDreadhordeGeneral.java     |  2 +-
 .../src/mage/cards/o/OliviaVoldaren.java      |  9 ++++++--
 .../src/mage/cards/s/StonebrowKrosanHero.java |  7 ++++---
 .../src/mage/cards/w/WarrenScourgeElf.java    |  5 +++--
 .../common/RegenerateSourceEffect.java        |  9 ++++++--
 .../AddCardSubTypeTargetEffect.java           | 10 ++++++++-
 .../game/command/emblems/DomriRadeEmblem.java | 21 +++++++++----------
 11 files changed, 53 insertions(+), 28 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/b/BrokenAmbitions.java b/Mage.Sets/src/mage/cards/b/BrokenAmbitions.java
index cccf4aed4e..b22f48055d 100644
--- a/Mage.Sets/src/mage/cards/b/BrokenAmbitions.java
+++ b/Mage.Sets/src/mage/cards/b/BrokenAmbitions.java
@@ -45,18 +45,21 @@ public final class BrokenAmbitions extends CardImpl {
 
 class BrokenAmbitionsEffect extends OneShotEffect {
 
+    private static final String effectText = "Counter target spell unless its controller pays {X}. Clash with an opponent. If you win, that spell's controller puts the top four cards of their library into their graveyard";
+
     protected Cost cost;
     protected DynamicValue genericMana;
 
     public BrokenAmbitionsEffect(Cost cost) {
         super(Outcome.Benefit);
         this.cost = cost;
-        this.staticText = "Counter target spell unless its controller pays {X}. Clash with an opponent. If you win, that spell's controller mills four cards";
+        this.staticText = effectText;
     }
 
     public BrokenAmbitionsEffect(DynamicValue genericMana) {
         super(Outcome.Detriment);
         this.genericMana = genericMana;
+        this.staticText = effectText;
     }
 
     public BrokenAmbitionsEffect(final BrokenAmbitionsEffect effect) {
@@ -67,6 +70,7 @@ class BrokenAmbitionsEffect extends OneShotEffect {
         if (effect.genericMana != null) {
             this.genericMana = effect.genericMana.copy();
         }
+        this.staticText = effectText;
     }
 
     @Override
diff --git a/Mage.Sets/src/mage/cards/c/ClotSliver.java b/Mage.Sets/src/mage/cards/c/ClotSliver.java
index e7bd0f9039..d3ba912fbb 100644
--- a/Mage.Sets/src/mage/cards/c/ClotSliver.java
+++ b/Mage.Sets/src/mage/cards/c/ClotSliver.java
@@ -1,7 +1,6 @@
 
 package mage.cards.c;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.common.SimpleStaticAbility;
@@ -16,6 +15,8 @@ import mage.constants.SubType;
 import mage.constants.Zone;
 import mage.filter.FilterPermanent;
 
+import java.util.UUID;
+
 /**
  * @author Loki
  */
@@ -33,7 +34,7 @@ public final class ClotSliver extends CardImpl {
 
         this.power = new MageInt(1);
         this.toughness = new MageInt(1);
-        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateSourceEffect(), new GenericManaCost(2)), Duration.WhileOnBattlefield, filter, false)));
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateSourceEffect("this permanent"), new GenericManaCost(2)), Duration.WhileOnBattlefield, filter, false)));
     }
 
     public ClotSliver(final ClotSliver card) {
diff --git a/Mage.Sets/src/mage/cards/h/HoldTheGates.java b/Mage.Sets/src/mage/cards/h/HoldTheGates.java
index e8b5b30675..83d95b9eaf 100644
--- a/Mage.Sets/src/mage/cards/h/HoldTheGates.java
+++ b/Mage.Sets/src/mage/cards/h/HoldTheGates.java
@@ -28,9 +28,10 @@ public final class HoldTheGates extends CardImpl {
 
         // Creatures you control get +0/+1 for each Gate you control and have vigilance.
         Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD,
-                new BoostControlledEffect(StaticValue.get(0), GateYouControlCount.instance, Duration.WhileOnBattlefield));
+                new BoostControlledEffect(StaticValue.get(0), GateYouControlCount.instance, Duration.WhileOnBattlefield)
+                        .setText("Creatures you control get +0/+1 for each Gate you control "));
         ability.addEffect(new GainAbilityControlledEffect(VigilanceAbility.getInstance(), Duration.WhileOnBattlefield, new FilterControlledCreaturePermanent("Creatures"))
-                .setText("Creatures you control get +0/+1 for each Gate you control and have vigilance"));
+                .setText("and have vigilance"));
         ability.addHint(GateYouControlHint.instance);
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/i/IlhargTheRazeBoar.java b/Mage.Sets/src/mage/cards/i/IlhargTheRazeBoar.java
index b17883fd8a..25a75accab 100644
--- a/Mage.Sets/src/mage/cards/i/IlhargTheRazeBoar.java
+++ b/Mage.Sets/src/mage/cards/i/IlhargTheRazeBoar.java
@@ -95,7 +95,7 @@ class IlhargTheRazeBoarEffect extends OneShotEffect {
         }
         game.getCombat().addAttackingCreature(permanent.getId(), game);
         Effect effect = new ReturnToHandTargetEffect();
-        effect.setText("return {this} to its owner's hand");
+        effect.setText("return " + permanent.getName() + " to its owner's hand");
         effect.setTargetPointer(new FixedTarget(permanent, game));
         game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect), source);
         return true;
diff --git a/Mage.Sets/src/mage/cards/l/LilianaDreadhordeGeneral.java b/Mage.Sets/src/mage/cards/l/LilianaDreadhordeGeneral.java
index a538d5ada6..5b77db31f2 100644
--- a/Mage.Sets/src/mage/cards/l/LilianaDreadhordeGeneral.java
+++ b/Mage.Sets/src/mage/cards/l/LilianaDreadhordeGeneral.java
@@ -31,7 +31,7 @@ import java.util.UUID;
  */
 public final class LilianaDreadhordeGeneral extends CardImpl {
 
-    private static final FilterControlledPermanent filter = new FilterControlledCreaturePermanent("creatures");
+    private static final FilterControlledPermanent filter = new FilterControlledCreaturePermanent(" creatures");
 
     public LilianaDreadhordeGeneral(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{4}{B}{B}");
diff --git a/Mage.Sets/src/mage/cards/o/OliviaVoldaren.java b/Mage.Sets/src/mage/cards/o/OliviaVoldaren.java
index 0c9b769960..bd8eafb3da 100644
--- a/Mage.Sets/src/mage/cards/o/OliviaVoldaren.java
+++ b/Mage.Sets/src/mage/cards/o/OliviaVoldaren.java
@@ -1,7 +1,6 @@
 
 package mage.cards.o;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleActivatedAbility;
@@ -24,6 +23,8 @@ import mage.filter.predicate.mageobject.CardIdPredicate;
 import mage.filter.predicate.permanent.AnotherPredicate;
 import mage.target.common.TargetCreaturePermanent;
 
+import java.util.UUID;
+
 /**
  *
  * @author nantuko
@@ -55,7 +56,11 @@ public final class OliviaVoldaren extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
 
         // {1}{R}: Olivia Voldaren deals 1 damage to another target creature. That creature becomes a Vampire in addition to its other types. Put a +1/+1 counter on Olivia Voldaren.
-        Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(1), new ManaCostsImpl("{1}{R}"));
+        Ability ability = new SimpleActivatedAbility(
+                Zone.BATTLEFIELD,
+                new DamageTargetEffect(1).setText("{this} deals 1 damage to another target creature"),
+                new ManaCostsImpl("{1}{R}")
+        );
         ability.addTarget(new TargetCreaturePermanent(filter));
         Effect effect = new AddCardSubTypeTargetEffect(SubType.VAMPIRE, Duration.WhileOnBattlefield);
         effect.setText("That creature becomes a Vampire in addition to its other types");
diff --git a/Mage.Sets/src/mage/cards/s/StonebrowKrosanHero.java b/Mage.Sets/src/mage/cards/s/StonebrowKrosanHero.java
index 468f5855e0..c5ffaa0327 100644
--- a/Mage.Sets/src/mage/cards/s/StonebrowKrosanHero.java
+++ b/Mage.Sets/src/mage/cards/s/StonebrowKrosanHero.java
@@ -1,7 +1,6 @@
 
 package mage.cards.s;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.common.AttacksCreatureYouControlTriggeredAbility;
 import mage.abilities.effects.Effect;
@@ -10,19 +9,21 @@ import mage.abilities.keyword.TrampleAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.SubType;
 import mage.constants.Duration;
+import mage.constants.SubType;
 import mage.constants.SuperType;
 import mage.filter.common.FilterControlledCreaturePermanent;
 import mage.filter.predicate.mageobject.AbilityPredicate;
 
+import java.util.UUID;
+
 /**
  *
  * @author emerald000
  */
 public final class StonebrowKrosanHero extends CardImpl {
     
-    private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("creature you control with trample");
+    private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent(" creature you control with trample");
     static {
         filter.add(new AbilityPredicate(TrampleAbility.class));
     }
diff --git a/Mage.Sets/src/mage/cards/w/WarrenScourgeElf.java b/Mage.Sets/src/mage/cards/w/WarrenScourgeElf.java
index 2969aa1cfd..739f123d83 100644
--- a/Mage.Sets/src/mage/cards/w/WarrenScourgeElf.java
+++ b/Mage.Sets/src/mage/cards/w/WarrenScourgeElf.java
@@ -1,7 +1,6 @@
 
 package mage.cards.w;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.keyword.ProtectionAbility;
 import mage.cards.CardImpl;
@@ -10,13 +9,15 @@ import mage.constants.CardType;
 import mage.constants.SubType;
 import mage.filter.FilterCard;
 
+import java.util.UUID;
+
 /**
  *
  * @author North
  */
 public final class WarrenScourgeElf extends CardImpl {
 
-    private static final FilterCard filter = new FilterCard("Goblin");
+    private static final FilterCard filter = new FilterCard("Goblins");
 
     static {
         filter.add(SubType.GOBLIN.getPredicate());
diff --git a/Mage/src/main/java/mage/abilities/effects/common/RegenerateSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/RegenerateSourceEffect.java
index 9bd10c4645..a1776b4b17 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/RegenerateSourceEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/RegenerateSourceEffect.java
@@ -2,10 +2,10 @@
 
 package mage.abilities.effects.common;
 
-import mage.constants.Duration;
-import mage.constants.Outcome;
 import mage.abilities.Ability;
 import mage.abilities.effects.ReplacementEffectImpl;
+import mage.constants.Duration;
+import mage.constants.Outcome;
 import mage.game.Game;
 import mage.game.events.GameEvent;
 import mage.game.permanent.Permanent;
@@ -21,6 +21,11 @@ public class RegenerateSourceEffect extends ReplacementEffectImpl {
         staticText = "Regenerate {this}";
     }
 
+    public RegenerateSourceEffect(String targetName) {
+        super(Duration.EndOfTurn, Outcome.Regenerate);
+        staticText = "Regenerate " + targetName;
+    }
+
     public RegenerateSourceEffect(final RegenerateSourceEffect effect) {
         super(effect);
     }
diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardSubTypeTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardSubTypeTargetEffect.java
index cf3e77643c..e013d1b60e 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardSubTypeTargetEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardSubTypeTargetEffect.java
@@ -47,13 +47,21 @@ public class AddCardSubTypeTargetEffect extends ContinuousEffectImpl {
 
     @Override
     public String getText(Mode mode) {
+        if (staticText != null && !staticText.isEmpty()) {
+            return staticText;
+        }
         StringBuilder sb = new StringBuilder();
         if (!mode.getTargets().isEmpty()) {
             sb.append("Target ").append(mode.getTargets().get(0).getTargetName());
         } else {
             sb.append("It ");
         }
-        sb.append(" becomes ").append(addedSubType).append(" in addition to its other types ").append(duration.toString());
+        if (addedSubType.toString().matches("(?i)^[AEIOUYaeiouy].*$")) {
+            sb.append(" becomes an ");
+        } else {
+            sb.append(" becomes a ");
+        }
+        sb.append(addedSubType).append(" in addition to its other types ").append(duration.toString());
         return sb.toString();
     }
 }
diff --git a/Mage/src/main/java/mage/game/command/emblems/DomriRadeEmblem.java b/Mage/src/main/java/mage/game/command/emblems/DomriRadeEmblem.java
index d71bb577c1..700f2ebbdf 100644
--- a/Mage/src/main/java/mage/game/command/emblems/DomriRadeEmblem.java
+++ b/Mage/src/main/java/mage/game/command/emblems/DomriRadeEmblem.java
@@ -1,7 +1,7 @@
 
 package mage.game.command.emblems;
 
-import mage.abilities.Ability;
+import mage.abilities.CompoundAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
 import mage.abilities.keyword.DoubleStrikeAbility;
@@ -22,16 +22,15 @@ public final class DomriRadeEmblem extends Emblem {
     // "Creatures you control have double strike, trample, hexproof and haste."
 
     public DomriRadeEmblem() {
-        this.setName("Emblem Domri");
+        this.setName("Emblem Domri Rade");
         FilterPermanent filter = new FilterControlledCreaturePermanent("Creatures");
-        GainAbilityControlledEffect effect = new GainAbilityControlledEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfGame, filter);
-        Ability ability = new SimpleStaticAbility(Zone.COMMAND, effect);
-        effect = new GainAbilityControlledEffect(TrampleAbility.getInstance(), Duration.EndOfGame, filter);
-        ability.addEffect(effect);
-        effect = new GainAbilityControlledEffect(HexproofAbility.getInstance(), Duration.EndOfGame, filter);
-        ability.addEffect(effect);
-        effect = new GainAbilityControlledEffect(HasteAbility.getInstance(), Duration.EndOfGame, filter);
-        ability.addEffect(effect);
-        this.getAbilities().add(ability);
+
+        CompoundAbility compoundAbilities = new CompoundAbility(
+                DoubleStrikeAbility.getInstance(),
+                TrampleAbility.getInstance(),
+                HexproofAbility.getInstance(),
+                HasteAbility.getInstance()
+        );
+        this.getAbilities().add(new SimpleStaticAbility(Zone.COMMAND, new GainAbilityControlledEffect(compoundAbilities, Duration.EndOfGame, filter)));
     }
 }

From cec7be86044b43793d56b4177579303f8f537604 Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Wed, 17 Jun 2020 14:32:34 -0500
Subject: [PATCH 346/586] - Text fixes

---
 Mage.Sets/src/mage/cards/b/BogDown.java       |  2 +-
 Mage.Sets/src/mage/cards/s/ScorchingLava.java | 13 +++++++------
 2 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/b/BogDown.java b/Mage.Sets/src/mage/cards/b/BogDown.java
index cf00d5babd..64fa2d7913 100644
--- a/Mage.Sets/src/mage/cards/b/BogDown.java
+++ b/Mage.Sets/src/mage/cards/b/BogDown.java
@@ -29,7 +29,7 @@ public final class BogDown extends CardImpl {
         // Target player discards two cards. If Bog Down was kicked, that player discards three cards instead.
         this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new DiscardTargetEffect(3),
                 new DiscardTargetEffect(2), KickedCondition.instance,
-                "Target player discards two cards. if this spell was kicked, that player discards three cards instead."));
+                "Target player discards two cards. If this spell was kicked, that player discards three cards instead."));
         this.getSpellAbility().addTarget(new TargetPlayer());
     }
 
diff --git a/Mage.Sets/src/mage/cards/s/ScorchingLava.java b/Mage.Sets/src/mage/cards/s/ScorchingLava.java
index 6cf9e7255b..6a16e3c70f 100644
--- a/Mage.Sets/src/mage/cards/s/ScorchingLava.java
+++ b/Mage.Sets/src/mage/cards/s/ScorchingLava.java
@@ -1,4 +1,3 @@
-
 package mage.cards.s;
 
 import java.util.UUID;
@@ -34,14 +33,17 @@ public final class ScorchingLava extends CardImpl {
 
         // Kicker {R}
         this.addAbility(new KickerAbility("{R}"));
-        // Scorching Lava deals 2 damage to any target. If Scorching Lava was kicked, that creature can't be regenerated this turn and if it would die this turn, exile it instead.
+        // Scorching Lava deals 2 damage to any target. If Scorching Lava was kicked, 
+        // that creature can't be regenerated this turn and if it would die this turn, exile it instead.
         this.getSpellAbility().addEffect(new DamageTargetEffect(2));
         this.getSpellAbility().addEffect(new ConditionalContinuousRuleModifyingEffect(
-                new CantRegenerateTargetEffect(Duration.EndOfTurn, "that creature"), new LockedInCondition(KickedCondition.instance)));
+                new CantRegenerateTargetEffect(Duration.EndOfTurn, "If Scorching Lava was kicked, "
+                        + "\n" + "that creature "),
+                new LockedInCondition(KickedCondition.instance)));
         this.getSpellAbility().addEffect(new ConditionalOneShotEffect(
                 new ExileTargetIfDiesEffect(),
-                new LockedInCondition(KickedCondition.instance)
-        ).setText("and if it would die this turn, exile it instead"));
+                new LockedInCondition(KickedCondition.instance),
+                "and if it would die this turn, exile it instead."));
         this.getSpellAbility().addTarget(new TargetAnyTarget());
     }
 
@@ -59,7 +61,6 @@ class ScorchingLavaEffect extends OneShotEffect {
 
     public ScorchingLavaEffect() {
         super(Outcome.Exile);
-        this.staticText = "and if it would die this turn, exile it instead";
     }
 
     public ScorchingLavaEffect(final ScorchingLavaEffect effect) {

From 7b3686e418677a88916012a55181128b13a6c8b1 Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Wed, 17 Jun 2020 15:26:47 -0500
Subject: [PATCH 347/586] - Text fix

---
 Mage.Sets/src/mage/cards/l/LifeMatrix.java | 18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/l/LifeMatrix.java b/Mage.Sets/src/mage/cards/l/LifeMatrix.java
index 09bb4c904e..137c62abe9 100644
--- a/Mage.Sets/src/mage/cards/l/LifeMatrix.java
+++ b/Mage.Sets/src/mage/cards/l/LifeMatrix.java
@@ -1,4 +1,3 @@
-
 package mage.cards.l;
 
 import java.util.UUID;
@@ -30,10 +29,19 @@ public final class LifeMatrix extends CardImpl {
     public LifeMatrix(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}");
 
-        // {4}, {T}: Put a matrix counter on target creature and that creature gains “Remove a matrix counter from this creature: Regenerate this creature.” Activate this ability only during your upkeep.
-        Ability ability = new ConditionalActivatedAbility(Zone.BATTLEFIELD, new AddCountersTargetEffect(CounterType.MATRIX.createInstance()), new GenericManaCost(4), 
-                new IsStepCondition(PhaseStep.UPKEEP), null);
-        Ability ability2 = new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateSourceEffect(), new RemoveCountersSourceCost(CounterType.MATRIX.createInstance()));
+        // {4}, {T}: Put a matrix counter on target creature and that creature gains 
+        // “Remove a matrix counter from this creature: Regenerate this creature.” 
+        // Activate this ability only during your upkeep.
+        Ability ability = new ConditionalActivatedAbility(
+                Zone.BATTLEFIELD,
+                new AddCountersTargetEffect(CounterType.MATRIX.createInstance()),
+                new GenericManaCost(4),
+                new IsStepCondition(PhaseStep.UPKEEP), "Put a matrix counter on target creature and "
+                + "that creature gains “Remove a matrix counter from this creature: "
+                + "Regenerate this creature.” Activate this ability only during your upkeep.");
+        Ability ability2 = new SimpleActivatedAbility(Zone.BATTLEFIELD,
+                new RegenerateSourceEffect(),
+                new RemoveCountersSourceCost(CounterType.MATRIX.createInstance()));
         ability.addEffect(new GainAbilityTargetEffect(ability2, Duration.Custom));
         ability.addCost(new TapSourceCost());
         ability.addTarget(new TargetCreaturePermanent());

From 327062240f1b2f0452bef186a3e7b4b219bd119e Mon Sep 17 00:00:00 2001
From: jmharmon <37360760+jmharmon@users.noreply.github.com>
Date: Wed, 17 Jun 2020 15:34:17 -0700
Subject: [PATCH 348/586] Implement Four Cards for M21 (#6651)

* Implement Four Cards for M21

Sanctum of Calm Waters
Sanctum of Shattered Heights
Transmogrify
Watcher of the Spheres

* Implement Sanctum of Calm Waters

* Implement Sanctum of Shattered Heights

* Implement Transmogrify

* Implement Watcher of the Spheres

* Add ability text and minimize number of lines

* Add ability text

* Clean up a few lines of code

* Update SanctumOfCalmWaters.java

* Update SanctumOfShatteredHeights.java

Co-authored-by: Evan Kranzler <theelk801@gmail.com>
---
 .../src/mage/cards/s/SanctumOfCalmWaters.java | 46 ++++++++++
 .../cards/s/SanctumOfShatteredHeights.java    | 56 +++++++++++
 Mage.Sets/src/mage/cards/t/Transmogrify.java  | 92 +++++++++++++++++++
 .../src/mage/cards/w/WatcherOfTheSpheres.java | 64 +++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  4 +
 5 files changed, 262 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/s/SanctumOfCalmWaters.java
 create mode 100644 Mage.Sets/src/mage/cards/s/SanctumOfShatteredHeights.java
 create mode 100644 Mage.Sets/src/mage/cards/t/Transmogrify.java
 create mode 100644 Mage.Sets/src/mage/cards/w/WatcherOfTheSpheres.java

diff --git a/Mage.Sets/src/mage/cards/s/SanctumOfCalmWaters.java b/Mage.Sets/src/mage/cards/s/SanctumOfCalmWaters.java
new file mode 100644
index 0000000000..e07cc5d225
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/SanctumOfCalmWaters.java
@@ -0,0 +1,46 @@
+package mage.cards.s;
+
+import mage.abilities.Ability;
+import mage.abilities.common.BeginningOfPreCombatMainTriggeredAbility;
+import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
+import mage.abilities.effects.common.DrawCardSourceControllerEffect;
+import mage.abilities.effects.common.discard.DiscardControllerEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.SuperType;
+import mage.constants.TargetController;
+import mage.filter.common.FilterControlledPermanent;
+
+import java.util.UUID;
+
+/**
+ * @author jmharmon
+ */
+
+public final class SanctumOfCalmWaters extends CardImpl {
+
+    public SanctumOfCalmWaters(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}");
+
+        this.addSuperType(SuperType.LEGENDARY);
+        this.subtype.add(SubType.SHRINE);
+
+        // At the beginning of your precombat main phase, you may draw X cards, where X is the number of Shrines you control. If you do, discard a card.
+        Ability ability = new BeginningOfPreCombatMainTriggeredAbility(new DrawCardSourceControllerEffect(new PermanentsOnBattlefieldCount(new FilterControlledPermanent(SubType.SHRINE)))
+                .setText("At the beginning of your precombat main phase, you may draw X cards, where X is the number of Shrines you control"),
+                TargetController.YOU, true);
+        ability.addEffect(new DiscardControllerEffect(1).setText("If you do, discard a card"));
+        this.addAbility(ability);
+    }
+
+    public SanctumOfCalmWaters(final SanctumOfCalmWaters card) {
+        super(card);
+    }
+
+    @Override
+    public SanctumOfCalmWaters copy() {
+        return new SanctumOfCalmWaters(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/cards/s/SanctumOfShatteredHeights.java b/Mage.Sets/src/mage/cards/s/SanctumOfShatteredHeights.java
new file mode 100644
index 0000000000..7bc906a3d5
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/SanctumOfShatteredHeights.java
@@ -0,0 +1,56 @@
+package mage.cards.s;
+
+import mage.abilities.Ability;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.common.DiscardTargetCost;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
+import mage.abilities.effects.common.DamageTargetEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.SuperType;
+import mage.constants.Zone;
+import mage.filter.FilterCard;
+import mage.filter.common.FilterControlledPermanent;
+import mage.filter.predicate.Predicates;
+import mage.target.common.TargetCardInHand;
+
+import java.util.UUID;
+
+/**
+ * @author jmharmon
+ */
+
+public final class SanctumOfShatteredHeights extends CardImpl {
+
+    private static final FilterCard filter = new FilterCard("a land card or Shrine card");
+
+    static {
+        filter.add(Predicates.or(CardType.LAND.getPredicate(), SubType.SHRINE.getPredicate()));
+    }
+
+    public SanctumOfShatteredHeights(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}");
+
+        this.addSuperType(SuperType.LEGENDARY);
+        this.subtype.add(SubType.SHRINE);
+
+        // {1}, Discard a land card or Shrine card: Sanctum of Shattered Heights deals X damage to target creature or planeswalker, where X is the number of Shrines you control.
+        Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(new PermanentsOnBattlefieldCount(new FilterControlledPermanent(SubType.SHRINE)))
+                .setText("Sanctum of Shattered Heights deals X damage to target creature or planeswalker, where X is the number of Shrines you control"),
+                new ManaCostsImpl("{1}"));
+        ability.addCost(new DiscardTargetCost(new TargetCardInHand(filter)));
+        this.addAbility(ability);
+    }
+
+    public SanctumOfShatteredHeights(final SanctumOfShatteredHeights card) {
+        super(card);
+    }
+
+    @Override
+    public SanctumOfShatteredHeights copy() {
+        return new SanctumOfShatteredHeights(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/cards/t/Transmogrify.java b/Mage.Sets/src/mage/cards/t/Transmogrify.java
new file mode 100644
index 0000000000..c21b545bb0
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/t/Transmogrify.java
@@ -0,0 +1,92 @@
+package mage.cards.t;
+
+import mage.abilities.Ability;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.ExileTargetEffect;
+import mage.cards.*;
+import mage.constants.CardType;
+import mage.constants.Outcome;
+import mage.constants.Zone;
+import mage.game.Game;
+import mage.game.permanent.Permanent;
+import mage.players.Library;
+import mage.players.Player;
+import mage.target.common.TargetCreaturePermanent;
+
+import java.util.UUID;
+
+/**
+ * @author jmharmon
+ */
+
+public final class Transmogrify extends CardImpl {
+
+    public Transmogrify(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{R}");
+
+        // Exile target creature.
+        this.getSpellAbility().addTarget(new TargetCreaturePermanent());
+        this.getSpellAbility().addEffect(new ExileTargetEffect());
+        // That creature’s controller reveals cards from the top of their library until they reveal a creature card.
+        // That player puts that card onto the battlefield, then shuffles the rest into their library.
+        this.getSpellAbility().addEffect(new TransmogrifyEffect());
+    }
+
+    public Transmogrify(final Transmogrify card) {
+        super(card);
+    }
+
+    @Override
+    public Transmogrify copy() {
+        return new Transmogrify(this);
+    }
+}
+
+class TransmogrifyEffect extends OneShotEffect {
+
+    public TransmogrifyEffect() {
+        super(Outcome.PutCreatureInPlay);
+        staticText = "That creature’s controller reveals cards from the top of their library until they reveal a creature card. That player puts that card onto the battlefield, then shuffles the rest into their library";
+    }
+
+    public TransmogrifyEffect(final TransmogrifyEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public TransmogrifyEffect copy() {
+        return new TransmogrifyEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source));
+        if (permanent != null) {
+            Player player = game.getPlayer(permanent.getControllerId());
+            if (player != null) {
+                Library library = player.getLibrary();
+                if (library.hasCards()) {
+                    Cards cards = new CardsImpl();
+                    Card toBattlefield = null;
+                    for (Card card : library.getCards(game)) {
+                        cards.add(card);
+                        if (card.isCreature()) {
+                            toBattlefield = card;
+                            break;
+                        }
+                    }
+                    if (toBattlefield != null) {
+                        player.moveCards(toBattlefield, Zone.BATTLEFIELD, source, game);
+                    }
+                    player.revealCards(source, cards, game);
+                    cards.remove(toBattlefield);
+                    if (!cards.isEmpty()) {
+                        player.shuffleLibrary(source, game);
+                    }
+                }
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/Mage.Sets/src/mage/cards/w/WatcherOfTheSpheres.java b/Mage.Sets/src/mage/cards/w/WatcherOfTheSpheres.java
new file mode 100644
index 0000000000..461db88742
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/w/WatcherOfTheSpheres.java
@@ -0,0 +1,64 @@
+package mage.cards.w;
+
+import mage.MageInt;
+import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.effects.common.continuous.BoostSourceEffect;
+import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect;
+import mage.abilities.keyword.FlyingAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+import mage.constants.Zone;
+import mage.filter.FilterPermanent;
+import mage.filter.common.FilterControlledCreaturePermanent;
+import mage.filter.common.FilterCreatureCard;
+import mage.filter.predicate.mageobject.AbilityPredicate;
+import mage.filter.predicate.permanent.AnotherPredicate;
+
+import java.util.UUID;
+
+/**
+ * @author jmharmon
+ */
+
+public final class WatcherOfTheSpheres extends CardImpl {
+
+    private static final FilterCreatureCard filter = new FilterCreatureCard("Creature spells with flying");
+    private static final FilterPermanent filter1 = new FilterControlledCreaturePermanent("another creature with flying");
+
+    static {
+        filter.add(new AbilityPredicate(FlyingAbility.class));
+        filter1.add(AnotherPredicate.instance);
+        filter1.add(new AbilityPredicate(FlyingAbility.class));
+    }
+
+    public WatcherOfTheSpheres(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{B}");
+
+        this.subtype.add(SubType.BIRD);
+        this.subtype.add(SubType.WIZARD);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(2);
+
+        // Flying
+        this.addAbility(FlyingAbility.getInstance());
+
+        // Creature spells with flying you cast cost {1} less to cast.
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filter, 1)));
+
+        // Whenever another creature with flying enters the battlefield under your control, Watcher of the Spheres gets +1/+1 until end of turn.
+        this.addAbility(new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, 1, Duration.EndOfTurn), filter1, false));
+    }
+
+    public WatcherOfTheSpheres(final WatcherOfTheSpheres card) {
+        super(card);
+    }
+
+    @Override
+    public WatcherOfTheSpheres copy() {
+        return new WatcherOfTheSpheres(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 5dee9f04c0..b507af7427 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -206,6 +206,8 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Rugged Highlands", 249, Rarity.COMMON, mage.cards.r.RuggedHighlands.class));
         cards.add(new SetCardInfo("Runed Halo", 32, Rarity.RARE, mage.cards.r.RunedHalo.class));
         cards.add(new SetCardInfo("Sabertooth Mauler", 202, Rarity.COMMON, mage.cards.s.SabertoothMauler.class));
+        cards.add(new SetCardInfo("Sanctum of Calm Waters", 68, Rarity.UNCOMMON, mage.cards.s.SanctumOfCalmWaters.class));
+        cards.add(new SetCardInfo("Sanctum of Shattered Heights", 157, Rarity.UNCOMMON, mage.cards.s.SanctumOfShatteredHeights.class));
         cards.add(new SetCardInfo("Scavenging Ooze", 204, Rarity.RARE, mage.cards.s.ScavengingOoze.class));
         cards.add(new SetCardInfo("Scorching Dragonfire", 158, Rarity.COMMON, mage.cards.s.ScorchingDragonfire.class));
         cards.add(new SetCardInfo("Scoured Barrens", 250, Rarity.COMMON, mage.cards.s.ScouredBarrens.class));
@@ -262,6 +264,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Tolarian Kraken", 80, Rarity.UNCOMMON, mage.cards.t.TolarianKraken.class));
         cards.add(new SetCardInfo("Tormod's Crypt", 241, Rarity.UNCOMMON, mage.cards.t.TormodsCrypt.class));
         cards.add(new SetCardInfo("Traitorous Greed", 166, Rarity.UNCOMMON, mage.cards.t.TraitorousGreed.class));
+        cards.add(new SetCardInfo("Transmogrify", 167, Rarity.RARE, mage.cards.t.Transmogrify.class));
         cards.add(new SetCardInfo("Tranquil Cove", 258, Rarity.COMMON, mage.cards.t.TranquilCove.class));
         cards.add(new SetCardInfo("Trufflesnout", 212, Rarity.COMMON, mage.cards.t.Trufflesnout.class));
         cards.add(new SetCardInfo("Turn to Slag", 168, Rarity.COMMON, mage.cards.t.TurnToSlag.class));
@@ -282,6 +285,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Wall of Runes", 85, Rarity.COMMON, mage.cards.w.WallOfRunes.class));
         cards.add(new SetCardInfo("Warded Battlements", 44, Rarity.COMMON, mage.cards.w.WardedBattlements.class));
         cards.add(new SetCardInfo("Warden of the Woods", 213, Rarity.UNCOMMON, mage.cards.w.WardenOfTheWoods.class));
+        cards.add(new SetCardInfo("Watcher of the Spheres", 227, Rarity.UNCOMMON, mage.cards.w.WatcherOfTheSpheres.class));
         cards.add(new SetCardInfo("Wildwood Patrol", 339, Rarity.COMMON, mage.cards.w.WildwoodPatrol.class));
         cards.add(new SetCardInfo("Wildwood Scourge", 214, Rarity.UNCOMMON, mage.cards.w.WildwoodScourge.class));
         cards.add(new SetCardInfo("Wind-Scarred Crag", 259, Rarity.COMMON, mage.cards.w.WindScarredCrag.class));

From 8e4d966ff3ae94245f3062eb70488163d01f95de Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Wed, 17 Jun 2020 18:36:34 -0400
Subject: [PATCH 349/586] updated M21 and JMP spoiler and reprints

---
 ...eAssassin.java => TwinbladeAssassins.java} | 10 +++---
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  2 +-
 Mage.Sets/src/mage/sets/Jumpstart.java        | 20 +++++++++++
 Utils/mtg-cards-data.txt                      | 33 +++++++++++++++++--
 4 files changed, 56 insertions(+), 9 deletions(-)
 rename Mage.Sets/src/mage/cards/t/{TwinbladeAssassin.java => TwinbladeAssassins.java} (83%)

diff --git a/Mage.Sets/src/mage/cards/t/TwinbladeAssassin.java b/Mage.Sets/src/mage/cards/t/TwinbladeAssassins.java
similarity index 83%
rename from Mage.Sets/src/mage/cards/t/TwinbladeAssassin.java
rename to Mage.Sets/src/mage/cards/t/TwinbladeAssassins.java
index e96a3c1008..d67fef4433 100644
--- a/Mage.Sets/src/mage/cards/t/TwinbladeAssassin.java
+++ b/Mage.Sets/src/mage/cards/t/TwinbladeAssassins.java
@@ -17,9 +17,9 @@ import java.util.UUID;
 /**
  * @author TheElk801
  */
-public final class TwinbladeAssassin extends CardImpl {
+public final class TwinbladeAssassins extends CardImpl {
 
-    public TwinbladeAssassin(UUID ownerId, CardSetInfo setInfo) {
+    public TwinbladeAssassins(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{G}");
 
         this.subtype.add(SubType.ELF);
@@ -36,12 +36,12 @@ public final class TwinbladeAssassin extends CardImpl {
         ), new MorbidWatcher());
     }
 
-    private TwinbladeAssassin(final TwinbladeAssassin card) {
+    private TwinbladeAssassins(final TwinbladeAssassins card) {
         super(card);
     }
 
     @Override
-    public TwinbladeAssassin copy() {
-        return new TwinbladeAssassin(this);
+    public TwinbladeAssassins copy() {
+        return new TwinbladeAssassins(this);
     }
 }
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index b507af7427..2e5022c177 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -269,7 +269,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Trufflesnout", 212, Rarity.COMMON, mage.cards.t.Trufflesnout.class));
         cards.add(new SetCardInfo("Turn to Slag", 168, Rarity.COMMON, mage.cards.t.TurnToSlag.class));
         cards.add(new SetCardInfo("Turret Ogre", 169, Rarity.COMMON, mage.cards.t.TurretOgre.class));
-        cards.add(new SetCardInfo("Twinblade Assassin", 226, Rarity.UNCOMMON, mage.cards.t.TwinbladeAssassin.class));
+        cards.add(new SetCardInfo("Twinblade Assassins", 226, Rarity.UNCOMMON, mage.cards.t.TwinbladeAssassins.class));
         cards.add(new SetCardInfo("Ugin, the Spirit Dragon", 1, Rarity.MYTHIC, mage.cards.u.UginTheSpiritDragon.class));
         cards.add(new SetCardInfo("Unleash Fury", 170, Rarity.UNCOMMON, mage.cards.u.UnleashFury.class));
         cards.add(new SetCardInfo("Unsubstantiate", 82, Rarity.UNCOMMON, mage.cards.u.Unsubstantiate.class));
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index 59f5c905de..4335d60522 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -20,25 +20,45 @@ public final class Jumpstart extends ExpansionSet {
         this.blockName = "Jumpstart";
         this.hasBasicLands = true;
 
+        cards.add(new SetCardInfo("Affectionate Indrik", 373, Rarity.UNCOMMON, mage.cards.a.AffectionateIndrik.class));
         cards.add(new SetCardInfo("Alloy Myr", 457, Rarity.COMMON, mage.cards.a.AlloyMyr.class));
         cards.add(new SetCardInfo("Auger Spree", 449, Rarity.COMMON, mage.cards.a.AugerSpree.class));
+        cards.add(new SetCardInfo("Bake into a Pie", 201, Rarity.COMMON, mage.cards.b.BakeIntoAPie.class));
+        cards.add(new SetCardInfo("Black Cat", 203, Rarity.COMMON, mage.cards.b.BlackCat.class));
+        cards.add(new SetCardInfo("Blood Divination", 207, Rarity.UNCOMMON, mage.cards.b.BloodDivination.class));
+        cards.add(new SetCardInfo("Bloodhunter Bat", 210, Rarity.COMMON, mage.cards.b.BloodhunterBat.class));
+        cards.add(new SetCardInfo("Bogbrew Witch", 211, Rarity.RARE, mage.cards.b.BogbrewWitch.class));
         cards.add(new SetCardInfo("Bone Picker", 212, Rarity.UNCOMMON, mage.cards.b.BonePicker.class));
+        cards.add(new SetCardInfo("Brushstrider", 381, Rarity.UNCOMMON, mage.cards.b.Brushstrider.class));
+        cards.add(new SetCardInfo("Bubbling Cauldron", 460, Rarity.UNCOMMON, mage.cards.b.BubblingCauldron.class));
         cards.add(new SetCardInfo("Chamber Sentry", 461, Rarity.RARE, mage.cards.c.ChamberSentry.class));
         cards.add(new SetCardInfo("Dinrova Horror", 450, Rarity.UNCOMMON, mage.cards.d.DinrovaHorror.class));
         cards.add(new SetCardInfo("Dutiful Attendant", 226, Rarity.COMMON, mage.cards.d.DutifulAttendant.class));
+        cards.add(new SetCardInfo("Entomber Exarch", 227, Rarity.UNCOMMON, mage.cards.e.EntomberExarch.class));
+        cards.add(new SetCardInfo("Festering Newt", 234, Rarity.COMMON, mage.cards.f.FesteringNewt.class));
+        cards.add(new SetCardInfo("Forest", 76, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Fusion Elemental", 451, Rarity.UNCOMMON, mage.cards.f.FusionElemental.class));
         cards.add(new SetCardInfo("Ghoulcaller's Accomplice", 237, Rarity.COMMON, mage.cards.g.GhoulcallersAccomplice.class));
+        cards.add(new SetCardInfo("Gravewaker", 241, Rarity.RARE, mage.cards.g.Gravewaker.class));
         cards.add(new SetCardInfo("Innocent Blood", 244, Rarity.COMMON, mage.cards.i.InnocentBlood.class));
         cards.add(new SetCardInfo("Ironroot Warlord", 452, Rarity.UNCOMMON, mage.cards.i.IronrootWarlord.class));
+        cards.add(new SetCardInfo("Isamaru, Hound of Konda", 113, Rarity.RARE, mage.cards.i.IsamaruHoundOfKonda.class));
+        cards.add(new SetCardInfo("Knight of the Tusk", 114, Rarity.COMMON, mage.cards.k.KnightOfTheTusk.class));
+        cards.add(new SetCardInfo("Last Gasp", 247, Rarity.COMMON, mage.cards.l.LastGasp.class));
         cards.add(new SetCardInfo("Lawmage's Binding", 453, Rarity.COMMON, mage.cards.l.LawmagesBinding.class));
         cards.add(new SetCardInfo("Maelstrom Archangel", 454, Rarity.MYTHIC, mage.cards.m.MaelstromArchangel.class));
+        cards.add(new SetCardInfo("Mire Triton", 257, Rarity.UNCOMMON, mage.cards.m.MireTriton.class));
         cards.add(new SetCardInfo("Mirrodin's Core", 492, Rarity.UNCOMMON, mage.cards.m.MirrodinsCore.class));
+        cards.add(new SetCardInfo("Nyxathid", 259, Rarity.RARE, mage.cards.n.Nyxathid.class));
         cards.add(new SetCardInfo("Prophetic Prism", 478, Rarity.COMMON, mage.cards.p.PropheticPrism.class));
         cards.add(new SetCardInfo("Raging Regisaur", 455, Rarity.UNCOMMON, mage.cards.r.RagingRegisaur.class));
+        cards.add(new SetCardInfo("Reanimate", 270, Rarity.RARE, mage.cards.r.Reanimate.class));
         cards.add(new SetCardInfo("Rupture Spire", 495, Rarity.COMMON, mage.cards.r.RuptureSpire.class));
         cards.add(new SetCardInfo("Shambling Goblin", 277, Rarity.COMMON, mage.cards.s.ShamblingGoblin.class));
         cards.add(new SetCardInfo("Skittering Surveyor", 486, Rarity.COMMON, mage.cards.s.SkitteringSurveyor.class));
         cards.add(new SetCardInfo("Swamp", 54, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Swarm of Bloodflies", 282, Rarity.UNCOMMON, mage.cards.s.SwarmOfBloodflies.class));
+        cards.add(new SetCardInfo("Tempting Witch", 283, Rarity.UNCOMMON, mage.cards.t.TemptingWitch.class));
         cards.add(new SetCardInfo("Terramorphic Expanse", 78, Rarity.COMMON, mage.cards.t.TerramorphicExpanse.class));
     }
 }
diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt
index f70c7e2060..10edd00769 100644
--- a/Utils/mtg-cards-data.txt
+++ b/Utils/mtg-cards-data.txt
@@ -37612,7 +37612,7 @@ Volcanic Geyser|Core Set 2021|171|U|{X}{R}{R}|Instant|||Volcanic Geyser deals X
 Volcanic Salvo|Core Set 2021|172|R|{10}{R}{R}|Sorcery|||This spell costs {X} less to cast, where X is the total power of creatures you control.$Volcanic Salvo deals 6 damage to each of up to two target creatures and/or planeswalkers.|
 Azusa, Lost but Seeking|Core Set 2021|173|R|{2}{G}|Legendary Creature - Human Monk|1|2|You may play two additional lands on each of your turns.|
 Burlfist Oak|Core Set 2021|174|U|{2}{G}{G}|Creature - Treefolk|2|3|Whenever you draw a card, Burlfist Oak gets +2/+2 until end of turn.|
-Canopy Stalker|Core Set 2021|175|U|{3}{G}|Creature - Cat|4|2|Canopy Stalker must be blocked if able.$When Canopy Stalker dies you gain 1 life for each creature that died this turn.|
+Canopy Stalker|Core Set 2021|175|U|{3}{G}|Creature - Cat|4|2|Canopy Stalker must be blocked if able.$When Canopy Stalker dies, you gain 1 life for each creature that died this turn.|
 Colossal Dreadmaw|Core Set 2021|176|C|{4}{G}{G}|Creature - Dinosaur|6|6|Trample|
 Cultivate|Core Set 2021|177|U|{2}{G}|Sorcery|||Search your library for up to two basic land cards, reveal those cards, and put one onto the battlefield tapped and the other into your hand. Then shuffle your library.|
 Drowsing Tyrannodon|Core Set 2021|178|C|{1}{G}|Creature - Dinosaur|3|3|Defender$As long as you control a creature with power 4 or greater, Drowsing Tyrannodon can attack as though it didn't have defender.|
@@ -37663,7 +37663,7 @@ Niambi, Esteemed Speaker|Core Set 2021|222|R|{W}{U}|Legendary Creature - Human C
 Obsessive Stitcher|Core Set 2021|223|U|{1}{U}{B}|Creature - Human Wizard|0|3|{T}: Draw a card, then discard a card.${2}{U}{B}, {T}, Sacrifice Obsessive Stitcher: Return target creature card from your graveyard to the battlefield.|
 Radha, Heart of Keld|Core Set 2021|224|R|{1}{R}{G}|Legendary Creature - Elf Warrior|3|3|As long as it's your turn, Radha, Heart of Keld has first strike.$You may look at the top card of your library any time, and you may play lands from the top of your library.${4}{R}{G}: Radha gets +X/+X until end of turn, where X is the number of lands you control.|
 Sanctum of All|Core Set 2021|225|R|{W}{U}{B}{R}{G}|Legendary Enchantment - Shrine|||At the beginning of your upkeep, you may search your library and/or graveyard for a Shrine card and put it onto the battlefield. If you search your library this way, shuffle it.$If an ability of another Shrine you control triggers while you control six or more Shrines, that ability triggers an additional time.|
-Twinblade Assassin|Core Set 2021|226|U|{3}{B}{G}|Creature - Elf Assassin|5|4|At the beginning of your end step, if a creature died this turn, draw a card.|
+Twinblade Assassins|Core Set 2021|226|U|{3}{B}{G}|Creature - Elf Assassin|5|4|At the beginning of your end step, if a creature died this turn, draw a card.|
 Watcher of the Spheres|Core Set 2021|227|U|{W}{U}|Creature - Bird Wizard|2|2|Flying$Creature spells with flying you cast cost {1} less to cast.$Whenever another creature with flying enters the battlefield under your control, Watcher of the Spheres gets +1/+1 until end of turn.|
 Chromatic Orrery|Core Set 2021|228|M|{7}|Legendary Artifact|||You may spend mana as though it were mana of any color.${T}: Add {C}{C}{C}{C}{C}.${5}, {T}: Draw a card for each color among permanents you control.|
 Chrome Replicator|Core Set 2021|229|U|{5}|Artifact Creature - Construct|4|4|When Chrome Replicator enters the battlefield, if you control two or more nonland, nontoken permanents with the same name as one another, create a 4/4 colorless Construct artifact creature token.|
@@ -37724,15 +37724,41 @@ Garruk's Warsteed|Core Set 2021|337|R|{3}{G}{G}|Creature - Rhino|3|5|Vigilance$W
 Predatory Wurm|Core Set 2021|338|U|{3}{G}|Creature - Wurm|4|4|Vigilance$Predatory Wurm gets +2/+2 as long as you control a Garruk planeswalker.|
 Wildwood Patrol|Core Set 2021|339|C|{2}{G}|Creature - Centaur Scout|4|2|Trample|
 Kels, Fight Fixer|Jumpstart|15|R|{2}{B}{B}|Legendary Creature - Azra Warlock|4|3|Menace$Whenever you sacrifice a creature, you may pay {U/B}. If you do, draw a card.${1}, Sacrifice a creature: Kels, Fight Fixer gains indestructible until end of turn.|
-Nocturnal Feeder|Jumpstart|16|C|{2}{B}|Creature - Vampire Rogue|2|1|Fying$When Nocturnal Feeder dies, each opponent loses 2 life and you gain 2 life.|
+Nocturnal Feeder|Jumpstart|16|C|{2}{B}|Creature - Vampire Rogue|2|1|Flying$When Nocturnal Feeder dies, each opponent loses 2 life and you gain 2 life.|
+Tinybones, Trinket Thief|Jumpstart|17|M|{1}{B}|Legendary Creature - Skeleton Rogue|1|2|At the beginning of each end step, if an opponent discarded a card this turn, you draw a card and you lose 1 life.${4}{B}{B}: Each opponent with no cards in hand loses 10 life.|
+Witch of the Moors|Jumpstart|18|R|{3}{B}{B}|Creature - Human Warlock|4|4|Deathtouch$At the beginning of your end step, if you gained life this turn, each opponent sacrifices a creature and you return up to one target creature card from your graveyard to your hand.|
+Neyith of the Dire Hunt|Jumpstart|30|R|{2}{G}{G}|Legendary Creature - Human Warrior|3|3|Whenever one or more creatures you control fight or become blocked, draw a card.$At the beginning of combat on your turn, you may pay {2}{R/G}. If you do, double target creature's power until end of turn. That creature must be blocked this combat if able.|
+Thriving Bluff|Jumpstart|33|C||Land|||Thriving Bluff enters the battlefield tapped.$As Thriving Bluff enters the battlefield, choose a color other than red.${T}: Add {R} or one mana of the chosen color.|
+Thriving Grove|Jumpstart|34|C||Land|||Thriving Grove enters the battlefield tapped.$As Thriving Grove enters the battlefield, choose a color other than green.${T}: Add {G} or one mana of the chosen color.|
+Thriving Heath|Jumpstart|35|C||Land|||Thriving Heath enters the battlefield tapped.$As Thriving Heath enters the battlefield, choose a color other than white.${T}: Add {W} or one mana of the chosen color.|
+Thriving Isle|Jumpstart|36|C||Land|||Thriving Isle enters the battlefield tapped.$As Thriving Isle enters the battlefield, choose a color other than blue.${T}: Add {U} or one mana of the chosen color.|
 Thriving Moor|Jumpstart|37|C||Land|||Thriving Moor enters the battlefield tapped.$As Thriving Moor enters the battlefield, choose a color other than black.${T}: Add {B} or one mana of the chosen color.|
 Swamp|Jumpstart|54|C||Basic Land - Swamp|||({T}: Add {B}.)|
+Forest|Jumpstart|76|C||Basic Land - Forest|||({T}: Add {G}.)|
 Terramorphic Expanse|Jumpstart|78|C||Land|||{T}, Sacrifice Terramorphic Expanse: Search your library for a basic land card, put it onto the battlefield tapped, then shuffle your library.|
+Isamaru, Hound of Konda|Jumpstart|113|R|{W}|Legendary Creature - Hound|2|2||
+Knight of the Tusk|Jumpstart|114|C|{4}{W}{W}|Creature - Human Knight|3|7|Vigilance|
+Bake into a Pie|Jumpstart|201|C|{2}{B}{B}|Instant|||Destroy target creature. Create a Food token.|
+Black Cat|Jumpstart|203|C|{1}{B}|Creature - Zombie Cat|1|1|When Black Cat dies, target opponent discards a card at random.|
+Blood Divination|Jumpstart|207|U|{3}{B}|Sorcery|||As an additional cost to cast this spell, sacrifice a creature.$Draw three cards.|
+Bloodhunter Bat|Jumpstart|210|C|{3}{B}|Creature - Bat|2|2|Flying$When Bloodhunter Bat enters the battlefield, target player loses 2 life and you gain 2 life.|
+Bogbrew Witch|Jumpstart|211|R|{3}{B}|Creature - Human Wizard|1|3|{2}, {T}: Search your library for a card named Festering Newt or Bubbling Cauldron, put it onto the battlefield tapped, then shuffle your library.|
 Bone Picker|Jumpstart|212|U|{3}{B}|Creature - Bird|3|2|This spell costs {3} less to cast if a creature died this turn.$Flying, deathtouch|
 Dutiful Attendant|Jumpstart|226|C|{2}{B}|Creature - Human Warrior|1|2|When Dutiful Attendant dies, return another target creature card from your graveyard to your hand.|
+Entomber Exarch|Jumpstart|227|U|{2}{B}{B}|Creature - Cleric|2|2|When Entomber Exarch enters the battlefield, choose one —$• Return target creature card from your graveyard to your hand.$• Target opponent reveals their hand. You choose a noncreature card from it. That player discards that card.|
+Festering Newt|Jumpstart|234|C|{B}|Creature - Salamander|1|1|When Festering Newt dies, target creature an opponent controls gets -1/-1 until end of turn. That creature gets -4/-4 instead if you control a creature named Bogbrew Witch.|
 Ghoulcaller's Accomplice|Jumpstart|237|C|{1}{B}|Creature - Human Rogue|2|2|{3}{B}, Exile Ghoulcaller's Accomplice from your graveyard: Create a 2/2 black Zombie creature token. Activate this ability only any time you could cast a sorcery.|
+Gravewaker|Jumpstart|241|R|{4}{B}{B}|Creature - Bird Spirit|5|5|Flying${5}{B}{B}: Return target creature card from your graveyard to the battlefield tapped.|
 Innocent Blood|Jumpstart|244|C|{B}|Sorcery|||Each player sacrifices a creature.|
+Last Gasp|Jumpstart|247|C|{1}{B}|Instant|||Target creature gets -3/-3 until end of turn.|
+Mire Triton|Jumpstart|257|U|{1}{B}|Creature - Zombie Merfolk|2|1|Deathtouch$When Mire Triton enters the battlefield, put the top two cards of your library into your graveyard and you gain 2 life.|
+Nyxathid|Jumpstart|259|R|{1}{B}{B}|Creature - Elemental|7|7|As Nyxathid enters the battlefield, choose an opponent.$Nyxathid gets -1/-1 for each card in the chosen player's hand.|
+Reanimate|Jumpstart|270|R|{B}|Sorcery|||Put target creature card from a graveyard onto the battlefield under your control. You lose life equal to its converted mana cost.|
 Shambling Goblin|Jumpstart|277|C|{B}|Creature - Zombie Goblin|1|1|When Shambling Goblin dies, target creature an opponent controls gets -1/-1 until end of turn.|
+Swarm of Bloodflies|Jumpstart|282|U|{4}{B}|Creature - Insect|0|0|Flying$Swarm of Bloodflies enters the battlefield with two +1/+1 counters on it.$Whenever another creature dies, put a +1/+1 counter on Swarm of Bloodflies.|
+Tempting Witch|Jumpstart|283|U|{2}{B}|Creature - Human Warlock|1|3|When Tempting Witch enters the battlefield, create a Food token.${2}, {T}, Sacrifice a Food: Target player loses 3 life.|
+Affectionate Indrik|Jumpstart|373|U|{5}{G}|Creature - Beast|4|4|When Affectionate Indrik enters the battlefield, you may have it fight target creature you don't control.|
+Brushstrider|Jumpstart|381|U|{1}{G}|Creature - Beast|3|1|Vigilance|
 Auger Spree|Jumpstart|449|C|{1}{B}{R}|Instant|||Target creature gets +4/-4 until end of turn.|
 Dinrova Horror|Jumpstart|450|U|{4}{U}{B}|Creature - Horror|4|4|When Dinrova Horror enters the battlefield, return target permanent to its owner's hand, then that player discards a card.|
 Fusion Elemental|Jumpstart|451|U|{W}{U}{B}{R}{G}|Creature - Elemental|8|8||
@@ -37741,6 +37767,7 @@ Lawmage's Binding|Jumpstart|453|C|{1}{W}{U}|Enchantment - Aura|||Flash$Enchant c
 Maelstrom Archangel|Jumpstart|454|M|{W}{U}{B}{R}{G}|Creature - Angel|5|5|Flying$Whenever Maelstrom Archangel deals combat damage to a player, you may cast a nonland card from your hand without paying its mana cost.|
 Raging Regisaur|Jumpstart|455|U|{2}{R}{G}|Creature - Dinosaur|4|4|Whenever Raging Regisaur attacks, it deals 1 damage to any target.|
 Alloy Myr|Jumpstart|457|C|{3}|Artifact Creature - Myr|2|2|{T}: Add one mana of any color.|
+Bubbling Cauldron|Jumpstart|460|U|{2}|Artifact|||{1}, {T}, Sacrifice a creature: You gain 4 life.${1}, {T}, Sacrifice a creature named Festering Newt: Each opponent loses 4 life. You gain life equal to the life lost this way.|
 Chamber Sentry|Jumpstart|461|R|{X}|Artifact Creature - Construct|0|0|Chamber Sentry enters the battlefield with a +1/+1 counter on it for each color of mana spent to cast it.${X}, {T}, Remove X +1/+1 counters from Chamber Sentry: It deals X damage to any target.${W}{U}{B}{R}{G}: Return Chamber Sentry from your graveyard to your hand.|
 Prophetic Prism|Jumpstart|478|C|{2}|Artifact|||When Prophetic Prism enters the battlefield, draw a card.${1}, {T}: Add one mana of any color.|
 Skittering Surveyor|Jumpstart|486|C|{3}|Artifact Creature - Construct|1|2|When Skittering Surveyor enters the battlefield, you may search your library for a basic land card, reveal it, put it into your hand, then shuffle your library.|

From 85709c0a1681c62652e494cef9144a78849b6e88 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Thu, 18 Jun 2020 01:19:23 +0200
Subject: [PATCH 350/586] * Some rework/clean up of the PlayFromNotOwnHandZone
 effects (fixes #6580). Some added tests.

---
 .../src/mage/cards/a/AminatousAugury.java     |   3 +-
 Mage.Sets/src/mage/cards/b/BolassCitadel.java |  55 ++++--
 Mage.Sets/src/mage/cards/f/FallenShinobi.java |  58 +------
 .../mage/cards/g/GolosTirelessPilgrim.java    |  53 +-----
 .../src/mage/cards/i/IgniteTheFuture.java     |  81 +--------
 .../src/mage/cards/i/IntetTheDreamer.java     |  89 ++--------
 .../src/mage/cards/k/KheruSpellsnatcher.java  |  71 +-------
 .../src/mage/cards/m/MagusOfTheMind.java      |  61 +------
 Mage.Sets/src/mage/cards/m/MindsDesire.java   |  71 +-------
 .../mage/cards/n/NarsetEnlightenedMaster.java |  10 +-
 .../mage/cards/n/NicolBolasGodPharaoh.java    |  53 +-----
 Mage.Sets/src/mage/cards/o/OraclesVault.java  | 106 +-----------
 .../mage/cards/p/PlaneswalkersMischief.java   |  11 +-
 .../src/mage/cards/r/ReleaseToTheWind.java    |  57 +-----
 Mage.Sets/src/mage/cards/s/SinsOfThePast.java |   9 +-
 Mage.Sets/src/mage/cards/s/Spelljack.java     |   2 +-
 Mage.Sets/src/mage/cards/s/StolenGoods.java   |  17 +-
 .../src/mage/cards/t/TemporalAperture.java    |   8 +-
 .../mage/cards/u/UrzaLordHighArtificer.java   |  67 +-------
 .../asthough/PlayFromNonHandZoneTest.java     | 162 ++++++++++++++++++
 .../split/CastSplitCardsWithFuseTest.java     |   4 +-
 .../triggers/dies/TidehollowScullerTest.java  |   2 +-
 .../java/org/mage/test/player/TestPlayer.java |  60 +++----
 .../base/impl/CardTestPlayerAPIImpl.java      |   2 +-
 .../java/mage/abilities/SpellAbility.java     |   2 +-
 .../abilities/effects/AsThoughEffectImpl.java |  43 ++++-
 .../PlayFromNotOwnHandZoneTargetEffect.java   |  97 ++++++++++-
 Mage/src/main/java/mage/cards/Card.java       |   2 +
 .../target/targetpointer/FixedTarget.java     |   6 +
 29 files changed, 465 insertions(+), 797 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/a/AminatousAugury.java b/Mage.Sets/src/mage/cards/a/AminatousAugury.java
index 9e230a447c..78a047d2d3 100644
--- a/Mage.Sets/src/mage/cards/a/AminatousAugury.java
+++ b/Mage.Sets/src/mage/cards/a/AminatousAugury.java
@@ -175,7 +175,8 @@ class AminatousAuguryCastFromExileEffect extends AsThoughEffectImpl {
                             usedCardTypes.add(CardType.fromString(choice.getChoice()));
                         }
                         usedCardTypes.addAll(unusedCardTypes);
-                        player.setCastSourceIdWithAlternateMana(sourceId, null, null);
+                        player.setCastSourceIdWithAlternateMana(sourceId, null, card.getSpellAbility().getCosts());
+                        // TODO- This does not correctly work when you cancel the cast (has to be done by watcher I guess)
                         game.getState().setValue(source.getSourceId().toString() + "cardTypes", usedCardTypes);
                     }
                     return true;
diff --git a/Mage.Sets/src/mage/cards/b/BolassCitadel.java b/Mage.Sets/src/mage/cards/b/BolassCitadel.java
index 2827e6a935..00c6d09653 100644
--- a/Mage.Sets/src/mage/cards/b/BolassCitadel.java
+++ b/Mage.Sets/src/mage/cards/b/BolassCitadel.java
@@ -20,9 +20,12 @@ import mage.filter.predicate.Predicates;
 import mage.game.Game;
 import mage.players.Player;
 import mage.target.common.TargetControlledPermanent;
-import mage.util.CardUtil;
 
 import java.util.UUID;
+import mage.cards.SplitCard;
+import mage.cards.SplitCardHalf;
+import mage.util.CardUtil;
+
 
 /**
  * @author jeffwadsworth
@@ -94,23 +97,43 @@ class BolassCitadelPlayTheTopCardEffect extends AsThoughEffectImpl {
 
     @Override
     public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) {
-        Card cardToCheck = game.getCard(objectId);
-        objectId = CardUtil.getMainCardId(game, objectId); // for split cards
+        Player player = game.getPlayer(playerId);
+        if (player != null) {
+            Card topCard = player.getLibrary().getFromTop(game);
+            UUID objectIdToCast = CardUtil.getMainCardId(game, objectId); // for adventure cards
+            if (topCard == null || !topCard.getId().equals(objectIdToCast)) {
+                return false;
+            }
+            if (!topCard.isLand()) {
+                if (topCard instanceof SplitCard) {
+                    SplitCardHalf leftCard = ((SplitCard) topCard).getLeftHalfCard();
+                    PayLifeCost lifeCost = new PayLifeCost(leftCard.getSpellAbility().getManaCosts().convertedManaCost());
+                    Costs leftCosts = new CostsImpl();
+                    leftCosts.add(lifeCost);
+                    leftCosts.addAll(leftCard.getSpellAbility().getCosts());
+                    player.setCastSourceIdWithAlternateMana(leftCard.getId(), null, leftCosts);
 
-        if (!isAbilityAppliedForAlternateCast(cardToCheck, affectedAbility, playerId, source)) {
-            return false;
-        }
-
-        Player controller = game.getPlayer(cardToCheck.getOwnerId());
-        Card topCard = controller == null ? null : controller.getLibrary().getFromTop(game);
-        if (topCard != null && objectId.equals(topCard.getId())) {
-            // add the life cost first
-            PayLifeCost cost = new PayLifeCost(affectedAbility.getManaCosts().convertedManaCost());
-            Costs costs = new CostsImpl();
-            costs.add(cost);
-            costs.addAll(affectedAbility.getCosts());
-            controller.setCastSourceIdWithAlternateMana(affectedAbility.getSourceId(), null, costs);
+                    SplitCardHalf rightCard = ((SplitCard) topCard).getRightHalfCard();
+                    lifeCost = new PayLifeCost(rightCard.getSpellAbility().getManaCosts().convertedManaCost());
+                    Costs rightCosts = new CostsImpl();
+                    rightCosts.add(lifeCost);
+                    rightCosts.addAll(rightCard.getSpellAbility().getCosts());
+                    player.setCastSourceIdWithAlternateMana(rightCard.getId(), null, rightCosts);
+                } else {
+                    if (affectedAbility == null) {
+                        affectedAbility = topCard.getSpellAbility();                        
+                    } else {
+                        objectIdToCast = affectedAbility.getSourceId();
+                    }
+                    PayLifeCost cost = new PayLifeCost(affectedAbility.getManaCosts().convertedManaCost());
+                    Costs costs = new CostsImpl();
+                    costs.add(cost);
+                    costs.addAll(affectedAbility.getCosts());
+                    player.setCastSourceIdWithAlternateMana(objectIdToCast, null, costs);
+                }
+            }
             return true;
+
         }
         return false;
     }
diff --git a/Mage.Sets/src/mage/cards/f/FallenShinobi.java b/Mage.Sets/src/mage/cards/f/FallenShinobi.java
index bc6cf45403..6405ab0686 100644
--- a/Mage.Sets/src/mage/cards/f/FallenShinobi.java
+++ b/Mage.Sets/src/mage/cards/f/FallenShinobi.java
@@ -4,7 +4,6 @@ import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
-import mage.abilities.effects.AsThoughEffectImpl;
 import mage.abilities.effects.ContinuousEffect;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.keyword.NinjutsuAbility;
@@ -12,9 +11,10 @@ import mage.cards.*;
 import mage.constants.*;
 import mage.game.Game;
 import mage.players.Player;
-import mage.target.targetpointer.FixedTarget;
 
 import java.util.UUID;
+import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect;
+import mage.target.targetpointer.FixedTargets;
 
 /**
  * @author TheElk801
@@ -52,8 +52,8 @@ class FallenShinobiEffect extends OneShotEffect {
 
     FallenShinobiEffect() {
         super(Outcome.Benefit);
-        staticText = "that player exiles the top two cards of their library. " +
-                "Until end of turn, you may play those cards without paying their mana cost.";
+        staticText = "that player exiles the top two cards of their library. "
+                + "Until end of turn, you may play those cards without paying their mana cost.";
     }
 
     private FallenShinobiEffect(final FallenShinobiEffect effect) {
@@ -71,53 +71,7 @@ class FallenShinobiEffect extends OneShotEffect {
         if (player == null) {
             return false;
         }
-        Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, 2));
-        player.moveCards(cards, Zone.EXILED, source, game);
-        for (Card card : cards.getCards(game)) {
-            ContinuousEffect effect = new UrzaLordHighArtificerFromExileEffect();
-            effect.setTargetPointer(new FixedTarget(card, game));
-            game.addEffect(effect, source);
-        }
-        return true;
-    }
-}
-
-class UrzaLordHighArtificerFromExileEffect extends AsThoughEffectImpl {
-
-    UrzaLordHighArtificerFromExileEffect() {
-        super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit);
-        staticText = "you may play that card without paying its mana cost";
-    }
-
-    private UrzaLordHighArtificerFromExileEffect(final UrzaLordHighArtificerFromExileEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        return true;
-    }
-
-    @Override
-    public UrzaLordHighArtificerFromExileEffect copy() {
-        return new UrzaLordHighArtificerFromExileEffect(this);
-    }
-
-    @Override
-    public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
-        if (!affectedControllerId.equals(source.getControllerId())
-                || !getTargetPointer().getTargets(game, source).contains(objectId)) {
-            return false;
-        }
-        Card card = game.getCard(objectId);
-        if (card == null || card.isLand() || card.getSpellAbility().getCosts() == null) {
-            return true;
-        }
-        Player player = game.getPlayer(affectedControllerId);
-        if (player == null) {
-            return false;
-        }
-        player.setCastSourceIdWithAlternateMana(objectId, null, card.getSpellAbility().getCosts());
-        return true;
+        return PlayFromNotOwnHandZoneTargetEffect.exileAndPlayFromExile(game, source, player.getLibrary().getTopCards(game, 2), 
+                TargetController.YOU, Duration.EndOfTurn, true);          
     }
 }
diff --git a/Mage.Sets/src/mage/cards/g/GolosTirelessPilgrim.java b/Mage.Sets/src/mage/cards/g/GolosTirelessPilgrim.java
index 1f4d3077e2..83d87fc44b 100644
--- a/Mage.Sets/src/mage/cards/g/GolosTirelessPilgrim.java
+++ b/Mage.Sets/src/mage/cards/g/GolosTirelessPilgrim.java
@@ -7,9 +7,9 @@ import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
-import mage.abilities.effects.AsThoughEffectImpl;
 import mage.abilities.effects.ContinuousEffect;
 import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect;
 import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect;
 import mage.cards.Card;
 import mage.cards.CardImpl;
@@ -19,7 +19,7 @@ import mage.filter.StaticFilters;
 import mage.game.Game;
 import mage.players.Player;
 import mage.target.common.TargetCardInLibrary;
-import mage.target.targetpointer.FixedTarget;
+import mage.target.targetpointer.FixedTargets;
 
 /**
  * @author TheElk801
@@ -75,15 +75,8 @@ class GolosTirelessPilgrimEffect extends OneShotEffect {
             return false;
         }
         Set<Card> cards = player.getLibrary().getTopCards(game, 3);
-        player.moveCards(cards, Zone.EXILED, source, game);
-        cards.stream()
-                .filter(card -> game.getState().getZone(card.getId()) == Zone.EXILED)
-                .forEach(card -> {
-                    ContinuousEffect effect = new GolosTirelessPilgrimCastFromExileEffect();
-                    effect.setTargetPointer(new FixedTarget(card, game));
-                    game.addEffect(effect, source);
-                });
-        return true;
+        return PlayFromNotOwnHandZoneTargetEffect.exileAndPlayFromExile(game, source, cards, 
+                TargetController.YOU, Duration.EndOfTurn, true);        
     }
 
     @Override
@@ -91,41 +84,3 @@ class GolosTirelessPilgrimEffect extends OneShotEffect {
         return new GolosTirelessPilgrimEffect(this);
     }
 }
-
-class GolosTirelessPilgrimCastFromExileEffect extends AsThoughEffectImpl {
-
-    GolosTirelessPilgrimCastFromExileEffect() {
-        super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit);
-    }
-
-    private GolosTirelessPilgrimCastFromExileEffect(final GolosTirelessPilgrimCastFromExileEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        return true;
-    }
-
-    @Override
-    public GolosTirelessPilgrimCastFromExileEffect copy() {
-        return new GolosTirelessPilgrimCastFromExileEffect(this);
-    }
-
-    @Override
-    public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
-        if (!objectId.equals(getTargetPointer().getFirst(game, source))
-                || !affectedControllerId.equals(source.getControllerId())) {
-            return false;
-        }
-        Card card = game.getCard(objectId);
-        if (card == null || card.isLand() || card.getSpellAbility().getCosts() == null) {
-            return true;
-        }
-        Player player = game.getPlayer(affectedControllerId);
-        if (player != null) {
-            player.setCastSourceIdWithAlternateMana(objectId, null, card.getSpellAbility().getCosts());
-        }
-        return true;
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/i/IgniteTheFuture.java b/Mage.Sets/src/mage/cards/i/IgniteTheFuture.java
index 14cd2b90b2..926b3ca706 100644
--- a/Mage.Sets/src/mage/cards/i/IgniteTheFuture.java
+++ b/Mage.Sets/src/mage/cards/i/IgniteTheFuture.java
@@ -2,7 +2,6 @@ package mage.cards.i;
 
 import mage.abilities.Ability;
 import mage.abilities.costs.mana.ManaCostsImpl;
-import mage.abilities.effects.AsThoughEffectImpl;
 import mage.abilities.effects.ContinuousEffect;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.keyword.FlashbackAbility;
@@ -13,10 +12,11 @@ import mage.constants.*;
 import mage.game.Game;
 import mage.game.stack.Spell;
 import mage.players.Player;
-import mage.target.targetpointer.FixedTarget;
 
 import java.util.Set;
 import java.util.UUID;
+import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect;
+import mage.target.targetpointer.FixedTargets;
 
 /**
  * @author TheElk801
@@ -69,81 +69,8 @@ class IgniteTheFutureEffect extends OneShotEffect {
         if (controller == null || spell == null) {
             return false;
         }
-        boolean forFree = spell.getFromZone() == Zone.GRAVEYARD;
         Set<Card> cards = controller.getLibrary().getTopCards(game, 3);
-        controller.moveCards(cards, Zone.EXILED, source, game);
-
-        cards.stream().forEach(card -> {
-            ContinuousEffect effect = new IgniteTheFutureMayPlayEffect(forFree);
-            effect.setTargetPointer(new FixedTarget(card.getId(), game));
-            game.addEffect(effect, source);
-        });
-
-        return true;
-    }
-}
-
-class IgniteTheFutureMayPlayEffect extends AsThoughEffectImpl {
-
-    private int castOnTurn = 0;
-    private final boolean forFree;
-
-    IgniteTheFutureMayPlayEffect(boolean forFree) {
-        super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit);
-        this.forFree = forFree;
-        if (forFree) {
-            this.staticText = "Until the end of your next turn, you may play that card without playing its mana cost.";
-        } else {
-            this.staticText = "Until the end of your next turn, you may play that card.";
-        }
-    }
-
-    private IgniteTheFutureMayPlayEffect(final IgniteTheFutureMayPlayEffect effect) {
-        super(effect);
-        castOnTurn = effect.castOnTurn;
-        this.forFree = effect.forFree;
-    }
-
-    @Override
-    public IgniteTheFutureMayPlayEffect copy() {
-        return new IgniteTheFutureMayPlayEffect(this);
-    }
-
-    @Override
-    public void init(Ability source, Game game) {
-        super.init(source, game);
-        castOnTurn = game.getTurnNum();
-    }
-
-    @Override
-    public boolean isInactive(Ability source, Game game) {
-        return castOnTurn != game.getTurnNum()
-                && game.getPhase().getStep().getType() == PhaseStep.END_TURN
-                && game.isActivePlayer(source.getControllerId());
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        return true;
-    }
-
-    @Override
-    public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
-        if (!source.isControlledBy(affectedControllerId)
-                || !getTargetPointer().getTargets(game, source).contains(objectId)) {
-            return false;
-        }
-        if (!forFree) {
-            return true;
-        }
-        Card card = game.getCard(objectId);
-        if (card == null || card.isLand() || card.getSpellAbility().getCosts() == null) {
-            return true;
-        }
-        Player player = game.getPlayer(affectedControllerId);
-        if (player != null) {
-            player.setCastSourceIdWithAlternateMana(objectId, null, card.getSpellAbility().getCosts());
-        }
-        return true;
+        return PlayFromNotOwnHandZoneTargetEffect.exileAndPlayFromExile(game, source, cards, 
+                TargetController.YOU, Duration.UntilEndOfYourNextTurn, Zone.GRAVEYARD.equals(spell.getFromZone()));
     }
 }
diff --git a/Mage.Sets/src/mage/cards/i/IntetTheDreamer.java b/Mage.Sets/src/mage/cards/i/IntetTheDreamer.java
index 5bf1a68777..247fcad509 100644
--- a/Mage.Sets/src/mage/cards/i/IntetTheDreamer.java
+++ b/Mage.Sets/src/mage/cards/i/IntetTheDreamer.java
@@ -14,12 +14,16 @@ import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.*;
-import mage.game.ExileZone;
 import mage.game.Game;
 import mage.players.Player;
 import mage.util.CardUtil;
 
 import java.util.UUID;
+import mage.abilities.condition.common.SourceOnBattlefieldCondition;
+import mage.abilities.decorator.ConditionalAsThoughEffect;
+import mage.abilities.effects.ContinuousEffect;
+import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect;
+import mage.target.targetpointer.FixedTarget;
 
 /**
  * @author fireshoes
@@ -39,15 +43,13 @@ public final class IntetTheDreamer extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
 
         // Whenever Intet, the Dreamer deals combat damage to a player, you may pay {2}{U}. If you do, exile the top card of your library face down.
+        // You may play that card without paying its mana cost for as long as Intet remains on the battlefield.
         this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(
                 new DoIfCostPaid(new IntetTheDreamerExileEffect(), new ManaCostsImpl("{2}{U}")), false, true));
 
         // You may look at that card for as long as it remains exiled.
         this.addAbility(new SimpleStaticAbility(Zone.ALL, new IntetTheDreamerLookEffect()));
 
-        // You may play that card without paying its mana cost for as long as Intet remains on the battlefield.
-        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new IntetTheDreamerCastEffect()));
-
     }
 
     public IntetTheDreamer(final IntetTheDreamer card) {
@@ -64,7 +66,7 @@ class IntetTheDreamerExileEffect extends OneShotEffect {
 
     public IntetTheDreamerExileEffect() {
         super(Outcome.Discard);
-        staticText = "exile the top card of your library face down";
+        staticText = "exile the top card of your library face down. You may play that card without paying its mana cost for as long as Intet remains on the battlefield";
     }
 
     public IntetTheDreamerExileEffect(final IntetTheDreamerExileEffect effect) {
@@ -77,20 +79,18 @@ class IntetTheDreamerExileEffect extends OneShotEffect {
         if (controller != null) {
             Card card = controller.getLibrary().getFromTop(game);
             MageObject sourceObject = source.getSourceObject(game);
-            if (card != null
-                    && sourceObject != null) {
+            if (card != null && sourceObject != null) {
                 card.setFaceDown(true, game);
-                controller.moveCardsToExile(
-                        card,
-                        source,
-                        game,
-                        false,
-                        CardUtil.getExileZoneId(game,
-                                source.getSourceId(),
-                                sourceObject.getZoneChangeCounter(game)),  // sourceObject must be used due to source not working correctly
+                controller.moveCardsToExile(card, source, game, false,
+                        CardUtil.getExileZoneId(game, source.getSourceId(), sourceObject.getZoneChangeCounter(game)), // sourceObject must be used due to source not working correctly
                         sourceObject.getIdName());
                 card.setFaceDown(true, game);
-                game.getState().setValue("Exiled_IntetTheDreamer" + card.getId(), Boolean.TRUE);
+                ContinuousEffect effect = new ConditionalAsThoughEffect(
+                        new PlayFromNotOwnHandZoneTargetEffect(Zone.EXILED, TargetController.YOU, Duration.Custom, true), 
+                        SourceOnBattlefieldCondition.instance);
+                effect.setTargetPointer(new FixedTarget(card, game));
+                game.getState().addEffect(effect, source);
+                game.getState().setValue("Exiled_IntetTheDreamer" + card.getId(), Boolean.TRUE); // TODO This value will never be removed
                 return true;
             }
         }
@@ -103,63 +103,6 @@ class IntetTheDreamerExileEffect extends OneShotEffect {
     }
 }
 
-class IntetTheDreamerCastEffect extends AsThoughEffectImpl {
-
-    public IntetTheDreamerCastEffect() {
-        super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit);
-        staticText = "You may play the card from exile without paying its mana cost for as long as {this} remains on the battlefield";
-    }
-
-    public IntetTheDreamerCastEffect(final IntetTheDreamerCastEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        return true;
-    }
-
-    @Override
-    public IntetTheDreamerCastEffect copy() {
-        return new IntetTheDreamerCastEffect(this);
-    }
-
-    @Override
-    public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
-        if (affectedControllerId.equals(source.getControllerId())) {
-            Player controller = game.getPlayer(source.getControllerId());
-            MageObject sourceObject = source.getSourceObject(game);
-            if (controller != null
-                    && sourceObject != null) {
-                Card card = game.getCard(objectId);
-                if (card != null
-                        && card.isFaceDown(game)) {
-                    ExileZone zone = game.getExile().getExileZone(
-                            CardUtil.getExileZoneId(game,
-                                    source.getSourceId(),
-                                    sourceObject.getZoneChangeCounter(game))); // sourceObject must be used due to source not working correctly
-                    if (zone != null
-                            && zone.contains(card.getId())) {
-                        if (card.isLand()) {
-                            if (game.canPlaySorcery(controller.getId())
-                                    && game.getPlayer(controller.getId()).canPlayLand()) {
-                                return controller.chooseUse(outcome, "Play " + card.getIdName() + '?', source, game);
-                            }
-                        } else {
-                            controller.setCastSourceIdWithAlternateMana(objectId,
-                                    null,
-                                    card.getSpellAbility().getCosts());
-                            return true;
-                        }
-                    }
-                }
-            }
-        }
-        return false;
-
-    }
-}
-
 class IntetTheDreamerLookEffect extends AsThoughEffectImpl {
 
     public IntetTheDreamerLookEffect() {
diff --git a/Mage.Sets/src/mage/cards/k/KheruSpellsnatcher.java b/Mage.Sets/src/mage/cards/k/KheruSpellsnatcher.java
index f6779f259a..da5b92c4fc 100644
--- a/Mage.Sets/src/mage/cards/k/KheruSpellsnatcher.java
+++ b/Mage.Sets/src/mage/cards/k/KheruSpellsnatcher.java
@@ -5,8 +5,6 @@ import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.common.TurnedFaceUpSourceTriggeredAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
-import mage.abilities.effects.AsThoughEffectImpl;
-import mage.abilities.effects.ContinuousEffect;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.keyword.MorphAbility;
 import mage.cards.Card;
@@ -14,14 +12,11 @@ import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.*;
 import mage.game.Game;
-import mage.game.stack.Spell;
 import mage.game.stack.StackObject;
-import mage.players.Player;
 import mage.target.TargetSpell;
-import mage.target.targetpointer.FixedTarget;
-import mage.util.CardUtil;
 
 import java.util.UUID;
+import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect;
 
 /**
  * @author emerald000
@@ -73,68 +68,14 @@ class KheruSpellsnatcherEffect extends OneShotEffect {
 
     @Override
     public boolean apply(Game game, Ability source) {
-        UUID objectId = targetPointer.getFirst(game, source);
-        UUID sourceId = source.getSourceId();
-
-        StackObject stackObject = game.getStack().getStackObject(objectId);
-        if (stackObject != null
+        MageObject sourceObject = source.getSourceObject(game);
+        StackObject stackObject = game.getStack().getStackObject(targetPointer.getFirst(game, source));
+        if (stackObject != null && sourceObject != null
                 && game.getStack().counter(targetPointer.getFirst(game, source), source.getSourceId(), game, Zone.EXILED, false, ZoneDetail.NONE)) {
             if (!stackObject.isCopy()) {
                 MageObject card = game.getObject(stackObject.getSourceId());
-                if (card instanceof Card) {
-                    UUID exileId = CardUtil.getCardExileZoneId(game, sourceId);
-                    ((Card) card).moveToExile(exileId, "Kheru Spellsnatcher - cast without mana cost", sourceId, game);
-                    ContinuousEffect effect = new KheruSpellsnatcherCastFromExileEffect();
-                    effect.setTargetPointer(new FixedTarget(card.getId()));
-                    game.addEffect(effect, source);
-                }
-            }
-            return true;
-        }
-        return false;
-    }
-}
-
-class KheruSpellsnatcherCastFromExileEffect extends AsThoughEffectImpl {
-
-    KheruSpellsnatcherCastFromExileEffect() {
-        super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit);
-        staticText = "You may cast that card without paying its mana cost as long as it remains exiled";
-    }
-
-    KheruSpellsnatcherCastFromExileEffect(final KheruSpellsnatcherCastFromExileEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        return true;
-    }
-
-    @Override
-    public KheruSpellsnatcherCastFromExileEffect copy() {
-        return new KheruSpellsnatcherCastFromExileEffect(this);
-    }
-
-    @Override
-    public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) {
-        if (affectedControllerId.equals(source.getControllerId())) {
-            if (getTargetPointer().getFirst(game, source) == null) {
-                this.discard();
-                return false;
-            }
-            if (sourceId.equals(getTargetPointer().getFirst(game, source))) {
-                Card card = game.getCard(sourceId);
-                if (card != null) {
-                    if (game.getState().getZone(sourceId) == Zone.EXILED) {
-                        Player player = game.getPlayer(affectedControllerId);
-                        if(player != null) {
-                            player.setCastSourceIdWithAlternateMana(sourceId, null, card.getSpellAbility().getCosts());
-                            return true;
-                        }
-                    } else {
-                        this.discard();
-                    }
+                if (card instanceof Card) {                    
+                    return PlayFromNotOwnHandZoneTargetEffect.exileAndPlayFromExile(game, source, (Card)card, TargetController.YOU, Duration.Custom, true);
                 }
             }
         }
diff --git a/Mage.Sets/src/mage/cards/m/MagusOfTheMind.java b/Mage.Sets/src/mage/cards/m/MagusOfTheMind.java
index 26a6b6e3e2..c3aa993fd2 100644
--- a/Mage.Sets/src/mage/cards/m/MagusOfTheMind.java
+++ b/Mage.Sets/src/mage/cards/m/MagusOfTheMind.java
@@ -1,4 +1,3 @@
-
 package mage.cards.m;
 
 import java.util.Set;
@@ -10,21 +9,19 @@ import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.common.SacrificeSourceCost;
 import mage.abilities.costs.common.TapSourceCost;
 import mage.abilities.costs.mana.ManaCostsImpl;
-import mage.abilities.effects.AsThoughEffectImpl;
-import mage.abilities.effects.ContinuousEffect;
 import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect;
 import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.AsThoughEffectType;
 import mage.constants.CardType;
 import mage.constants.SubType;
 import mage.constants.Duration;
 import mage.constants.Outcome;
+import mage.constants.TargetController;
 import mage.constants.Zone;
 import mage.game.Game;
 import mage.players.Player;
-import mage.target.targetpointer.FixedTarget;
 import mage.watchers.common.CastSpellLastTurnWatcher;
 
 /**
@@ -79,7 +76,7 @@ class MagusOfTheMindEffect extends OneShotEffect {
         Player controller = game.getPlayer(source.getControllerId());
         MageObject sourceObject = source.getSourceObject(game);
         CastSpellLastTurnWatcher watcher = game.getState().getWatcher(CastSpellLastTurnWatcher.class);
-        if(watcher == null){
+        if (watcher == null) {
             return false;
         }
         int stormCount = watcher.getAmountOfSpellsAllPlayersCastOnCurrentTurn() + 1;
@@ -87,60 +84,10 @@ class MagusOfTheMindEffect extends OneShotEffect {
             controller.shuffleLibrary(source, game);
             if (controller.getLibrary().hasCards()) {
                 Set<Card> cards = controller.getLibrary().getTopCards(game, stormCount);
-                if (cards != null) {
-                    for (Card card : cards) {
-                        if (card != null) {
-                            controller.moveCardToExileWithInfo(card, source.getSourceId(), sourceObject.getIdName(), source.getSourceId(), game, Zone.LIBRARY, true);
-                            ContinuousEffect effect = new MagusOfTheMindCastFromExileEffect();
-                            effect.setTargetPointer(new FixedTarget(card.getId()));
-                            game.addEffect(effect, source);
-                        }
-                    }
-                }
+                return PlayFromNotOwnHandZoneTargetEffect.exileAndPlayFromExile(game, source, cards, TargetController.YOU, Duration.EndOfTurn, true);
             }
             return true;
         }
         return false;
     }
 }
-
-class MagusOfTheMindCastFromExileEffect extends AsThoughEffectImpl {
-
-    MagusOfTheMindCastFromExileEffect() {
-        super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit);
-        staticText = "you may play that card without paying its mana cost";
-    }
-
-    MagusOfTheMindCastFromExileEffect(final MagusOfTheMindCastFromExileEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        return true;
-    }
-
-    @Override
-    public MagusOfTheMindCastFromExileEffect copy() {
-        return new MagusOfTheMindCastFromExileEffect(this);
-    }
-
-    @Override
-    public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
-        if (objectId != null && objectId.equals(getTargetPointer().getFirst(game, source))) {
-            if (affectedControllerId.equals(source.getControllerId())) {
-                Card card = game.getCard(objectId);
-                if (card != null && game.getState().getZone(objectId) == Zone.EXILED) {
-                    if (!card.isLand() && card.getSpellAbility().getCosts() != null) {
-                        Player player = game.getPlayer(affectedControllerId);
-                        if (player != null) {
-                            player.setCastSourceIdWithAlternateMana(objectId, null, card.getSpellAbility().getCosts());
-                        }
-                    }
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/m/MindsDesire.java b/Mage.Sets/src/mage/cards/m/MindsDesire.java
index eea1c4c7fa..d5cf67afd6 100644
--- a/Mage.Sets/src/mage/cards/m/MindsDesire.java
+++ b/Mage.Sets/src/mage/cards/m/MindsDesire.java
@@ -1,23 +1,19 @@
 package mage.cards.m;
 
 import mage.abilities.Ability;
-import mage.abilities.effects.AsThoughEffectImpl;
-import mage.abilities.effects.ContinuousEffect;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.keyword.StormAbility;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.AsThoughEffectType;
 import mage.constants.CardType;
 import mage.constants.Duration;
 import mage.constants.Outcome;
 import mage.game.Game;
 import mage.players.Player;
-import mage.target.targetpointer.FixedTargets;
-import mage.util.CardUtil;
 
 import java.util.UUID;
+import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect;
+import mage.constants.TargetController;
 
 /**
  * @author emerald000
@@ -65,66 +61,9 @@ class MindsDesireEffect extends OneShotEffect {
         Player controller = game.getPlayer(source.getControllerId());
         if (controller != null) {
             controller.shuffleLibrary(source, game);
-            Card card = controller.getLibrary().getFromTop(game);
-            if (card != null) {
-                UUID exileId = CardUtil.getExileZoneId(controller.getId().toString() + "-" + game.getState().getTurnNum() + "-" + MindsDesire.class.toString(), game);
-                String exileName = "Mind's Desire free cast on " + game.getState().getTurnNum() + " turn for " + controller.getName();
-                game.getExile().createZone(exileId, exileName).setCleanupOnEndTurn(true);
-                if (controller.moveCardsToExile(card, source, game, true, exileId, exileName)) {
-                    ContinuousEffect effect = new MindsDesireCastFromExileEffect();
-                    effect.setTargetPointer(new FixedTargets(game.getExile().getExileZone(exileId).getCards(game), game));
-                    game.addEffect(effect, source);
-                }
-            }
-            return true;
+            return PlayFromNotOwnHandZoneTargetEffect.exileAndPlayFromExile(game, source, controller.getLibrary().getFromTop(game),
+                    TargetController.YOU, Duration.EndOfTurn, true);            
         }
         return false;
     }
-}
-
-class MindsDesireCastFromExileEffect extends AsThoughEffectImpl {
-
-    MindsDesireCastFromExileEffect() {
-        super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit);
-        staticText = "you may play that card without paying its mana cost";
-    }
-
-    MindsDesireCastFromExileEffect(final MindsDesireCastFromExileEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        return true;
-    }
-
-    @Override
-    public MindsDesireCastFromExileEffect copy() {
-        return new MindsDesireCastFromExileEffect(this);
-    }
-
-    @Override
-    public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
-        return applies(objectId, null, source, game, affectedControllerId);
-    }
-
-    @Override
-    public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) {
-        Card cardToCheck = game.getCard(objectId);
-        objectId = CardUtil.getMainCardId(game, objectId); // for split cards
-
-        if (!isAbilityAppliedForAlternateCast(cardToCheck, affectedAbility, playerId, source)) {
-            return false;
-        }
-
-        Player controller = game.getPlayer(cardToCheck.getOwnerId());
-        if (controller != null
-                && getTargetPointer().getTargets(game, source).contains(objectId)) {
-            controller.setCastSourceIdWithAlternateMana(affectedAbility.getSourceId(), null, affectedAbility.getCosts());
-            return true;
-        }
-
-
-        return false;
-    }
-}
+}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/n/NarsetEnlightenedMaster.java b/Mage.Sets/src/mage/cards/n/NarsetEnlightenedMaster.java
index 8301068ad2..195dea2f7b 100644
--- a/Mage.Sets/src/mage/cards/n/NarsetEnlightenedMaster.java
+++ b/Mage.Sets/src/mage/cards/n/NarsetEnlightenedMaster.java
@@ -116,13 +116,9 @@ class NarsetEnlightenedMasterCastFromExileEffect extends AsThoughEffectImpl {
     public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
         if (objectId.equals(getTargetPointer().getFirst(game, source))
                 && affectedControllerId.equals(source.getControllerId())) {
-            Card card = game.getCard(objectId);
-            if (card != null) {
-                Player player = game.getPlayer(affectedControllerId);
-                if (player != null) {
-                    player.setCastSourceIdWithAlternateMana(objectId, null, card.getSpellAbility().getCosts());
-                    return true;
-                }
+            Player player = game.getPlayer(affectedControllerId);
+            if (player != null) {
+                return allowCardToPlayWithoutMana(objectId, source, affectedControllerId, game);
             }
         }
         return false;
diff --git a/Mage.Sets/src/mage/cards/n/NicolBolasGodPharaoh.java b/Mage.Sets/src/mage/cards/n/NicolBolasGodPharaoh.java
index 476f219ad1..9fdcb6b28f 100644
--- a/Mage.Sets/src/mage/cards/n/NicolBolasGodPharaoh.java
+++ b/Mage.Sets/src/mage/cards/n/NicolBolasGodPharaoh.java
@@ -21,11 +21,12 @@ import mage.target.Target;
 import mage.target.common.TargetAnyTarget;
 import mage.target.common.TargetCardInHand;
 import mage.target.common.TargetOpponent;
-import mage.target.targetpointer.FixedTarget;
 
 import java.util.HashMap;
 import java.util.Map;
 import java.util.UUID;
+import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect;
+import mage.target.targetpointer.FixedTarget;
 
 /**
  * @author Will
@@ -168,9 +169,8 @@ class NicolBolasGodPharaohPlusTwoEffect extends OneShotEffect {
             if (card.isLand()) {
                 continue;
             }
-            ContinuousEffect effect = new NicolBolasGodPharaohFromExileEffect();
-            effect.setTargetPointer(new FixedTarget(card.getId(),
-                    game.getState().getZoneChangeCounter(card.getId())));
+            ContinuousEffect effect =  new PlayFromNotOwnHandZoneTargetEffect(Zone.EXILED, TargetController.YOU, Duration.EndOfTurn, true);
+            effect.setTargetPointer(new FixedTarget(card, game));
             game.addEffect(effect, source);
             break;
         } while (library.hasCards()
@@ -178,48 +178,3 @@ class NicolBolasGodPharaohPlusTwoEffect extends OneShotEffect {
         return true;
     }
 }
-
-class NicolBolasGodPharaohFromExileEffect extends AsThoughEffectImpl {
-
-    NicolBolasGodPharaohFromExileEffect() {
-        super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit);
-        staticText = "You may cast card from exile";
-    }
-
-    private NicolBolasGodPharaohFromExileEffect(final NicolBolasGodPharaohFromExileEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        return true;
-    }
-
-    @Override
-    public NicolBolasGodPharaohFromExileEffect copy() {
-        return new NicolBolasGodPharaohFromExileEffect(this);
-    }
-
-    @Override
-    public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) {
-        if (sourceId == null
-                || !sourceId.equals(getTargetPointer().getFirst(game, source))
-                || !affectedControllerId.equals(source.getControllerId())) {
-            return false;
-        }
-        Card card = game.getCard(sourceId);
-        if (card == null
-                || game.getState().getZone(sourceId) != Zone.EXILED) {
-            return false;
-        }
-        Player controller = game.getPlayer(affectedControllerId);
-        if (controller == null) {
-            return false;
-        }
-        controller.setCastSourceIdWithAlternateMana(
-                sourceId,
-                null,
-                card.getSpellAbility().getCosts());
-        return true;
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/o/OraclesVault.java b/Mage.Sets/src/mage/cards/o/OraclesVault.java
index 57ed7bfd9d..e69ae9ac6e 100644
--- a/Mage.Sets/src/mage/cards/o/OraclesVault.java
+++ b/Mage.Sets/src/mage/cards/o/OraclesVault.java
@@ -1,4 +1,3 @@
-
 package mage.cards.o;
 
 import java.util.UUID;
@@ -11,8 +10,10 @@ import mage.abilities.costs.common.TapSourceCost;
 import mage.abilities.costs.mana.GenericManaCost;
 import mage.abilities.decorator.ConditionalActivatedAbility;
 import mage.abilities.effects.AsThoughEffectImpl;
+import mage.abilities.effects.ContinuousEffect;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect;
 import mage.abilities.effects.common.counter.AddCountersSourceEffect;
 import mage.cards.Card;
 import mage.cards.CardImpl;
@@ -21,11 +22,13 @@ import mage.constants.AsThoughEffectType;
 import mage.constants.CardType;
 import mage.constants.Duration;
 import mage.constants.Outcome;
+import mage.constants.TargetController;
 import mage.constants.Zone;
 import mage.counters.CounterType;
 import mage.game.Game;
 import mage.players.Library;
 import mage.players.Player;
+import mage.target.targetpointer.FixedTarget;
 import mage.util.CardUtil;
 
 /**
@@ -84,13 +87,8 @@ class OraclesVaultEffect extends OneShotEffect {
     public boolean apply(Game game, Ability source) {
         Player controller = game.getPlayer(source.getControllerId());
         if (controller != null) {
-            Card card = controller.getLibrary().getFromTop(game);
-            if (card != null) {
-                controller.moveCardsToExile(card, source, game, true, source.getSourceId(),
-                        CardUtil.createObjectRealtedWindowTitle(source, game, "<this card may be played the turn it was exiled>"));
-                game.addEffect(new OraclesVaultPlayEffect(new MageObjectReference(card, game)), source);
-            }
-            return true;
+            return PlayFromNotOwnHandZoneTargetEffect.exileAndPlayFromExile(game, source, controller.getLibrary().getFromTop(game),
+                    TargetController.YOU, Duration.EndOfTurn, false);
         }
         return false;
     }
@@ -114,95 +112,9 @@ class OraclesVaultFreeEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player controller = game.getPlayer(source.getControllerId());
-        MageObject sourceObject = source.getSourceObject(game);
-        if (controller != null && sourceObject != null && controller.getLibrary().hasCards()) {
-            Library library = controller.getLibrary();
-            Card card = library.getFromTop(game);
-            if (card != null) {
-                controller.moveCardsToExile(card, source, game, true, source.getSourceId(),
-                        CardUtil.createObjectRealtedWindowTitle(source, game, " <this card may be played the turn it was exiled>"));
-                game.addEffect(new OraclesVaultPlayForFreeEffect(new MageObjectReference(card, game)), source);
-            }
-            return true;
-        }
-        return false;
-    }
-}
-
-class OraclesVaultPlayEffect extends AsThoughEffectImpl {
-
-    private final MageObjectReference objectReference;
-
-    public OraclesVaultPlayEffect(MageObjectReference objectReference) {
-        super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit);
-        this.objectReference = objectReference;
-        staticText = "Until end of turn, you may play that card";
-    }
-
-    public OraclesVaultPlayEffect(final OraclesVaultPlayEffect effect) {
-        super(effect);
-        this.objectReference = effect.objectReference;
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        return true;
-    }
-
-    @Override
-    public OraclesVaultPlayEffect copy() {
-        return new OraclesVaultPlayEffect(this);
-    }
-
-    @Override
-    public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
-        if (objectReference.refersTo(objectId, game) && affectedControllerId.equals(source.getControllerId())) {
-            Player controller = game.getPlayer(source.getControllerId());
-            if (controller != null) {
-                return true;
-            } else {
-                discard();
-            }
-        }
-        return false;
-    }
-}
-
-class OraclesVaultPlayForFreeEffect extends AsThoughEffectImpl {
-
-    private final MageObjectReference objectReference;
-
-    public OraclesVaultPlayForFreeEffect(MageObjectReference objectReference) {
-        super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit);
-        this.objectReference = objectReference;
-        staticText = "Until end of turn, you may play that card without paying its mana cost";
-    }
-
-    public OraclesVaultPlayForFreeEffect(final OraclesVaultPlayForFreeEffect effect) {
-        super(effect);
-        this.objectReference = effect.objectReference;
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        return true;
-    }
-
-    @Override
-    public OraclesVaultPlayForFreeEffect copy() {
-        return new OraclesVaultPlayForFreeEffect(this);
-    }
-
-    @Override
-    public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
-        if (objectReference.refersTo(objectId, game) && affectedControllerId.equals(source.getControllerId())) {
-            Player controller = game.getPlayer(source.getControllerId());
-            if (controller != null) {
-                controller.setCastSourceIdWithAlternateMana(objectId, null, null);
-                return true;
-            } else {
-                discard();
-            }
+        if (controller != null) {
+            return PlayFromNotOwnHandZoneTargetEffect.exileAndPlayFromExile(game, source, controller.getLibrary().getFromTop(game),
+                    TargetController.YOU, Duration.EndOfTurn, true);            
         }
         return false;
     }
diff --git a/Mage.Sets/src/mage/cards/p/PlaneswalkersMischief.java b/Mage.Sets/src/mage/cards/p/PlaneswalkersMischief.java
index 1662e16197..c546bfda19 100644
--- a/Mage.Sets/src/mage/cards/p/PlaneswalkersMischief.java
+++ b/Mage.Sets/src/mage/cards/p/PlaneswalkersMischief.java
@@ -22,6 +22,7 @@ import mage.watchers.common.SpellsCastWatcher;
 
 import java.util.List;
 import java.util.UUID;
+import mage.MageObject;
 
 /**
  * @author jeffwadsworth
@@ -67,16 +68,15 @@ class PlaneswalkersMischiefEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player opponent = game.getPlayer(source.getFirstTarget());
-        if (opponent != null
-                && opponent.getHand().size() > 0) {
+        if (opponent != null && opponent.getHand().size() > 0) {
             Card revealedCard = opponent.getHand().getRandom(game);
             if (revealedCard == null) {
                 return false;
             }
             Cards cards = new CardsImpl(revealedCard);
-            opponent.revealCards("Planeswalker's Mischief Reveal", cards, game);
+            opponent.revealCards(source, cards, game);
             if (revealedCard.isInstant()
-                    || revealedCard.isSorcery()) {
+                    || revealedCard.isSorcery()) {                
                 opponent.moveCardToExileWithInfo(revealedCard, source.getSourceId(), "Planeswalker's Mischief", source.getSourceId(), game, Zone.HAND, true);
                 AsThoughEffect effect = new PlaneswalkersMischiefCastFromExileEffect();
                 effect.setTargetPointer(new FixedTarget(revealedCard.getId()));
@@ -123,8 +123,7 @@ class PlaneswalkersMischiefCastFromExileEffect extends AsThoughEffectImpl {
             Card card = game.getCard(objectId);
             if (player != null
                     && card != null) {
-                player.setCastSourceIdWithAlternateMana(objectId, null, card.getSpellAbility().getCosts());
-                return true;
+                return allowCardToPlayWithoutMana(objectId, source, affectedControllerId, game);
             }
         }
         return false;
diff --git a/Mage.Sets/src/mage/cards/r/ReleaseToTheWind.java b/Mage.Sets/src/mage/cards/r/ReleaseToTheWind.java
index c8a9ebdf8d..6ee617e186 100644
--- a/Mage.Sets/src/mage/cards/r/ReleaseToTheWind.java
+++ b/Mage.Sets/src/mage/cards/r/ReleaseToTheWind.java
@@ -4,8 +4,8 @@ package mage.cards.r;
 import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.effects.AsThoughEffectImpl;
-import mage.abilities.effects.ContinuousEffect;
 import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect;
 import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -13,7 +13,7 @@ import mage.constants.AsThoughEffectType;
 import mage.constants.CardType;
 import mage.constants.Duration;
 import mage.constants.Outcome;
-import mage.constants.Zone;
+import mage.constants.TargetController;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
 import mage.players.Player;
@@ -67,58 +67,7 @@ class ReleaseToTheWindEffect extends OneShotEffect {
         if (controller != null) {
             Permanent targetPermanent = game.getPermanent(getTargetPointer().getFirst(game, source));
             if (targetPermanent != null) {
-                if (controller.moveCards(targetPermanent, Zone.EXILED, source, game)) {
-                    Card card = game.getCard(targetPermanent.getId());
-                    if (card != null) {
-                        ContinuousEffect effect = new ReleaseToTheWindEffectCastFromExileEffect();
-                        effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game)));
-                        game.addEffect(effect, source);
-                    }
-                }
-            }
-            return true;
-        }
-        return false;
-    }
-}
-
-class ReleaseToTheWindEffectCastFromExileEffect extends AsThoughEffectImpl {
-
-    public ReleaseToTheWindEffectCastFromExileEffect() {
-        super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit);
-        staticText = "For as long as that card remains exiled, its owner may cast it without paying its mana cost";
-    }
-
-    public ReleaseToTheWindEffectCastFromExileEffect(final ReleaseToTheWindEffectCastFromExileEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        return true;
-    }
-
-    @Override
-    public ReleaseToTheWindEffectCastFromExileEffect copy() {
-        return new ReleaseToTheWindEffectCastFromExileEffect(this);
-    }
-
-    @Override
-    public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
-        UUID ownerId = game.getOwnerId(objectId);
-        if (objectId.equals(getTargetPointer().getFirst(game, source))) {
-            if (affectedControllerId.equals(ownerId)) {
-                Card card = game.getCard(objectId);
-                Player player = game.getPlayer(ownerId);
-                if (player != null && card != null) {
-                    player.setCastSourceIdWithAlternateMana(objectId, null, card.getSpellAbility().getCosts());
-                    return true;
-                }
-            }
-        } else {
-            if (((FixedTarget) getTargetPointer()).getTarget().equals(objectId)) {
-                // object has moved zone so effect can be discarted
-                this.discard();
+                return PlayFromNotOwnHandZoneTargetEffect.exileAndPlayFromExile(game, source, targetPermanent, TargetController.OWNER, Duration.Custom, true);
             }
         }
         return false;
diff --git a/Mage.Sets/src/mage/cards/s/SinsOfThePast.java b/Mage.Sets/src/mage/cards/s/SinsOfThePast.java
index 21aa8edb35..05441948f1 100644
--- a/Mage.Sets/src/mage/cards/s/SinsOfThePast.java
+++ b/Mage.Sets/src/mage/cards/s/SinsOfThePast.java
@@ -97,11 +97,12 @@ class SinsOfThePastCastFromGraveyardEffect extends AsThoughEffectImpl {
     }
 
     @Override
-    public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) {
-        if (sourceId.equals(this.getTargetPointer().getFirst(game, source)) && affectedControllerId.equals(source.getControllerId())) {
+    public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
+        if (objectId.equals(this.getTargetPointer().getFirst(game, source)) && affectedControllerId.equals(source.getControllerId())) {
             Player player = game.getPlayer(affectedControllerId);
-            if(player != null) {
-                player.setCastSourceIdWithAlternateMana(sourceId, null, null);
+            Card cardToCast = game.getCard(objectId);
+            if(player != null && cardToCast != null) {
+                player.setCastSourceIdWithAlternateMana(objectId, null, cardToCast.getSpellAbility().getCosts());
                 return true;
             }
         }
diff --git a/Mage.Sets/src/mage/cards/s/Spelljack.java b/Mage.Sets/src/mage/cards/s/Spelljack.java
index f966eb2091..d15fcb8045 100644
--- a/Mage.Sets/src/mage/cards/s/Spelljack.java
+++ b/Mage.Sets/src/mage/cards/s/Spelljack.java
@@ -116,7 +116,7 @@ class SpelljackCastFromExileEffect extends AsThoughEffectImpl {
                     if (game.getState().getZone(sourceId) == Zone.EXILED) {
                         Player player = game.getPlayer(affectedControllerId);
                         if(player != null) {
-                            player.setCastSourceIdWithAlternateMana(sourceId, null, null);
+                            player.setCastSourceIdWithAlternateMana(sourceId, null, card.getSpellAbility().getCosts());
                         }
                         return true;
                     } else {
diff --git a/Mage.Sets/src/mage/cards/s/StolenGoods.java b/Mage.Sets/src/mage/cards/s/StolenGoods.java
index c6588a5328..0cecd5c087 100644
--- a/Mage.Sets/src/mage/cards/s/StolenGoods.java
+++ b/Mage.Sets/src/mage/cards/s/StolenGoods.java
@@ -1,4 +1,3 @@
-
 package mage.cards.s;
 
 import java.util.UUID;
@@ -74,7 +73,7 @@ class StolenGoodsEffect extends OneShotEffect {
 
             if (card != null) {
                 ContinuousEffect effect = new StolenGoodsCastFromExileEffect();
-                effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game)));
+                effect.setTargetPointer(new FixedTarget(card, game));
                 game.addEffect(effect, source);
             }
             return true;
@@ -105,17 +104,11 @@ class StolenGoodsCastFromExileEffect extends AsThoughEffectImpl {
     }
 
     @Override
-    public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) {
-        if (sourceId != null && sourceId.equals(getTargetPointer().getFirst(game, source))
+    public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
+        if (objectId != null && objectId.equals(getTargetPointer().getFirst(game, source))
                 && affectedControllerId.equals(source.getControllerId())) {
-            Card card = game.getCard(sourceId);
-            if (card != null && game.getState().getZone(sourceId) == Zone.EXILED) {
-                Player player = game.getPlayer(affectedControllerId);
-                if (player != null) {
-                    player.setCastSourceIdWithAlternateMana(sourceId, null, card.getSpellAbility().getCosts());
-                    return true;
-                }
-            }
+            allowCardToPlayWithoutMana(objectId, source, affectedControllerId, game);
+            return true;
         }
         return false;
     }
diff --git a/Mage.Sets/src/mage/cards/t/TemporalAperture.java b/Mage.Sets/src/mage/cards/t/TemporalAperture.java
index 1bd65e5d27..9f6e7ae786 100644
--- a/Mage.Sets/src/mage/cards/t/TemporalAperture.java
+++ b/Mage.Sets/src/mage/cards/t/TemporalAperture.java
@@ -1,4 +1,3 @@
-
 package mage.cards.t;
 
 import java.util.UUID;
@@ -121,11 +120,8 @@ class TemporalApertureTopCardCastEffect extends AsThoughEffectImpl {
                 if (controller != null
                         && game.getState().getZone(objectId) == Zone.LIBRARY) {
                     if (controller.getLibrary().getFromTop(game).equals(card)) {
-                        if (objectCard == card
-                                && objectCard.getSpellAbility() != null
-                                && objectCard.getSpellAbility().spellCanBeActivatedRegularlyNow(controller.getId(), game)
-                                || objectCard.isLand()) {
-                            controller.setCastSourceIdWithAlternateMana(objectId, null, null);
+                        if (objectCard == card && objectCard.getSpellAbility() != null) { // only if castable
+                            allowCardToPlayWithoutMana(objectId, source, affectedControllerId, game);
                             return true;
                         }
                     }
diff --git a/Mage.Sets/src/mage/cards/u/UrzaLordHighArtificer.java b/Mage.Sets/src/mage/cards/u/UrzaLordHighArtificer.java
index be880a8f95..616e8dbb6e 100644
--- a/Mage.Sets/src/mage/cards/u/UrzaLordHighArtificer.java
+++ b/Mage.Sets/src/mage/cards/u/UrzaLordHighArtificer.java
@@ -7,7 +7,7 @@ import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.common.TapTargetCost;
 import mage.abilities.costs.mana.GenericManaCost;
-import mage.abilities.effects.AsThoughEffectImpl;
+
 import mage.abilities.effects.ContinuousEffect;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.CreateTokenEffect;
@@ -28,6 +28,8 @@ import mage.target.targetpointer.FixedTargets;
 import mage.util.CardUtil;
 
 import java.util.UUID;
+import mage.MageObject;
+import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect;
 
 /**
  * @author TheElk801
@@ -92,68 +94,13 @@ class UrzaLordHighArtificerEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player controller = game.getPlayer(source.getControllerId());
-        if (controller == null) {
+        MageObject sourceObject = source.getSourceObject(game);
+        if (controller == null || sourceObject == null) {
             return false;
         }
         controller.shuffleLibrary(source, game);
         Card card = controller.getLibrary().getFromTop(game);
-        if (card == null) {
-            return false;
-        }
-        UUID exileId = CardUtil.getExileZoneId(
-                controller.getId().toString()
-                        + "-" + game.getState().getTurnNum()
-                        + "-" + UrzaLordHighArtificer.class.toString(), game
-        );
-        String exileName = "Urza, Lord High Artificer free cast on "
-                + game.getState().getTurnNum() + " turn for " + controller.getName();
-        game.getExile().createZone(exileId, exileName).setCleanupOnEndTurn(true);
-        if (!controller.moveCardsToExile(card, source, game, true, exileId, exileName)) {
-            return false;
-        }
-        ContinuousEffect effect = new UrzaLordHighArtificerFromExileEffect();
-        effect.setTargetPointer(new FixedTargets(game.getExile().getExileZone(exileId).getCards(game), game));
-        game.addEffect(effect, source);
-        return true;
-    }
-}
-
-class UrzaLordHighArtificerFromExileEffect extends AsThoughEffectImpl {
-
-    UrzaLordHighArtificerFromExileEffect() {
-        super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit);
-        staticText = "you may play that card without paying its mana cost";
-    }
-
-    private UrzaLordHighArtificerFromExileEffect(final UrzaLordHighArtificerFromExileEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        return true;
-    }
-
-    @Override
-    public UrzaLordHighArtificerFromExileEffect copy() {
-        return new UrzaLordHighArtificerFromExileEffect(this);
-    }
-
-    @Override
-    public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
-        if (!affectedControllerId.equals(source.getControllerId())
-                || !getTargetPointer().getTargets(game, source).contains(objectId)) {
-            return false;
-        }
-        Card card = game.getCard(objectId);
-        if (card == null || card.isLand() || card.getSpellAbility().getCosts() == null) {
-            return true;
-        }
-        Player player = game.getPlayer(affectedControllerId);
-        if (player == null) {
-            return false;
-        }
-        player.setCastSourceIdWithAlternateMana(objectId, null, card.getSpellAbility().getCosts());
-        return true;
+        return PlayFromNotOwnHandZoneTargetEffect.exileAndPlayFromExile(game, source, card, 
+                TargetController.YOU, Duration.EndOfTurn, true);          
     }
 }
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/asthough/PlayFromNonHandZoneTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/asthough/PlayFromNonHandZoneTest.java
index 763b589500..1c0480fc1b 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/asthough/PlayFromNonHandZoneTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/asthough/PlayFromNonHandZoneTest.java
@@ -267,4 +267,166 @@ public class PlayFromNonHandZoneTest extends CardTestPlayerBase {
         assertActivePlayer(playerA);
     }
 
+    /**
+     * #6580
+     * Fallen Shinobi - In the second log, when Tormenting Voice is cast first,
+     * the discard was required. In the first log, when it was cast after
+     * Angelic Purge the discard was not required.
+     */
+    
+    @Test
+    public void castFromExileButWithAdditionalCostTest() {
+        // Ninjutsu {2}{U}{B}
+        // Whenever Fallen Shinobi deals combat damage to a player, that player exiles the top two cards 
+        // of their library. Until end of turn, you may play those cards without paying their mana cost.
+        addCard(Zone.BATTLEFIELD, playerB, "Fallen Shinobi", 1); // Creature 5/4
+        addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion");
+        addCard(Zone.HAND, playerB, "Pillarfield Ox");
+        
+        addCard(Zone.LIBRARY, playerB, "Pillarfield Ox"); // Card to draw on turn 2
+        // As an additional cost to cast Tormenting Voice, discard a card.
+        // Draw two cards.        
+        addCard(Zone.LIBRARY, playerA, "Tormenting Voice"); // Sorcery {1}{R}        
+        // As an additional cost to cast this spell, sacrifice a creature.
+        // Flying, Trample        
+        addCard(Zone.LIBRARY, playerA, "Demon of Catastrophes"); // Creature {2}{B}{B}  6/6
+  
+        skipInitShuffling();
+
+        attack(2, playerB, "Fallen Shinobi");
+        
+        castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Tormenting Voice");
+        setChoice(playerB, "Pillarfield Ox"); // Discord for Tormenting Voice
+        
+        castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Demon of Catastrophes");
+        setChoice(playerB, "Silvercoat Lion"); // Sacrifice for Demon
+
+        setStopAt(2, PhaseStep.END_TURN);
+
+        setStrictChooseMode(true);
+        execute();
+        assertAllCommandsUsed();
+
+        assertLife(playerA, 15);
+        assertPermanentCount(playerB, "Fallen Shinobi", 1);
+        
+        assertGraveyardCount(playerA,  "Tormenting Voice", 1);
+        assertGraveyardCount(playerB,  "Pillarfield Ox", 1);  // Discarded for Tormenting Voice
+        
+        
+        assertPermanentCount(playerB, "Demon of Catastrophes", 1);
+        assertGraveyardCount(playerB,  "Silvercoat Lion", 1);  // sacrificed for Demon
+        
+        assertHandCount(playerB, "Pillarfield Ox", 1);
+        assertHandCount(playerB, 3); // 2 from Tormenting Voice + 1 from Turn 2
+        assertExileCount(playerA, 0); // Both exiled cards are cast
+    }    
+    
+    
+    @Test
+    public void castFromExileButWithAdditionalCost2Test() {
+        // Ninjutsu {2}{U}{B}
+        // Whenever Fallen Shinobi deals combat damage to a player, that player exiles the top two cards 
+        // of their library. Until end of turn, you may play those cards without paying their mana cost.
+        addCard(Zone.BATTLEFIELD, playerB, "Fallen Shinobi", 1); // Creature 5/4
+        addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion");
+        addCard(Zone.HAND, playerB, "Pillarfield Ox");
+        
+        addCard(Zone.BATTLEFIELD, playerA, "Amulet of Kroog"); // Just to exile for Angelic Purge
+
+        // As an additional cost to cast Tormenting Voice, discard a card.
+        // Draw two cards.        
+        addCard(Zone.LIBRARY, playerA, "Tormenting Voice"); // Sorcery {1}{R}        
+
+        // As an additional cost to cast Angelic Purge, sacrifice a permanent.
+        // Exile target artifact, creature, or enchantment.      
+        addCard(Zone.LIBRARY, playerA, "Angelic Purge"); // Sorcery {2}{W}
+  
+        skipInitShuffling();
+
+        attack(2, playerB, "Fallen Shinobi");
+        
+        castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Angelic Purge");
+        setChoice(playerB, "Silvercoat Lion"); // Sacrifice for Purge
+        addTarget(playerB, "Amulet of Kroog"); // Exile with Purge
+        
+        castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Tormenting Voice");
+        setChoice(playerB, "Pillarfield Ox"); // Discord for Tormenting Voice
+        
+        setStopAt(2, PhaseStep.END_TURN);
+
+        setStrictChooseMode(true);
+        execute();
+        assertAllCommandsUsed();
+
+        assertLife(playerA, 15);
+        assertPermanentCount(playerB, "Fallen Shinobi", 1);
+        
+        assertGraveyardCount(playerA, "Angelic Purge", 1);
+        assertGraveyardCount(playerB,  "Silvercoat Lion", 1);  // sacrificed for Purge
+
+        assertGraveyardCount(playerA,  "Tormenting Voice", 1);
+        assertGraveyardCount(playerB,  "Pillarfield Ox", 1);  // Discarded for Tormenting Voice
+                     
+        assertHandCount(playerB, 3); // 2 from Tormenting Voice + 1 from Turn 2 draw
+        
+        assertExileCount(playerA, 1); // Both exiled cards are cast
+        assertExileCount(playerA, "Amulet of Kroog", 1); // Exiled with Purge
+    }     
+    
+     @Test
+    public void castAdventureWithFallenShinobiTest() {     
+        // Ninjutsu {2}{U}{B}
+        // Whenever Fallen Shinobi deals combat damage to a player, that player exiles the top two cards 
+        // of their library. Until end of turn, you may play those cards without paying their mana cost.
+        addCard(Zone.BATTLEFIELD, playerB, "Fallen Shinobi", 1); // Creature 5/4
+        addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion");
+        
+        addCard(Zone.BATTLEFIELD, playerA, "Amulet of Kroog"); // Just to exile for Angelic Purge
+
+        /* Curious Pair {1}{G}
+         * Creature — Human Peasant
+         * 1/3
+         * ----
+         * Treats to Share {G}
+         * Sorcery — Adventure
+         * Create a Food token.
+         */        
+        addCard(Zone.LIBRARY, playerA, "Curious Pair");      
+
+        // As an additional cost to cast Angelic Purge, sacrifice a permanent.
+        // Exile target artifact, creature, or enchantment.      
+        addCard(Zone.LIBRARY, playerA, "Angelic Purge"); // Sorcery {2}{W}
+  
+        skipInitShuffling();
+
+        attack(2, playerB, "Fallen Shinobi");
+        
+        castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Angelic Purge");
+        setChoice(playerB, "Silvercoat Lion"); // Sacrifice for Purge
+        addTarget(playerB, "Amulet of Kroog"); // Exile with Purge
+        
+        castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Treats to Share");
+        
+        setStopAt(2, PhaseStep.END_TURN);
+
+        setStrictChooseMode(true);
+        execute();
+        assertAllCommandsUsed();
+
+        assertLife(playerA, 15);
+        assertPermanentCount(playerB, "Fallen Shinobi", 1);
+        
+        assertGraveyardCount(playerA, "Angelic Purge", 1);
+        assertGraveyardCount(playerB,  "Silvercoat Lion", 1);  // sacrificed for Purge
+
+        assertPermanentCount(playerB, "Food", 1);
+        assertExileCount(playerA, "Curious Pair", 1);
+                     
+        assertHandCount(playerB, 1); // 1 from Turn 2 draw
+        
+        assertExileCount(playerA, 2); // Both exiled cards are cast 
+        assertExileCount(playerA, "Amulet of Kroog", 1); // Exiled with Purge        
+    }
+    
 }
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithFuseTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithFuseTest.java
index 034305cd91..af231c2753 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithFuseTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithFuseTest.java
@@ -80,12 +80,12 @@ public class CastSplitCardsWithFuseTest extends CardTestPlayerBase {
         addCard(Zone.BATTLEFIELD, playerB, "Absolute Grace"); // Enchantment
         addCard(Zone.BATTLEFIELD, playerB, "Juggernaut"); // Artifact
 
-        showAvaileableAbilities("abils", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
+        // showAvaileableAbilities("abils", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "fused Wear // Tear");
         addTarget(playerA, "Juggernaut");
         addTarget(playerA, "Absolute Grace");
         //playerA.addTarget("Absolute Grace");
-        showBattlefield("after", 1, PhaseStep.BEGIN_COMBAT, playerB);
+        // showBattlefield("after", 1, PhaseStep.BEGIN_COMBAT, playerB);
 
         setStrictChooseMode(true);
         setStopAt(1, PhaseStep.BEGIN_COMBAT);
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/TidehollowScullerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/TidehollowScullerTest.java
index 2fd417ba20..6714b58284 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/TidehollowScullerTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/TidehollowScullerTest.java
@@ -104,7 +104,7 @@ public class TidehollowScullerTest extends CardTestPlayerBase {
         for (int i = 1; i <= 10; i++) {
             try {
                 this.reset();
-                System.out.println("run " + i);
+                // System.out.println("run " + i);
                 test_CastTwoCardFromHandWillBeExiled();
             } catch (Exception e) {
                 //
diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
index a93b43236a..d50d688844 100644
--- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
+++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
@@ -359,7 +359,7 @@ public class TestPlayer implements Player {
         }
     }
 
-    public boolean isAbilityHaveTargetNameOrAlias(Game game, Ability ability, String nameOrAlias) {
+    public boolean hasAbilityTargetNameOrAlias(Game game, Ability ability, String nameOrAlias) {
         // use cases:
         // * Cast cardName with extra
         // * Cast @ref
@@ -379,7 +379,7 @@ public class TestPlayer implements Player {
                 foundObject = true;
                 foundAbility = ability.toString().startsWith(nameOrAlias);
             } else {
-                foundObject = isObjectHaveTargetNameOrAlias(game.getObject(ability.getSourceId()), searchObject);
+                foundObject = hasObjectTargetNameOrAlias(game.getObject(ability.getSourceId()), searchObject);
                 foundAbility = searchObject.startsWith(ALIAS_PREFIX) || ability.toString().startsWith(nameOrAlias);
             }
         } else if (nameOrAlias.startsWith(ALIAS_PREFIX)) {
@@ -388,7 +388,7 @@ public class TestPlayer implements Player {
             Assert.assertTrue("ability alias must contains space", nameOrAlias.contains(" "));
             String searchObject = nameOrAlias.substring(0, nameOrAlias.indexOf(" "));
             String searchAbility = nameOrAlias.substring(nameOrAlias.indexOf(" ") + 1);
-            foundObject = isObjectHaveTargetNameOrAlias(game.getObject(ability.getSourceId()), searchObject);
+            foundObject = hasObjectTargetNameOrAlias(game.getObject(ability.getSourceId()), searchObject);
             foundAbility = ability.toString().startsWith(searchAbility);
         } else {
             // ability text
@@ -399,7 +399,7 @@ public class TestPlayer implements Player {
         return foundObject && foundAbility;
     }
 
-    public boolean isObjectHaveTargetNameOrAlias(MageObject object, String nameOrAlias) {
+    public boolean hasObjectTargetNameOrAlias(MageObject object, String nameOrAlias) {
         if (object == null || nameOrAlias == null) {
             return false;
         }
@@ -505,7 +505,7 @@ public class TestPlayer implements Player {
                         }
 
                         // need by alias or by name
-                        if (!isObjectHaveTargetNameOrAlias(object, targetName)) {
+                        if (!hasObjectTargetNameOrAlias(object, targetName)) {
                             continue;
                         }
 
@@ -578,7 +578,7 @@ public class TestPlayer implements Player {
                         break;
                     }
                     for (ActivatedAbility ability : computerPlayer.getPlayable(game, true)) { // add wrong action log?
-                        if (isAbilityHaveTargetNameOrAlias(game, ability, groups[0])) {
+                        if (hasAbilityTargetNameOrAlias(game, ability, groups[0])) {
                             int bookmark = game.bookmarkState();
                             ActivatedAbility newAbility = ability.copy();
                             if (groups.length > 1 && !groups[1].equals("target=" + NO_TARGET)) {
@@ -606,7 +606,7 @@ public class TestPlayer implements Player {
                     for (MageObject mageObject : manaObjects) {
                         if (mageObject instanceof Permanent) {
                             for (Ability manaAbility : ((Permanent) mageObject).getAbilities(game).getAvailableActivatedManaAbilities(Zone.BATTLEFIELD, game)) {
-                                if (isAbilityHaveTargetNameOrAlias(game, manaAbility, groups[0])) {
+                                if (hasAbilityTargetNameOrAlias(game, manaAbility, groups[0])) {
                                     Ability newManaAbility = manaAbility.copy();
                                     computerPlayer.activateAbility((ActivatedAbility) newManaAbility, game);
                                     actions.remove(action);
@@ -615,7 +615,7 @@ public class TestPlayer implements Player {
                             }
                         } else if (mageObject instanceof Card) {
                             for (Ability manaAbility : ((Card) mageObject).getAbilities(game).getAvailableActivatedManaAbilities(game.getState().getZone(mageObject.getId()), game)) {
-                                if (isAbilityHaveTargetNameOrAlias(game, manaAbility, groups[0])) {
+                                if (hasAbilityTargetNameOrAlias(game, manaAbility, groups[0])) {
                                     Ability newManaAbility = manaAbility.copy();
                                     computerPlayer.activateAbility((ActivatedAbility) newManaAbility, game);
                                     actions.remove(action);
@@ -624,7 +624,7 @@ public class TestPlayer implements Player {
                             }
                         } else {
                             for (Ability manaAbility : mageObject.getAbilities().getAvailableActivatedManaAbilities(game.getState().getZone(mageObject.getId()), game)) {
-                                if (isAbilityHaveTargetNameOrAlias(game, manaAbility, groups[0])) {
+                                if (hasAbilityTargetNameOrAlias(game, manaAbility, groups[0])) {
                                     Ability newManaAbility = manaAbility.copy();
                                     computerPlayer.activateAbility((ActivatedAbility) newManaAbility, game);
                                     actions.remove(action);
@@ -636,7 +636,7 @@ public class TestPlayer implements Player {
                     List<Permanent> manaPermsWithCost = computerPlayer.getAvailableManaProducersWithCost(game);
                     for (Permanent perm : manaPermsWithCost) {
                         for (ActivatedManaAbilityImpl manaAbility : perm.getAbilities().getAvailableActivatedManaAbilities(Zone.BATTLEFIELD, game)) {
-                            if (isAbilityHaveTargetNameOrAlias(game, manaAbility, groups[0])
+                            if (hasAbilityTargetNameOrAlias(game, manaAbility, groups[0])
                                     && manaAbility.canActivate(computerPlayer.getId(), game).canActivate()) {
                                 Ability newManaAbility = manaAbility.copy();
                                 computerPlayer.activateAbility((ActivatedAbility) newManaAbility, game);
@@ -650,7 +650,7 @@ public class TestPlayer implements Player {
                     command = command.substring(command.indexOf("addCounters:") + 12);
                     String[] groups = command.split("\\$");
                     for (Permanent permanent : game.getBattlefield().getAllActivePermanents()) {
-                        if (isObjectHaveTargetNameOrAlias(permanent, groups[0])) {
+                        if (hasObjectTargetNameOrAlias(permanent, groups[0])) {
                             CounterType counterType = CounterType.findByName(groups[1]);
                             Assert.assertNotNull("Invalid counter type " + groups[1], counterType);
                             Counter counter = counterType.createInstance(Integer.parseInt(groups[2]));
@@ -989,7 +989,7 @@ public class TestPlayer implements Player {
             }
 
             // need by alias or by name
-            if (!isObjectHaveTargetNameOrAlias(perm, cardName)) {
+            if (!hasObjectTargetNameOrAlias(perm, cardName)) {
                 continue;
             }
 
@@ -1198,7 +1198,7 @@ public class TestPlayer implements Player {
     private void assertPermanentCount(PlayerAction action, Game game, Player player, String permanentName, int count) {
         int foundedCount = 0;
         for (Permanent perm : game.getBattlefield().getAllPermanents()) {
-            if (isObjectHaveTargetNameOrAlias(perm, permanentName) && perm.getControllerId().equals(player.getId())) {
+            if (hasObjectTargetNameOrAlias(perm, permanentName) && perm.getControllerId().equals(player.getId())) {
                 foundedCount++;
             }
         }
@@ -1209,7 +1209,7 @@ public class TestPlayer implements Player {
     private void assertPermanentCounters(PlayerAction action, Game game, Player player, String permanentName, CounterType counterType, int count) {
         int foundedCount = 0;
         for (Permanent perm : game.getBattlefield().getAllPermanents()) {
-            if (isObjectHaveTargetNameOrAlias(perm, permanentName) && perm.getControllerId().equals(player.getId())) {
+            if (hasObjectTargetNameOrAlias(perm, permanentName) && perm.getControllerId().equals(player.getId())) {
                 foundedCount = perm.getCounters(game).getCount(counterType);
             }
         }
@@ -1220,7 +1220,7 @@ public class TestPlayer implements Player {
     private void assertExileCount(PlayerAction action, Game game, Player player, String permanentName, int count) {
         int foundedCount = 0;
         for (Card card : game.getExile().getAllCards(game)) {
-            if (isObjectHaveTargetNameOrAlias(card, permanentName) && card.isOwnedBy(player.getId())) {
+            if (hasObjectTargetNameOrAlias(card, permanentName) && card.isOwnedBy(player.getId())) {
                 foundedCount++;
             }
         }
@@ -1231,7 +1231,7 @@ public class TestPlayer implements Player {
     private void assertGraveyardCount(PlayerAction action, Game game, Player player, String permanentName, int count) {
         int foundedCount = 0;
         for (Card card : player.getGraveyard().getCards(game)) {
-            if (isObjectHaveTargetNameOrAlias(card, permanentName) && card.isOwnedBy(player.getId())) {
+            if (hasObjectTargetNameOrAlias(card, permanentName) && card.isOwnedBy(player.getId())) {
                 foundedCount++;
             }
         }
@@ -1247,7 +1247,7 @@ public class TestPlayer implements Player {
         int realCount = 0;
         for (UUID cardId : player.getHand()) {
             Card card = game.getCard(cardId);
-            if (isObjectHaveTargetNameOrAlias(card, cardName)) {
+            if (hasObjectTargetNameOrAlias(card, cardName)) {
                 realCount++;
             }
         }
@@ -1259,7 +1259,7 @@ public class TestPlayer implements Player {
         int realCount = 0;
         for (UUID cardId : game.getCommandersIds(player)) {
             Card card = game.getCard(cardId);
-            if (isObjectHaveTargetNameOrAlias(card, cardName) && Zone.COMMAND.equals(game.getState().getZone(cardId))) {
+            if (hasObjectTargetNameOrAlias(card, cardName) && Zone.COMMAND.equals(game.getState().getZone(cardId))) {
                 realCount++;
             }
         }
@@ -1476,7 +1476,7 @@ public class TestPlayer implements Player {
                     if (group.startsWith("planeswalker=")) {
                         String planeswalkerName = group.substring(group.indexOf("planeswalker=") + 13);
                         for (Permanent permanent : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_PLANESWALKER, game)) {
-                            if (isObjectHaveTargetNameOrAlias(permanent, planeswalkerName)) {
+                            if (hasObjectTargetNameOrAlias(permanent, planeswalkerName)) {
                                 defenderId = permanent.getId();
                             }
                         }
@@ -1832,7 +1832,7 @@ public class TestPlayer implements Player {
                             if (target.getTargets().contains(permanent.getId())) {
                                 continue;
                             }
-                            if (isObjectHaveTargetNameOrAlias(permanent, targetName)) {
+                            if (hasObjectTargetNameOrAlias(permanent, targetName)) {
                                 if (target.isNotTarget() || target.canTarget(abilityControllerId, permanent.getId(), source, game)) {
                                     if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly)) {
                                         target.add(permanent.getId(), game);
@@ -1897,7 +1897,7 @@ public class TestPlayer implements Player {
                         CheckTargetsList:
                         for (UUID targetId : possibleCards) {
                             MageObject targetObject = game.getObject(targetId);
-                            if (isObjectHaveTargetNameOrAlias(targetObject, possibleChoice)) {
+                            if (hasObjectTargetNameOrAlias(targetObject, possibleChoice)) {
                                 if (target.canTarget(targetObject.getId(), game)) {
                                     // only unique targets
                                     if (usedTargets.contains(targetObject.getId())) {
@@ -1950,7 +1950,7 @@ public class TestPlayer implements Player {
                         for (UUID targetId : possibleTargets) {
                             MageObject targetObject = game.getObject(targetId);
                             if (targetObject != null) {
-                                if (isObjectHaveTargetNameOrAlias(targetObject, targetName)) {
+                                if (hasObjectTargetNameOrAlias(targetObject, targetName)) {
                                     List<UUID> alreadyTargetted = target.getTargets();
                                     if (t.canTarget(targetObject.getId(), game)) {
                                         if (alreadyTargetted != null && !alreadyTargetted.contains(targetObject.getId())) {
@@ -2086,7 +2086,7 @@ public class TestPlayer implements Player {
                             filter = ((FilterPlaneswalkerOrPlayer) filter).getFilterPermanent();
                         }
                         for (Permanent permanent : game.getBattlefield().getActivePermanents((FilterPermanent) filter, abilityControllerId, sourceId, game)) {
-                            if (isObjectHaveTargetNameOrAlias(permanent, targetName) || (permanent.getName() + '-' + permanent.getExpansionSetCode()).equals(targetName)) { // TODO: remove exp code search?
+                            if (hasObjectTargetNameOrAlias(permanent, targetName) || (permanent.getName() + '-' + permanent.getExpansionSetCode()).equals(targetName)) { // TODO: remove exp code search?
                                 if (target.canTarget(abilityControllerId, permanent.getId(), source, game) && !target.getTargets().contains(permanent.getId())) {
                                     if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly)) {
                                         target.addTarget(permanent.getId(), source, game);
@@ -2112,7 +2112,7 @@ public class TestPlayer implements Player {
                     boolean targetFound = false;
                     for (String targetName : targetList) {
                         for (Card card : computerPlayer.getHand().getCards(((TargetCardInHand) target.getOriginalTarget()).getFilter(), game)) {
-                            if (isObjectHaveTargetNameOrAlias(card, targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) { // TODO: remove set code search?
+                            if (hasObjectTargetNameOrAlias(card, targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) { // TODO: remove set code search?
                                 if (target.canTarget(abilityControllerId, card.getId(), source, game) && !target.getTargets().contains(card.getId())) {
                                     target.addTarget(card.getId(), source, game);
                                     targetFound = true;
@@ -2137,7 +2137,7 @@ public class TestPlayer implements Player {
                     boolean targetFound = false;
                     for (String targetName : targetList) {
                         for (Card card : game.getExile().getCards(targetFull.getFilter(), game)) {
-                            if (isObjectHaveTargetNameOrAlias(card, targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) { // TODO: remove set code search?
+                            if (hasObjectTargetNameOrAlias(card, targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) { // TODO: remove set code search?
                                 if (target.canTarget(abilityControllerId, card.getId(), source, game) && !target.getTargets().contains(card.getId())) {
                                     target.addTarget(card.getId(), source, game);
                                     targetFound = true;
@@ -2162,7 +2162,7 @@ public class TestPlayer implements Player {
                     boolean targetFound = false;
                     for (String targetName : targetList) {
                         for (Card card : game.getBattlefield().getAllActivePermanents()) {
-                            if (isObjectHaveTargetNameOrAlias(card, targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) { // TODO: remove set code search?
+                            if (hasObjectTargetNameOrAlias(card, targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) { // TODO: remove set code search?
                                 if (targetFull.canTarget(abilityControllerId, card.getId(), source, game) && !targetFull.getTargets().contains(card.getId())) {
                                     targetFull.add(card.getId(), game);
                                     targetFound = true;
@@ -2212,7 +2212,7 @@ public class TestPlayer implements Player {
                         for (UUID playerId : needPlayers) {
                             Player player = game.getPlayer(playerId);
                             for (Card card : player.getGraveyard().getCards(targetFull.getFilter(), game)) {
-                                if (isObjectHaveTargetNameOrAlias(card, targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) { // TODO: remove set code search?
+                                if (hasObjectTargetNameOrAlias(card, targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) { // TODO: remove set code search?
                                     if (target.canTarget(abilityControllerId, card.getId(), source, game) && !target.getTargets().contains(card.getId())) {
                                         target.addTarget(card.getId(), source, game);
                                         targetFound = true;
@@ -2239,7 +2239,7 @@ public class TestPlayer implements Player {
                     boolean targetFound = false;
                     for (String targetName : targetList) {
                         for (StackObject stackObject : game.getStack()) {
-                            if (isObjectHaveTargetNameOrAlias(stackObject, targetName)) {
+                            if (hasObjectTargetNameOrAlias(stackObject, targetName)) {
                                 if (target.canTarget(abilityControllerId, stackObject.getId(), source, game) && !target.getTargets().contains(stackObject.getId())) {
                                     target.addTarget(stackObject.getId(), source, game);
                                     targetFound = true;
@@ -2289,7 +2289,7 @@ public class TestPlayer implements Player {
                 boolean targetFound = false;
                 for (String targetName : targetList) {
                     for (Card card : cards.getCards(game)) {
-                        if (isObjectHaveTargetNameOrAlias(card, targetName) && !target.getTargets().contains(card.getId())) {
+                        if (hasObjectTargetNameOrAlias(card, targetName) && !target.getTargets().contains(card.getId())) {
                             target.addTarget(card.getId(), source, game);
                             targetFound = true;
                             break;
@@ -3517,7 +3517,7 @@ public class TestPlayer implements Player {
                         if (target.getTargets().contains(card.getId())) {
                             continue;
                         }
-                        if (isObjectHaveTargetNameOrAlias(card, targetName)) {
+                        if (hasObjectTargetNameOrAlias(card, targetName)) {
                             if (target.isNotTarget() || target.canTarget(card.getId(), game)) {
                                 target.add(card.getId(), game);
                                 targetFound = true;
diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
index 3d3e4e8eda..57a4c26ac6 100644
--- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
+++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
@@ -1819,7 +1819,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     private boolean isObjectHaveTargetNameOrAlias(Player player, MageObject object, String nameOrAlias) {
         TestPlayer testPlayer = (TestPlayer) player;
         if (player != null) { // TODO: remove null check and replace all null-player calls in tests by player
-            return testPlayer.isObjectHaveTargetNameOrAlias(object, nameOrAlias);
+            return testPlayer.hasObjectTargetNameOrAlias(object, nameOrAlias);
         } else {
             return object.getName().equals(nameOrAlias);
         }
diff --git a/Mage/src/main/java/mage/abilities/SpellAbility.java b/Mage/src/main/java/mage/abilities/SpellAbility.java
index a0ee47b51c..82593588e4 100644
--- a/Mage/src/main/java/mage/abilities/SpellAbility.java
+++ b/Mage/src/main/java/mage/abilities/SpellAbility.java
@@ -105,7 +105,7 @@ public class SpellAbility extends ActivatedAbilityImpl {
                     return ActivationStatus.getFalse();
                 }
             }
-            if (costs.canPay(this, sourceId, controllerId, game)) {
+            if (costs.canPay(this, sourceId, playerId, game)) {
                 if (getSpellAbilityType() == SpellAbilityType.SPLIT_FUSED) {
                     SplitCard splitCard = (SplitCard) game.getCard(getSourceId());
                     if (splitCard != null) {
diff --git a/Mage/src/main/java/mage/abilities/effects/AsThoughEffectImpl.java b/Mage/src/main/java/mage/abilities/effects/AsThoughEffectImpl.java
index d17ca0e263..52bf7f4436 100644
--- a/Mage/src/main/java/mage/abilities/effects/AsThoughEffectImpl.java
+++ b/Mage/src/main/java/mage/abilities/effects/AsThoughEffectImpl.java
@@ -7,6 +7,9 @@ import mage.constants.*;
 import mage.game.Game;
 
 import java.util.UUID;
+import mage.cards.SplitCard;
+import mage.cards.SplitCardHalf;
+import mage.players.Player;
 
 /**
  * @author BetaSteward_at_googlemail.com
@@ -42,7 +45,14 @@ public abstract class AsThoughEffectImpl extends ContinuousEffectImpl implements
     }
 
     /**
-     * Helper to check that affectedAbility is compatible for alternative cast modifications by setCastSourceIdWithAlternateMana
+     * Helper to check that affectedAbility is compatible for alternative cast
+     * modifications by setCastSourceIdWithAlternateMana
+     *
+     * @param cardToCheck
+     * @param affectedAbilityToCheck
+     * @param playerToCheck
+     * @param source
+     * @return
      */
     public boolean isAbilityAppliedForAlternateCast(Card cardToCheck, Ability affectedAbilityToCheck, UUID playerToCheck, Ability source) {
         return cardToCheck != null
@@ -52,4 +62,35 @@ public abstract class AsThoughEffectImpl extends ContinuousEffectImpl implements
                 && (affectedAbilityToCheck.getAbilityType() == AbilityType.SPELL
                 || affectedAbilityToCheck.getAbilityType() == AbilityType.PLAY_LAND);
     }
+
+    /**
+     * Internal method to do the neccessary to allow the card from objectId to be cast or played (if it's a land) without paying any mana.
+     * Additional costs (like sacrificing or discarding) have still to be payed.
+     * Checks if the card is of the correct type or in the correct zone have to be done before.
+     * 
+     * @param objectId sourceId of the card to play
+     * @param source source ability that allows this effect
+     * @param affectedControllerId player allowed to play the card
+     * @param game
+     * @return 
+     */
+    protected boolean allowCardToPlayWithoutMana(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
+        Player player = game.getPlayer(affectedControllerId);
+        Card card = game.getCard(objectId);
+        if (card == null || player == null) {
+            return false;
+        }
+        if (!card.isLand()) {
+            if (card instanceof SplitCard) {
+                SplitCardHalf leftCard = ((SplitCard) card).getLeftHalfCard();
+                player.setCastSourceIdWithAlternateMana(leftCard.getId(), null, leftCard.getSpellAbility().getCosts());
+                SplitCardHalf rightCard = ((SplitCard) card).getRightHalfCard();
+                player.setCastSourceIdWithAlternateMana(rightCard.getId(), null, rightCard.getSpellAbility().getCosts());
+            } else {
+                player.setCastSourceIdWithAlternateMana(objectId, null, card.getSpellAbility().getCosts());
+            }
+        }
+        return true;
+    }
+
 }
diff --git a/Mage/src/main/java/mage/abilities/effects/common/asthought/PlayFromNotOwnHandZoneTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/asthought/PlayFromNotOwnHandZoneTargetEffect.java
index ec878eb5b1..2bbb910e5f 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/asthought/PlayFromNotOwnHandZoneTargetEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/asthought/PlayFromNotOwnHandZoneTargetEffect.java
@@ -1,15 +1,23 @@
 package mage.abilities.effects.common.asthought;
 
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import java.util.UUID;
+import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.effects.AsThoughEffectImpl;
+import mage.abilities.effects.ContinuousEffect;
+import mage.cards.Card;
 import mage.constants.AsThoughEffectType;
 import mage.constants.Duration;
 import mage.constants.Outcome;
 import mage.constants.TargetController;
 import mage.constants.Zone;
 import mage.game.Game;
+import mage.players.Player;
+import mage.target.targetpointer.FixedTargets;
+import mage.util.CardUtil;
 
 /**
  *
@@ -19,6 +27,7 @@ public class PlayFromNotOwnHandZoneTargetEffect extends AsThoughEffectImpl {
 
     private final Zone fromZone;
     private final TargetController allowedCaster;
+    private final boolean withoutMana;
 
     public PlayFromNotOwnHandZoneTargetEffect() {
         this(Duration.EndOfTurn);
@@ -33,15 +42,21 @@ public class PlayFromNotOwnHandZoneTargetEffect extends AsThoughEffectImpl {
     }
 
     public PlayFromNotOwnHandZoneTargetEffect(Zone fromZone, TargetController allowedCaster, Duration duration) {
-        super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, duration, Outcome.Benefit);
+        this(fromZone, allowedCaster, duration, false);
+    }
+
+    public PlayFromNotOwnHandZoneTargetEffect(Zone fromZone, TargetController allowedCaster, Duration duration, boolean withoutMana) {
+        super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, duration, withoutMana ? Outcome.PlayForFree : Outcome.PutCardInPlay);
         this.fromZone = fromZone;
         this.allowedCaster = allowedCaster;
+        this.withoutMana = withoutMana;
     }
 
     public PlayFromNotOwnHandZoneTargetEffect(final PlayFromNotOwnHandZoneTargetEffect effect) {
         super(effect);
         this.fromZone = effect.fromZone;
         this.allowedCaster = effect.allowedCaster;
+        this.withoutMana = effect.withoutMana;
     }
 
     @Override
@@ -56,27 +71,91 @@ public class PlayFromNotOwnHandZoneTargetEffect extends AsThoughEffectImpl {
 
     @Override
     public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
+        return applies(objectId, null, source, game, affectedControllerId);
+    }
+
+    @Override
+    public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) {
+
+        List<UUID> targets = getTargetPointer().getTargets(game, source);
+        if (targets.isEmpty()) {
+            this.discard();
+            return false;
+        }
         switch (allowedCaster) {
             case YOU:
-                if (affectedControllerId != source.getControllerId()) {
+                if (playerId != source.getControllerId()) {
                     return false;
                 }
                 break;
             case OPPONENT:
-                if (!game.getOpponents(source.getControllerId()).contains(affectedControllerId)) {
+                if (!game.getOpponents(source.getControllerId()).contains(playerId)) {
                     return false;
                 }
                 break;
             case ANY:
                 break;
         }
-        List<UUID> targets = getTargetPointer().getTargets(game, source);
-        if (targets.isEmpty()) {
-            this.discard();
+        UUID objectIdToCast = CardUtil.getMainCardId(game, objectId);
+        if (targets.contains(objectIdToCast)
+                && playerId.equals(source.getControllerId())
+                && game.getState().getZone(objectId).match(fromZone)) {
+            if (withoutMana) {
+                if (affectedAbility != null) {
+                    objectIdToCast = affectedAbility.getSourceId();                    
+                }
+                return allowCardToPlayWithoutMana(objectIdToCast, source, playerId, game);
+            }
+            return true;
+        }
+        return false;
+    }
+    
+    public static boolean exileAndPlayFromExile(Game game, Ability source, Card card, TargetController allowedCaster, Duration duration, boolean withoutMana) {
+        if (card == null) {
+            return true;
+        }
+        Set<Card> cards = new HashSet<>();
+        cards.add(card);
+        return exileAndPlayFromExile(game, source, cards, allowedCaster, duration, withoutMana);
+    }    
+    /**
+     * Exiles the cards and let the allowed player play them from exile for the given duration
+     * 
+     * @param game
+     * @param source
+     * @param cards
+     * @param allowedCaster
+     * @param duration
+     * @param withoutMana
+     * @return 
+     */
+    public static boolean exileAndPlayFromExile(Game game, Ability source, Set<Card> cards, TargetController allowedCaster, Duration duration, boolean withoutMana) {
+        if (cards == null || cards.isEmpty()) {
+            return true;
+        }
+        Player controller = game.getPlayer(source.getControllerId());
+        MageObject sourceObject = source.getSourceObject(game);
+        if (controller == null || sourceObject == null) {
             return false;
         }
-        return targets.contains(objectId)
-                && affectedControllerId.equals(source.getControllerId())
-                && game.getState().getZone(objectId).match(fromZone);
+        UUID exileId = CardUtil.getExileZoneId(
+                controller.getId().toString()
+                        + "-" + game.getState().getTurnNum()
+                        + "-" + sourceObject.getIdName(), game
+        );
+        String exileName = sourceObject.getIdName() + " free play" 
+                + (Duration.EndOfTurn.equals(duration) ? " on turn " + game.getState().getTurnNum():"") 
+                + " for " + controller.getName();
+        if (Duration.EndOfTurn.equals(duration)) {
+            game.getExile().createZone(exileId, exileName).setCleanupOnEndTurn(true);
+        }        
+        if (!controller.moveCardsToExile(cards, source, game, true, exileId, exileName)) {
+            return false;
+        }
+        ContinuousEffect effect = new PlayFromNotOwnHandZoneTargetEffect(Zone.EXILED, allowedCaster, duration, withoutMana);
+        effect.setTargetPointer(new FixedTargets(cards, game));
+        game.addEffect(effect, source);
+        return true;
     }
 }
diff --git a/Mage/src/main/java/mage/cards/Card.java b/Mage/src/main/java/mage/cards/Card.java
index 3bb996652b..364415d96d 100644
--- a/Mage/src/main/java/mage/cards/Card.java
+++ b/Mage/src/main/java/mage/cards/Card.java
@@ -29,6 +29,8 @@ public interface Card extends MageObject {
     /**
      * For cards: return all basic and dynamic abilities
      * For permanents: return all basic and dynamic abilities
+     * @param game
+     * @return 
      */
     Abilities<Ability> getAbilities(Game game);
 
diff --git a/Mage/src/main/java/mage/target/targetpointer/FixedTarget.java b/Mage/src/main/java/mage/target/targetpointer/FixedTarget.java
index 137303c8c5..d125bcc84b 100644
--- a/Mage/src/main/java/mage/target/targetpointer/FixedTarget.java
+++ b/Mage/src/main/java/mage/target/targetpointer/FixedTarget.java
@@ -27,6 +27,12 @@ public class FixedTarget implements TargetPointer {
         this(mor.getSourceId(), mor.getZoneChangeCounter());
     }
 
+    /**
+     * Target counter is immediatly initialised with current zoneChangeCounter value from the GameState
+     * Sets fixed the currect zone chnage counter 
+     * @param card used to get the objectId
+     * @param game 
+     */
     public FixedTarget(Card card, Game game) {
         this.targetId = card.getId();
         this.zoneChangeCounter = card.getZoneChangeCounter(game);

From f362ebd4a317e7ef2294877d235d02ebd04a7e59 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Wed, 17 Jun 2020 20:37:35 -0400
Subject: [PATCH 351/586] updated M21 and JMP spoiler and reprints

---
 Mage.Sets/src/mage/sets/CoreSet2021.java |  2 +-
 Mage.Sets/src/mage/sets/Jumpstart.java   | 13 +++++++++++++
 Utils/mtg-cards-data.txt                 | 18 +++++++++++++++++-
 3 files changed, 31 insertions(+), 2 deletions(-)

diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 2e5022c177..c69e99a7dc 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -264,8 +264,8 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Tolarian Kraken", 80, Rarity.UNCOMMON, mage.cards.t.TolarianKraken.class));
         cards.add(new SetCardInfo("Tormod's Crypt", 241, Rarity.UNCOMMON, mage.cards.t.TormodsCrypt.class));
         cards.add(new SetCardInfo("Traitorous Greed", 166, Rarity.UNCOMMON, mage.cards.t.TraitorousGreed.class));
-        cards.add(new SetCardInfo("Transmogrify", 167, Rarity.RARE, mage.cards.t.Transmogrify.class));
         cards.add(new SetCardInfo("Tranquil Cove", 258, Rarity.COMMON, mage.cards.t.TranquilCove.class));
+        cards.add(new SetCardInfo("Transmogrify", 167, Rarity.RARE, mage.cards.t.Transmogrify.class));
         cards.add(new SetCardInfo("Trufflesnout", 212, Rarity.COMMON, mage.cards.t.Trufflesnout.class));
         cards.add(new SetCardInfo("Turn to Slag", 168, Rarity.COMMON, mage.cards.t.TurnToSlag.class));
         cards.add(new SetCardInfo("Turret Ogre", 169, Rarity.COMMON, mage.cards.t.TurretOgre.class));
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index 4335d60522..6471d88668 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -25,40 +25,53 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Auger Spree", 449, Rarity.COMMON, mage.cards.a.AugerSpree.class));
         cards.add(new SetCardInfo("Bake into a Pie", 201, Rarity.COMMON, mage.cards.b.BakeIntoAPie.class));
         cards.add(new SetCardInfo("Black Cat", 203, Rarity.COMMON, mage.cards.b.BlackCat.class));
+        cards.add(new SetCardInfo("Blighted Bat", 205, Rarity.COMMON, mage.cards.b.BlightedBat.class));
         cards.add(new SetCardInfo("Blood Divination", 207, Rarity.UNCOMMON, mage.cards.b.BloodDivination.class));
         cards.add(new SetCardInfo("Bloodhunter Bat", 210, Rarity.COMMON, mage.cards.b.BloodhunterBat.class));
         cards.add(new SetCardInfo("Bogbrew Witch", 211, Rarity.RARE, mage.cards.b.BogbrewWitch.class));
         cards.add(new SetCardInfo("Bone Picker", 212, Rarity.UNCOMMON, mage.cards.b.BonePicker.class));
         cards.add(new SetCardInfo("Brushstrider", 381, Rarity.UNCOMMON, mage.cards.b.Brushstrider.class));
         cards.add(new SetCardInfo("Bubbling Cauldron", 460, Rarity.UNCOMMON, mage.cards.b.BubblingCauldron.class));
+        cards.add(new SetCardInfo("Cemetery Recruitment", 217, Rarity.COMMON, mage.cards.c.CemeteryRecruitment.class));
         cards.add(new SetCardInfo("Chamber Sentry", 461, Rarity.RARE, mage.cards.c.ChamberSentry.class));
         cards.add(new SetCardInfo("Dinrova Horror", 450, Rarity.UNCOMMON, mage.cards.d.DinrovaHorror.class));
         cards.add(new SetCardInfo("Dutiful Attendant", 226, Rarity.COMMON, mage.cards.d.DutifulAttendant.class));
         cards.add(new SetCardInfo("Entomber Exarch", 227, Rarity.UNCOMMON, mage.cards.e.EntomberExarch.class));
+        cards.add(new SetCardInfo("Fanatical Firebrand", 315, Rarity.COMMON, mage.cards.f.FanaticalFirebrand.class));
         cards.add(new SetCardInfo("Festering Newt", 234, Rarity.COMMON, mage.cards.f.FesteringNewt.class));
+        cards.add(new SetCardInfo("Flames of the Firebrand", 357, Rarity.UNCOMMON, mage.cards.f.FlamesOfTheFirebrand.class));
         cards.add(new SetCardInfo("Forest", 76, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Fusion Elemental", 451, Rarity.UNCOMMON, mage.cards.f.FusionElemental.class));
         cards.add(new SetCardInfo("Ghoulcaller's Accomplice", 237, Rarity.COMMON, mage.cards.g.GhoulcallersAccomplice.class));
+        cards.add(new SetCardInfo("Ghoulraiser", 238, Rarity.COMMON, mage.cards.g.Ghoulraiser.class));
         cards.add(new SetCardInfo("Gravewaker", 241, Rarity.RARE, mage.cards.g.Gravewaker.class));
+        cards.add(new SetCardInfo("Hungry Flames", 336, Rarity.UNCOMMON, mage.cards.h.HungryFlames.class));
         cards.add(new SetCardInfo("Innocent Blood", 244, Rarity.COMMON, mage.cards.i.InnocentBlood.class));
         cards.add(new SetCardInfo("Ironroot Warlord", 452, Rarity.UNCOMMON, mage.cards.i.IronrootWarlord.class));
         cards.add(new SetCardInfo("Isamaru, Hound of Konda", 113, Rarity.RARE, mage.cards.i.IsamaruHoundOfKonda.class));
         cards.add(new SetCardInfo("Knight of the Tusk", 114, Rarity.COMMON, mage.cards.k.KnightOfTheTusk.class));
         cards.add(new SetCardInfo("Last Gasp", 247, Rarity.COMMON, mage.cards.l.LastGasp.class));
         cards.add(new SetCardInfo("Lawmage's Binding", 453, Rarity.COMMON, mage.cards.l.LawmagesBinding.class));
+        cards.add(new SetCardInfo("Liliana's Elite", 250, Rarity.UNCOMMON, mage.cards.l.LilianasElite.class));
         cards.add(new SetCardInfo("Maelstrom Archangel", 454, Rarity.MYTHIC, mage.cards.m.MaelstromArchangel.class));
         cards.add(new SetCardInfo("Mire Triton", 257, Rarity.UNCOMMON, mage.cards.m.MireTriton.class));
         cards.add(new SetCardInfo("Mirrodin's Core", 492, Rarity.UNCOMMON, mage.cards.m.MirrodinsCore.class));
         cards.add(new SetCardInfo("Nyxathid", 259, Rarity.RARE, mage.cards.n.Nyxathid.class));
+        cards.add(new SetCardInfo("Pillar of Flame", 355, Rarity.RARE, mage.cards.p.PillarOfFlame.class));
+        cards.add(new SetCardInfo("Plains", 45, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Prophetic Prism", 478, Rarity.COMMON, mage.cards.p.PropheticPrism.class));
+        cards.add(new SetCardInfo("Pyroclastic Elemental", 356, Rarity.UNCOMMON, mage.cards.p.PyroclasticElemental.class));
         cards.add(new SetCardInfo("Raging Regisaur", 455, Rarity.UNCOMMON, mage.cards.r.RagingRegisaur.class));
         cards.add(new SetCardInfo("Reanimate", 270, Rarity.RARE, mage.cards.r.Reanimate.class));
         cards.add(new SetCardInfo("Rupture Spire", 495, Rarity.COMMON, mage.cards.r.RuptureSpire.class));
+        cards.add(new SetCardInfo("Settle the Score", 276, Rarity.UNCOMMON, mage.cards.s.SettleTheScore.class));
         cards.add(new SetCardInfo("Shambling Goblin", 277, Rarity.COMMON, mage.cards.s.ShamblingGoblin.class));
         cards.add(new SetCardInfo("Skittering Surveyor", 486, Rarity.COMMON, mage.cards.s.SkitteringSurveyor.class));
         cards.add(new SetCardInfo("Swamp", 54, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Swarm of Bloodflies", 282, Rarity.UNCOMMON, mage.cards.s.SwarmOfBloodflies.class));
         cards.add(new SetCardInfo("Tempting Witch", 283, Rarity.UNCOMMON, mage.cards.t.TemptingWitch.class));
         cards.add(new SetCardInfo("Terramorphic Expanse", 78, Rarity.COMMON, mage.cards.t.TerramorphicExpanse.class));
+        cards.add(new SetCardInfo("Wailing Ghoul", 286, Rarity.COMMON, mage.cards.w.WailingGhoul.class));
+        cards.add(new SetCardInfo("Young Pyromancer", 372, Rarity.UNCOMMON, mage.cards.y.YoungPyromancer.class));
     }
 }
diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt
index 10edd00769..4e0002a837 100644
--- a/Utils/mtg-cards-data.txt
+++ b/Utils/mtg-cards-data.txt
@@ -37601,7 +37601,7 @@ Soul Sear|Core Set 2021|160|U|{2}{R}|Instant|||Soul Sear deals 5 damage to targe
 Spellgorger Weird|Core Set 2021|161|C|{2}{R}|Creature - Weird|2|2|Whenever you cast a noncreature spell, put a +1/+1 counter on Spellgorger Weird.|
 Subira, Tulzidi Caravanner|Core Set 2021|162|R|{2}{R}|Legendary Creature - Human Shaman|2|3|Haste${1}: Another target creature with power 2 or less can't be blocked this turn.${1}{R}, {T}, Discard your hand: Until end of turn, whenever a creature you control with power 2 or less deals combat damage to a player, draw a card.|
 Sure Strike|Core Set 2021|163|C|{1}{R}|Instant|||Target creature gets +3/+0 and gains first strike until end of turn.|
-Terror of the Peaks|Core Set 2021|164|M|{3}{R}{R}|Creature - Dragon|5|4|Flying$Spells your opponents cast that target Terror of the Peaks cost an additional 3 life to cast.$Whenever another creature enters the battlefield under your control, Terror of the Peaks deals damage equal to that creature's power to any taret.|
+Terror of the Peaks|Core Set 2021|164|M|{3}{R}{R}|Creature - Dragon|5|4|Flying$Spells your opponents cast that target Terror of the Peaks cost an additional 3 life to cast.$Whenever another creature enters the battlefield under your control, Terror of the Peaks deals damage equal to that creature's power to any target.|
 Thrill of Possibility|Core Set 2021|165|C|{1}{R}|Instant|||As an additional cost to cast this spell, discard a card.$Draw two cards.|
 Traitorous Greed|Core Set 2021|166|U|{3}{R}|Sorcery|||Gain control of target creature until end of turn. Untap that creature. It gains haste until end of turn. Add two mana of any one color.|
 Transmogrify|Core Set 2021|167|R|{3}{R}|Sorcery|||Exile target creature. That creature's controller reveals cards from the top of their library until they reveal a creature card. That player puts that card onto the battlefield, then shuffles the rest into their library.|
@@ -37723,6 +37723,9 @@ Garruk, Savage Herald|Core Set 2021|336|M|{4}{G}{G}|Legendary Planeswalker - Gar
 Garruk's Warsteed|Core Set 2021|337|R|{3}{G}{G}|Creature - Rhino|3|5|Vigilance$When Garruk's Warsteed enters the battlefield, you may search your library and/or graveyard for a card named Garruk, Savage Herald, reveal it, and put it into your hand. If you search your library this way, shuffle it.|
 Predatory Wurm|Core Set 2021|338|U|{3}{G}|Creature - Wurm|4|4|Vigilance$Predatory Wurm gets +2/+2 as long as you control a Garruk planeswalker.|
 Wildwood Patrol|Core Set 2021|339|C|{2}{G}|Creature - Centaur Scout|4|2|Trample|
+Release the Dogs|Jumpstart|4|U|{3}{W}|Sorcery|||Create four 1/1 white Dog creature tokens.|
+Supply Runners|Jumpstart|7|U|{4}{W}|Creature - Dog|2|2|When Supply Runners enters the battlefield, put a +1/+1 counter on each other creature you control.|
+Trusty Retriever|Jumpstart|8|C|{3}{W}|Creature - Dog|2|3|When Trusty Retriever enters the battlefield, choose one —$• Put a +1/+1 counter on Trusty Retriever.$• Return target artifact or enchantment card from your graveyard to your hand.|
 Kels, Fight Fixer|Jumpstart|15|R|{2}{B}{B}|Legendary Creature - Azra Warlock|4|3|Menace$Whenever you sacrifice a creature, you may pay {U/B}. If you do, draw a card.${1}, Sacrifice a creature: Kels, Fight Fixer gains indestructible until end of turn.|
 Nocturnal Feeder|Jumpstart|16|C|{2}{B}|Creature - Vampire Rogue|2|1|Flying$When Nocturnal Feeder dies, each opponent loses 2 life and you gain 2 life.|
 Tinybones, Trinket Thief|Jumpstart|17|M|{1}{B}|Legendary Creature - Skeleton Rogue|1|2|At the beginning of each end step, if an opponent discarded a card this turn, you draw a card and you lose 1 life.${4}{B}{B}: Each opponent with no cards in hand loses 10 life.|
@@ -37733,6 +37736,7 @@ Thriving Grove|Jumpstart|34|C||Land|||Thriving Grove enters the battlefield tapp
 Thriving Heath|Jumpstart|35|C||Land|||Thriving Heath enters the battlefield tapped.$As Thriving Heath enters the battlefield, choose a color other than white.${T}: Add {W} or one mana of the chosen color.|
 Thriving Isle|Jumpstart|36|C||Land|||Thriving Isle enters the battlefield tapped.$As Thriving Isle enters the battlefield, choose a color other than blue.${T}: Add {U} or one mana of the chosen color.|
 Thriving Moor|Jumpstart|37|C||Land|||Thriving Moor enters the battlefield tapped.$As Thriving Moor enters the battlefield, choose a color other than black.${T}: Add {B} or one mana of the chosen color.|
+Plains|Jumpstart|45|C||Basic Land - Plains|||({T}: Add {W}.)|
 Swamp|Jumpstart|54|C||Basic Land - Swamp|||({T}: Add {B}.)|
 Forest|Jumpstart|76|C||Basic Land - Forest|||({T}: Add {G}.)|
 Terramorphic Expanse|Jumpstart|78|C||Land|||{T}, Sacrifice Terramorphic Expanse: Search your library for a basic land card, put it onto the battlefield tapped, then shuffle your library.|
@@ -37740,23 +37744,35 @@ Isamaru, Hound of Konda|Jumpstart|113|R|{W}|Legendary Creature - Hound|2|2||
 Knight of the Tusk|Jumpstart|114|C|{4}{W}{W}|Creature - Human Knight|3|7|Vigilance|
 Bake into a Pie|Jumpstart|201|C|{2}{B}{B}|Instant|||Destroy target creature. Create a Food token.|
 Black Cat|Jumpstart|203|C|{1}{B}|Creature - Zombie Cat|1|1|When Black Cat dies, target opponent discards a card at random.|
+Blighted Bat|Jumpstart|205|C|{2}{B}|Creature - Zombie Bat|2|1|Flying${1}: Blighted Bat gains haste until end of turn.|
 Blood Divination|Jumpstart|207|U|{3}{B}|Sorcery|||As an additional cost to cast this spell, sacrifice a creature.$Draw three cards.|
 Bloodhunter Bat|Jumpstart|210|C|{3}{B}|Creature - Bat|2|2|Flying$When Bloodhunter Bat enters the battlefield, target player loses 2 life and you gain 2 life.|
 Bogbrew Witch|Jumpstart|211|R|{3}{B}|Creature - Human Wizard|1|3|{2}, {T}: Search your library for a card named Festering Newt or Bubbling Cauldron, put it onto the battlefield tapped, then shuffle your library.|
 Bone Picker|Jumpstart|212|U|{3}{B}|Creature - Bird|3|2|This spell costs {3} less to cast if a creature died this turn.$Flying, deathtouch|
+Cemetery Recruitment|Jumpstart|217|C|{1}{B}|Sorcery|||Return target creature card from your graveyard to your hand. If it's a Zombie card, draw a card.|
 Dutiful Attendant|Jumpstart|226|C|{2}{B}|Creature - Human Warrior|1|2|When Dutiful Attendant dies, return another target creature card from your graveyard to your hand.|
 Entomber Exarch|Jumpstart|227|U|{2}{B}{B}|Creature - Cleric|2|2|When Entomber Exarch enters the battlefield, choose one —$• Return target creature card from your graveyard to your hand.$• Target opponent reveals their hand. You choose a noncreature card from it. That player discards that card.|
 Festering Newt|Jumpstart|234|C|{B}|Creature - Salamander|1|1|When Festering Newt dies, target creature an opponent controls gets -1/-1 until end of turn. That creature gets -4/-4 instead if you control a creature named Bogbrew Witch.|
 Ghoulcaller's Accomplice|Jumpstart|237|C|{1}{B}|Creature - Human Rogue|2|2|{3}{B}, Exile Ghoulcaller's Accomplice from your graveyard: Create a 2/2 black Zombie creature token. Activate this ability only any time you could cast a sorcery.|
+Ghoulraiser|Jumpstart|238|C|{1}{B}{B}|Creature - Zombie|2|2|When Ghoulraiser enters the battlefield, return a Zombie card at random from your graveyard to your hand.|
 Gravewaker|Jumpstart|241|R|{4}{B}{B}|Creature - Bird Spirit|5|5|Flying${5}{B}{B}: Return target creature card from your graveyard to the battlefield tapped.|
 Innocent Blood|Jumpstart|244|C|{B}|Sorcery|||Each player sacrifices a creature.|
 Last Gasp|Jumpstart|247|C|{1}{B}|Instant|||Target creature gets -3/-3 until end of turn.|
+Liliana's Elite|Jumpstart|250|U|{2}{B}|Creature - Zombie|1|1|Liliana's Elite gets +1/+1 for each creature card in your graveyard.|
 Mire Triton|Jumpstart|257|U|{1}{B}|Creature - Zombie Merfolk|2|1|Deathtouch$When Mire Triton enters the battlefield, put the top two cards of your library into your graveyard and you gain 2 life.|
 Nyxathid|Jumpstart|259|R|{1}{B}{B}|Creature - Elemental|7|7|As Nyxathid enters the battlefield, choose an opponent.$Nyxathid gets -1/-1 for each card in the chosen player's hand.|
 Reanimate|Jumpstart|270|R|{B}|Sorcery|||Put target creature card from a graveyard onto the battlefield under your control. You lose life equal to its converted mana cost.|
+Settle the Score|Jumpstart|276|U|{2}{B}{B}|Sorcery|||Exile target creature. Put two loyalty counters on a planeswalker you control.|
 Shambling Goblin|Jumpstart|277|C|{B}|Creature - Zombie Goblin|1|1|When Shambling Goblin dies, target creature an opponent controls gets -1/-1 until end of turn.|
 Swarm of Bloodflies|Jumpstart|282|U|{4}{B}|Creature - Insect|0|0|Flying$Swarm of Bloodflies enters the battlefield with two +1/+1 counters on it.$Whenever another creature dies, put a +1/+1 counter on Swarm of Bloodflies.|
 Tempting Witch|Jumpstart|283|U|{2}{B}|Creature - Human Warlock|1|3|When Tempting Witch enters the battlefield, create a Food token.${2}, {T}, Sacrifice a Food: Target player loses 3 life.|
+Wailing Ghoul|Jumpstart|286|C|{1}{B}|Creature - Zombie|1|3|When Wailing Ghoul enters the battlefield, put the top two cards of your library into your graveyard.|
+Fanatical Firebrand|Jumpstart|315|C|{R}|Creature - Goblin Pirate|1|1|Haste${T}, Sacrifice Fanatical Firebrand: It deals 1 damage to any target.|
+Hungry Flames|Jumpstart|336|U|{2}{R}|Instant|||Hungry Flames deals 3 damage to target creature and 2 damage to target player or planeswalker.|
+Pillar of Flame|Jumpstart|355|R|{R}|Sorcery|||Pillar of Flame deals 2 damage to any target. If a creature dealt damage this way would die this turn, exile it instead.|
+Pyroclastic Elemental|Jumpstart|356|U|{3}{R}{R}|Creature - Elemental|5|4|{1}{R}{R}: Pyroclastic Elemental deals 1 damage to target player.|
+Flames of the Firebrand|Jumpstart|357|U|{2}{R}|Sorcery|||Flames of the Firebrand deals 3 damage divided as you choose among one, two, or three targets.|
+Young Pyromancer|Jumpstart|372|U|{1}{R}|Creature - Human Shaman|2|1|Whenever you cast an instant or sorcery spell, create a 1/1 red Elemental creature token.|
 Affectionate Indrik|Jumpstart|373|U|{5}{G}|Creature - Beast|4|4|When Affectionate Indrik enters the battlefield, you may have it fight target creature you don't control.|
 Brushstrider|Jumpstart|381|U|{1}{G}|Creature - Beast|3|1|Vigilance|
 Auger Spree|Jumpstart|449|C|{1}{B}{R}|Instant|||Target creature gets +4/-4 until end of turn.|

From d8370013afe6068b1e424ce5060cef5e58c60efb Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Wed, 17 Jun 2020 20:49:36 -0400
Subject: [PATCH 352/586] Implemented Thriving Heath

---
 Mage.Sets/src/mage/cards/t/ThrivingHeath.java | 45 +++++++++++++++++++
 Mage.Sets/src/mage/sets/Jumpstart.java        |  1 +
 .../effects/common/ChooseColorEffect.java     | 32 ++++++++-----
 3 files changed, 66 insertions(+), 12 deletions(-)
 create mode 100644 Mage.Sets/src/mage/cards/t/ThrivingHeath.java

diff --git a/Mage.Sets/src/mage/cards/t/ThrivingHeath.java b/Mage.Sets/src/mage/cards/t/ThrivingHeath.java
new file mode 100644
index 0000000000..1368e8cb79
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/t/ThrivingHeath.java
@@ -0,0 +1,45 @@
+package mage.cards.t;
+
+import mage.abilities.common.AsEntersBattlefieldAbility;
+import mage.abilities.common.EntersBattlefieldTappedAbility;
+import mage.abilities.costs.common.TapSourceCost;
+import mage.abilities.effects.common.ChooseColorEffect;
+import mage.abilities.effects.mana.AddManaChosenColorEffect;
+import mage.abilities.mana.SimpleManaAbility;
+import mage.abilities.mana.WhiteManaAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Outcome;
+import mage.constants.Zone;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class ThrivingHeath extends CardImpl {
+
+    public ThrivingHeath(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.LAND}, "");
+
+        // Thriving Heath enters the battlefield tapped.
+        this.addAbility(new EntersBattlefieldTappedAbility());
+
+        // As Thriving Heath enters the battlefield, choose a color other than white.
+        this.addAbility(new AsEntersBattlefieldAbility(new ChooseColorEffect(Outcome.Neutral, "White")));
+
+        // {T}: Add {W} or one mana of the chosen color.
+        this.addAbility(new WhiteManaAbility());
+        this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, new AddManaChosenColorEffect(), new TapSourceCost()));
+    }
+
+    private ThrivingHeath(final ThrivingHeath card) {
+        super(card);
+    }
+
+    @Override
+    public ThrivingHeath copy() {
+        return new ThrivingHeath(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index 6471d88668..b2bac91caf 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -71,6 +71,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Swarm of Bloodflies", 282, Rarity.UNCOMMON, mage.cards.s.SwarmOfBloodflies.class));
         cards.add(new SetCardInfo("Tempting Witch", 283, Rarity.UNCOMMON, mage.cards.t.TemptingWitch.class));
         cards.add(new SetCardInfo("Terramorphic Expanse", 78, Rarity.COMMON, mage.cards.t.TerramorphicExpanse.class));
+        cards.add(new SetCardInfo("Thriving Heath", 35, Rarity.COMMON, mage.cards.t.ThrivingHeath.class));
         cards.add(new SetCardInfo("Wailing Ghoul", 286, Rarity.COMMON, mage.cards.w.WailingGhoul.class));
         cards.add(new SetCardInfo("Young Pyromancer", 372, Rarity.UNCOMMON, mage.cards.y.YoungPyromancer.class));
     }
diff --git a/Mage/src/main/java/mage/abilities/effects/common/ChooseColorEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ChooseColorEffect.java
index 50a1c928bf..72f5fc686c 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/ChooseColorEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/ChooseColorEffect.java
@@ -12,18 +12,25 @@ import mage.players.Player;
 import mage.util.CardUtil;
 
 /**
- *
  * @author Plopman
  */
 public class ChooseColorEffect extends OneShotEffect {
 
+    private final String exceptColor;
+
     public ChooseColorEffect(Outcome outcome) {
+        this(outcome, null);
+    }
+
+    public ChooseColorEffect(Outcome outcome, String exceptColor) {
         super(outcome);
-        staticText = "choose a color";
+        this.exceptColor = exceptColor;
+        staticText = "choose a color" + (exceptColor != null ? " other than " + exceptColor.toLowerCase() : "");
     }
 
     public ChooseColorEffect(final ChooseColorEffect effect) {
         super(effect);
+        this.exceptColor = effect.exceptColor;
     }
 
     @Override
@@ -34,17 +41,18 @@ public class ChooseColorEffect extends OneShotEffect {
             mageObject = game.getObject(source.getSourceId());
         }
         ChoiceColor choice = new ChoiceColor();
-        if (controller != null && mageObject != null && controller.choose(outcome, choice, game)) {
-            if (!game.isSimulation()) {
-                game.informPlayers(mageObject.getLogName() + ": " + controller.getLogName() + " has chosen " + choice.getChoice());
-            }
-            game.getState().setValue(mageObject.getId() + "_color", choice.getColor());
-            if (mageObject instanceof Permanent) {
-                ((Permanent) mageObject).addInfo("chosen color", CardUtil.addToolTipMarkTags("Chosen color: " + choice.getChoice()), game);
-            }
-            return true;
+        if (exceptColor != null) {
+            choice.removeColorFromChoices(exceptColor);
         }
-        return false;
+        if (controller == null || mageObject == null || !controller.choose(outcome, choice, game)) {
+            return false;
+        }
+        game.informPlayers(mageObject.getLogName() + ": " + controller.getLogName() + " has chosen " + choice.getChoice());
+        game.getState().setValue(mageObject.getId() + "_color", choice.getColor());
+        if (mageObject instanceof Permanent) {
+            ((Permanent) mageObject).addInfo("chosen color", CardUtil.addToolTipMarkTags("Chosen color: " + choice.getChoice()), game);
+        }
+        return true;
     }
 
     @Override

From b9ffd39c4326719c4af951e624e36c154286294b Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Wed, 17 Jun 2020 20:51:55 -0400
Subject: [PATCH 353/586] Implemented Thriving Isle

---
 Mage.Sets/src/mage/cards/t/ThrivingIsle.java | 45 ++++++++++++++++++++
 Mage.Sets/src/mage/sets/Jumpstart.java       |  1 +
 2 files changed, 46 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/t/ThrivingIsle.java

diff --git a/Mage.Sets/src/mage/cards/t/ThrivingIsle.java b/Mage.Sets/src/mage/cards/t/ThrivingIsle.java
new file mode 100644
index 0000000000..c0564e8c2a
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/t/ThrivingIsle.java
@@ -0,0 +1,45 @@
+package mage.cards.t;
+
+import mage.abilities.common.AsEntersBattlefieldAbility;
+import mage.abilities.common.EntersBattlefieldTappedAbility;
+import mage.abilities.costs.common.TapSourceCost;
+import mage.abilities.effects.common.ChooseColorEffect;
+import mage.abilities.effects.mana.AddManaChosenColorEffect;
+import mage.abilities.mana.BlueManaAbility;
+import mage.abilities.mana.SimpleManaAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Outcome;
+import mage.constants.Zone;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class ThrivingIsle extends CardImpl {
+
+    public ThrivingIsle(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.LAND}, "");
+
+        // Thriving Isle enters the battlefield tapped.
+        this.addAbility(new EntersBattlefieldTappedAbility());
+
+        // As Thriving Isle enters the battlefield, choose a color other than blue.
+        this.addAbility(new AsEntersBattlefieldAbility(new ChooseColorEffect(Outcome.Neutral, "Blue")));
+
+        // {T}: Add {U} or one mana of the chosen color.
+        this.addAbility(new BlueManaAbility());
+        this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, new AddManaChosenColorEffect(), new TapSourceCost()));
+    }
+
+    private ThrivingIsle(final ThrivingIsle card) {
+        super(card);
+    }
+
+    @Override
+    public ThrivingIsle copy() {
+        return new ThrivingIsle(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index b2bac91caf..66f8c4c98f 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -72,6 +72,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Tempting Witch", 283, Rarity.UNCOMMON, mage.cards.t.TemptingWitch.class));
         cards.add(new SetCardInfo("Terramorphic Expanse", 78, Rarity.COMMON, mage.cards.t.TerramorphicExpanse.class));
         cards.add(new SetCardInfo("Thriving Heath", 35, Rarity.COMMON, mage.cards.t.ThrivingHeath.class));
+        cards.add(new SetCardInfo("Thriving Isle", 36, Rarity.COMMON, mage.cards.t.ThrivingIsle.class));
         cards.add(new SetCardInfo("Wailing Ghoul", 286, Rarity.COMMON, mage.cards.w.WailingGhoul.class));
         cards.add(new SetCardInfo("Young Pyromancer", 372, Rarity.UNCOMMON, mage.cards.y.YoungPyromancer.class));
     }

From da71f612520ce8370f596af1dfd27b845acfe557 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Wed, 17 Jun 2020 20:54:34 -0400
Subject: [PATCH 354/586] Implemented Thriving Moor

---
 Mage.Sets/src/mage/cards/t/ThrivingMoor.java | 45 ++++++++++++++++++++
 Mage.Sets/src/mage/sets/Jumpstart.java       |  1 +
 2 files changed, 46 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/t/ThrivingMoor.java

diff --git a/Mage.Sets/src/mage/cards/t/ThrivingMoor.java b/Mage.Sets/src/mage/cards/t/ThrivingMoor.java
new file mode 100644
index 0000000000..5765c37066
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/t/ThrivingMoor.java
@@ -0,0 +1,45 @@
+package mage.cards.t;
+
+import mage.abilities.common.AsEntersBattlefieldAbility;
+import mage.abilities.common.EntersBattlefieldTappedAbility;
+import mage.abilities.costs.common.TapSourceCost;
+import mage.abilities.effects.common.ChooseColorEffect;
+import mage.abilities.effects.mana.AddManaChosenColorEffect;
+import mage.abilities.mana.BlackManaAbility;
+import mage.abilities.mana.SimpleManaAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Outcome;
+import mage.constants.Zone;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class ThrivingMoor extends CardImpl {
+
+    public ThrivingMoor(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.LAND}, "");
+
+        // Thriving Moor enters the battlefield tapped.
+        this.addAbility(new EntersBattlefieldTappedAbility());
+
+        // As Thriving Moor enters the battlefield, choose a color other than black.
+        this.addAbility(new AsEntersBattlefieldAbility(new ChooseColorEffect(Outcome.Neutral, "Black")));
+
+        // {T}: Add {B} or one mana of the chosen color.
+        this.addAbility(new BlackManaAbility());
+        this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, new AddManaChosenColorEffect(), new TapSourceCost()));
+    }
+
+    private ThrivingMoor(final ThrivingMoor card) {
+        super(card);
+    }
+
+    @Override
+    public ThrivingMoor copy() {
+        return new ThrivingMoor(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index 66f8c4c98f..e1c98b2861 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -73,6 +73,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Terramorphic Expanse", 78, Rarity.COMMON, mage.cards.t.TerramorphicExpanse.class));
         cards.add(new SetCardInfo("Thriving Heath", 35, Rarity.COMMON, mage.cards.t.ThrivingHeath.class));
         cards.add(new SetCardInfo("Thriving Isle", 36, Rarity.COMMON, mage.cards.t.ThrivingIsle.class));
+        cards.add(new SetCardInfo("Thriving Moor", 37, Rarity.COMMON, mage.cards.t.ThrivingMoor.class));
         cards.add(new SetCardInfo("Wailing Ghoul", 286, Rarity.COMMON, mage.cards.w.WailingGhoul.class));
         cards.add(new SetCardInfo("Young Pyromancer", 372, Rarity.UNCOMMON, mage.cards.y.YoungPyromancer.class));
     }

From 07b2887039de9fe8c01c3c9a547a912a7e012046 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Wed, 17 Jun 2020 20:56:32 -0400
Subject: [PATCH 355/586] Implemented Thriving Bluff

---
 Mage.Sets/src/mage/cards/t/ThrivingBluff.java | 45 +++++++++++++++++++
 Mage.Sets/src/mage/sets/Jumpstart.java        |  1 +
 2 files changed, 46 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/t/ThrivingBluff.java

diff --git a/Mage.Sets/src/mage/cards/t/ThrivingBluff.java b/Mage.Sets/src/mage/cards/t/ThrivingBluff.java
new file mode 100644
index 0000000000..f3f5cfec25
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/t/ThrivingBluff.java
@@ -0,0 +1,45 @@
+package mage.cards.t;
+
+import mage.abilities.common.AsEntersBattlefieldAbility;
+import mage.abilities.common.EntersBattlefieldTappedAbility;
+import mage.abilities.costs.common.TapSourceCost;
+import mage.abilities.effects.common.ChooseColorEffect;
+import mage.abilities.effects.mana.AddManaChosenColorEffect;
+import mage.abilities.mana.RedManaAbility;
+import mage.abilities.mana.SimpleManaAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Outcome;
+import mage.constants.Zone;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class ThrivingBluff extends CardImpl {
+
+    public ThrivingBluff(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.LAND}, "");
+
+        // Thriving Bluff enters the battlefield tapped.
+        this.addAbility(new EntersBattlefieldTappedAbility());
+
+        // As Thriving Bluff enters the battlefield, choose a color other than red.
+        this.addAbility(new AsEntersBattlefieldAbility(new ChooseColorEffect(Outcome.Neutral, "Red")));
+
+        // {T}: Add {R} or one mana of the chosen color.
+        this.addAbility(new RedManaAbility());
+        this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, new AddManaChosenColorEffect(), new TapSourceCost()));
+    }
+
+    private ThrivingBluff(final ThrivingBluff card) {
+        super(card);
+    }
+
+    @Override
+    public ThrivingBluff copy() {
+        return new ThrivingBluff(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index e1c98b2861..f16ea7b4d0 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -71,6 +71,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Swarm of Bloodflies", 282, Rarity.UNCOMMON, mage.cards.s.SwarmOfBloodflies.class));
         cards.add(new SetCardInfo("Tempting Witch", 283, Rarity.UNCOMMON, mage.cards.t.TemptingWitch.class));
         cards.add(new SetCardInfo("Terramorphic Expanse", 78, Rarity.COMMON, mage.cards.t.TerramorphicExpanse.class));
+        cards.add(new SetCardInfo("Thriving Bluff", 33, Rarity.COMMON, mage.cards.t.ThrivingBluff.class));
         cards.add(new SetCardInfo("Thriving Heath", 35, Rarity.COMMON, mage.cards.t.ThrivingHeath.class));
         cards.add(new SetCardInfo("Thriving Isle", 36, Rarity.COMMON, mage.cards.t.ThrivingIsle.class));
         cards.add(new SetCardInfo("Thriving Moor", 37, Rarity.COMMON, mage.cards.t.ThrivingMoor.class));

From 1ff92cbfea4104d2b5b2235b2b6da415ea07f30f Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Wed, 17 Jun 2020 21:01:57 -0400
Subject: [PATCH 356/586] Implemented Thriving Grove

---
 Mage.Sets/src/mage/cards/t/ThrivingGrove.java | 45 +++++++++++++++++++
 Mage.Sets/src/mage/sets/Jumpstart.java        |  1 +
 2 files changed, 46 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/t/ThrivingGrove.java

diff --git a/Mage.Sets/src/mage/cards/t/ThrivingGrove.java b/Mage.Sets/src/mage/cards/t/ThrivingGrove.java
new file mode 100644
index 0000000000..6b11f3196e
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/t/ThrivingGrove.java
@@ -0,0 +1,45 @@
+package mage.cards.t;
+
+import mage.abilities.common.AsEntersBattlefieldAbility;
+import mage.abilities.common.EntersBattlefieldTappedAbility;
+import mage.abilities.costs.common.TapSourceCost;
+import mage.abilities.effects.common.ChooseColorEffect;
+import mage.abilities.effects.mana.AddManaChosenColorEffect;
+import mage.abilities.mana.GreenManaAbility;
+import mage.abilities.mana.SimpleManaAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Outcome;
+import mage.constants.Zone;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class ThrivingGrove extends CardImpl {
+
+    public ThrivingGrove(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.LAND}, "");
+
+        // Thriving Grove enters the battlefield tapped.
+        this.addAbility(new EntersBattlefieldTappedAbility());
+
+        // As Thriving Grove enters the battlefield, choose a color other than green.
+        this.addAbility(new AsEntersBattlefieldAbility(new ChooseColorEffect(Outcome.Neutral, "Green")));
+
+        // {T}: Add {G} or one mana of the chosen color.
+        this.addAbility(new GreenManaAbility());
+        this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, new AddManaChosenColorEffect(), new TapSourceCost()));
+    }
+
+    private ThrivingGrove(final ThrivingGrove card) {
+        super(card);
+    }
+
+    @Override
+    public ThrivingGrove copy() {
+        return new ThrivingGrove(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index f16ea7b4d0..ce2c7fe640 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -72,6 +72,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Tempting Witch", 283, Rarity.UNCOMMON, mage.cards.t.TemptingWitch.class));
         cards.add(new SetCardInfo("Terramorphic Expanse", 78, Rarity.COMMON, mage.cards.t.TerramorphicExpanse.class));
         cards.add(new SetCardInfo("Thriving Bluff", 33, Rarity.COMMON, mage.cards.t.ThrivingBluff.class));
+        cards.add(new SetCardInfo("Thriving Grove", 34, Rarity.COMMON, mage.cards.t.ThrivingGrove.class));
         cards.add(new SetCardInfo("Thriving Heath", 35, Rarity.COMMON, mage.cards.t.ThrivingHeath.class));
         cards.add(new SetCardInfo("Thriving Isle", 36, Rarity.COMMON, mage.cards.t.ThrivingIsle.class));
         cards.add(new SetCardInfo("Thriving Moor", 37, Rarity.COMMON, mage.cards.t.ThrivingMoor.class));

From dcae6f61e46824b0fd1d54f34dff7be8ea4bae76 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Wed, 17 Jun 2020 21:03:28 -0400
Subject: [PATCH 357/586] Implemented Release the Dogs

---
 .../src/mage/cards/r/ReleaseTheDogs.java      | 31 +++++++++++++++++++
 Mage.Sets/src/mage/sets/Jumpstart.java        |  1 +
 2 files changed, 32 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/r/ReleaseTheDogs.java

diff --git a/Mage.Sets/src/mage/cards/r/ReleaseTheDogs.java b/Mage.Sets/src/mage/cards/r/ReleaseTheDogs.java
new file mode 100644
index 0000000000..6e7e8dc8b9
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/r/ReleaseTheDogs.java
@@ -0,0 +1,31 @@
+package mage.cards.r;
+
+import mage.abilities.effects.common.CreateTokenEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.game.permanent.token.WhiteDogToken;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class ReleaseTheDogs extends CardImpl {
+
+    public ReleaseTheDogs(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{W}");
+
+        // Create four 1/1 white Dog creature tokens.
+        this.getSpellAbility().addEffect(new CreateTokenEffect(new WhiteDogToken(), 4));
+    }
+
+    private ReleaseTheDogs(final ReleaseTheDogs card) {
+        super(card);
+    }
+
+    @Override
+    public ReleaseTheDogs copy() {
+        return new ReleaseTheDogs(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index ce2c7fe640..ad02a8e2cc 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -63,6 +63,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Pyroclastic Elemental", 356, Rarity.UNCOMMON, mage.cards.p.PyroclasticElemental.class));
         cards.add(new SetCardInfo("Raging Regisaur", 455, Rarity.UNCOMMON, mage.cards.r.RagingRegisaur.class));
         cards.add(new SetCardInfo("Reanimate", 270, Rarity.RARE, mage.cards.r.Reanimate.class));
+        cards.add(new SetCardInfo("Release the Dogs", 4, Rarity.UNCOMMON, mage.cards.r.ReleaseTheDogs.class));
         cards.add(new SetCardInfo("Rupture Spire", 495, Rarity.COMMON, mage.cards.r.RuptureSpire.class));
         cards.add(new SetCardInfo("Settle the Score", 276, Rarity.UNCOMMON, mage.cards.s.SettleTheScore.class));
         cards.add(new SetCardInfo("Shambling Goblin", 277, Rarity.COMMON, mage.cards.s.ShamblingGoblin.class));

From abf19b5ce3f59c508548e523bb8d206394f096bb Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Wed, 17 Jun 2020 21:11:17 -0400
Subject: [PATCH 358/586] Implemented Kels, Fight Fixer

---
 .../src/mage/cards/k/KelsFightFixer.java      | 94 +++++++++++++++++++
 Mage.Sets/src/mage/sets/Jumpstart.java        |  1 +
 2 files changed, 95 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/k/KelsFightFixer.java

diff --git a/Mage.Sets/src/mage/cards/k/KelsFightFixer.java b/Mage.Sets/src/mage/cards/k/KelsFightFixer.java
new file mode 100644
index 0000000000..3762736bc8
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/k/KelsFightFixer.java
@@ -0,0 +1,94 @@
+package mage.cards.k;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.TriggeredAbilityImpl;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.common.SacrificeTargetCost;
+import mage.abilities.costs.mana.GenericManaCost;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.effects.common.DoIfCostPaid;
+import mage.abilities.effects.common.DrawCardSourceControllerEffect;
+import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
+import mage.abilities.keyword.IndestructibleAbility;
+import mage.abilities.keyword.MenaceAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.*;
+import mage.filter.StaticFilters;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+import mage.target.common.TargetControlledPermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class KelsFightFixer extends CardImpl {
+
+    public KelsFightFixer(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{B}");
+
+        this.addSuperType(SuperType.LEGENDARY);
+        this.subtype.add(SubType.AZRA);
+        this.subtype.add(SubType.WARLOCK);
+        this.power = new MageInt(4);
+        this.toughness = new MageInt(3);
+
+        // Menace
+        this.addAbility(new MenaceAbility());
+
+        // Whenever you sacrifice a creature, you may pay {U/B}. If you do, draw a card.
+        this.addAbility(new KelsFightFixerTriggeredAbility());
+
+        // {1}, Sacrifice a creature: Kels, Fight Fixer gains indestructible until end of turn.
+        Ability ability = new SimpleActivatedAbility(new GainAbilitySourceEffect(
+                IndestructibleAbility.getInstance(), Duration.EndOfTurn
+        ), new GenericManaCost(1));
+        ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT)));
+        this.addAbility(ability);
+    }
+
+    private KelsFightFixer(final KelsFightFixer card) {
+        super(card);
+    }
+
+    @Override
+    public KelsFightFixer copy() {
+        return new KelsFightFixer(this);
+    }
+}
+
+class KelsFightFixerTriggeredAbility extends TriggeredAbilityImpl {
+
+    KelsFightFixerTriggeredAbility() {
+        super(Zone.BATTLEFIELD, new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new ManaCostsImpl<>("{U/B}")), false);
+        setLeavesTheBattlefieldTrigger(true);
+    }
+
+    private KelsFightFixerTriggeredAbility(final KelsFightFixerTriggeredAbility ability) {
+        super(ability);
+    }
+
+    @Override
+    public KelsFightFixerTriggeredAbility copy() {
+        return new KelsFightFixerTriggeredAbility(this);
+    }
+
+    @Override
+    public boolean checkEventType(GameEvent event, Game game) {
+        return event.getType() == GameEvent.EventType.SACRIFICED_PERMANENT;
+    }
+
+    @Override
+    public boolean checkTrigger(GameEvent event, Game game) {
+        return event.getPlayerId().equals(this.getControllerId())
+                && game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD).isCreature();
+    }
+
+    @Override
+    public String getRule() {
+        return "Whenever you sacrifice a creature, " + super.getRule();
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index ad02a8e2cc..9fa4085321 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -49,6 +49,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Innocent Blood", 244, Rarity.COMMON, mage.cards.i.InnocentBlood.class));
         cards.add(new SetCardInfo("Ironroot Warlord", 452, Rarity.UNCOMMON, mage.cards.i.IronrootWarlord.class));
         cards.add(new SetCardInfo("Isamaru, Hound of Konda", 113, Rarity.RARE, mage.cards.i.IsamaruHoundOfKonda.class));
+        cards.add(new SetCardInfo("Kels, Fight Fixer", 15, Rarity.RARE, mage.cards.k.KelsFightFixer.class));
         cards.add(new SetCardInfo("Knight of the Tusk", 114, Rarity.COMMON, mage.cards.k.KnightOfTheTusk.class));
         cards.add(new SetCardInfo("Last Gasp", 247, Rarity.COMMON, mage.cards.l.LastGasp.class));
         cards.add(new SetCardInfo("Lawmage's Binding", 453, Rarity.COMMON, mage.cards.l.LawmagesBinding.class));

From cba0094c207c12d9a80a0351a8b4374d2b4c39bc Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Wed, 17 Jun 2020 21:17:20 -0400
Subject: [PATCH 359/586] Implemented Nocturnal Feeder

---
 .../src/mage/cards/n/NocturnalFeeder.java     | 46 +++++++++++++++++++
 Mage.Sets/src/mage/sets/Jumpstart.java        |  1 +
 2 files changed, 47 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/n/NocturnalFeeder.java

diff --git a/Mage.Sets/src/mage/cards/n/NocturnalFeeder.java b/Mage.Sets/src/mage/cards/n/NocturnalFeeder.java
new file mode 100644
index 0000000000..2415335428
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/n/NocturnalFeeder.java
@@ -0,0 +1,46 @@
+package mage.cards.n;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.effects.common.GainLifeEffect;
+import mage.abilities.effects.common.LoseLifeOpponentsEffect;
+import mage.abilities.keyword.FlyingAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class NocturnalFeeder extends CardImpl {
+
+    public NocturnalFeeder(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}");
+
+        this.subtype.add(SubType.VAMPIRE);
+        this.subtype.add(SubType.ROGUE);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(1);
+
+        // Flying
+        this.addAbility(FlyingAbility.getInstance());
+
+        // When Nocturnal Feeder dies, each opponent loses 2 life and you gain 2 life.
+        Ability ability = new DiesTriggeredAbility(new LoseLifeOpponentsEffect(2));
+        ability.addEffect(new GainLifeEffect(2).concatBy("and"));
+        this.addAbility(ability);
+    }
+
+    private NocturnalFeeder(final NocturnalFeeder card) {
+        super(card);
+    }
+
+    @Override
+    public NocturnalFeeder copy() {
+        return new NocturnalFeeder(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index 9fa4085321..d3484dd77d 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -57,6 +57,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Maelstrom Archangel", 454, Rarity.MYTHIC, mage.cards.m.MaelstromArchangel.class));
         cards.add(new SetCardInfo("Mire Triton", 257, Rarity.UNCOMMON, mage.cards.m.MireTriton.class));
         cards.add(new SetCardInfo("Mirrodin's Core", 492, Rarity.UNCOMMON, mage.cards.m.MirrodinsCore.class));
+        cards.add(new SetCardInfo("Nocturnal Feeder", 16, Rarity.COMMON, mage.cards.n.NocturnalFeeder.class));
         cards.add(new SetCardInfo("Nyxathid", 259, Rarity.RARE, mage.cards.n.Nyxathid.class));
         cards.add(new SetCardInfo("Pillar of Flame", 355, Rarity.RARE, mage.cards.p.PillarOfFlame.class));
         cards.add(new SetCardInfo("Plains", 45, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));

From 5801e91f4a2b6ca77bb5e4bedb7a9e85133f9772 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Wed, 17 Jun 2020 21:30:22 -0400
Subject: [PATCH 360/586] fixed a few errors

---
 Mage.Sets/src/mage/cards/t/Transmogrify.java        | 2 +-
 Mage.Sets/src/mage/cards/w/WatcherOfTheSpheres.java | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/t/Transmogrify.java b/Mage.Sets/src/mage/cards/t/Transmogrify.java
index c21b545bb0..1d338a60eb 100644
--- a/Mage.Sets/src/mage/cards/t/Transmogrify.java
+++ b/Mage.Sets/src/mage/cards/t/Transmogrify.java
@@ -46,7 +46,7 @@ class TransmogrifyEffect extends OneShotEffect {
 
     public TransmogrifyEffect() {
         super(Outcome.PutCreatureInPlay);
-        staticText = "That creature’s controller reveals cards from the top of their library until they reveal a creature card. That player puts that card onto the battlefield, then shuffles the rest into their library";
+        staticText = "That creature's controller reveals cards from the top of their library until they reveal a creature card. That player puts that card onto the battlefield, then shuffles the rest into their library";
     }
 
     public TransmogrifyEffect(final TransmogrifyEffect effect) {
diff --git a/Mage.Sets/src/mage/cards/w/WatcherOfTheSpheres.java b/Mage.Sets/src/mage/cards/w/WatcherOfTheSpheres.java
index 461db88742..2e23709dbf 100644
--- a/Mage.Sets/src/mage/cards/w/WatcherOfTheSpheres.java
+++ b/Mage.Sets/src/mage/cards/w/WatcherOfTheSpheres.java
@@ -36,7 +36,7 @@ public final class WatcherOfTheSpheres extends CardImpl {
     }
 
     public WatcherOfTheSpheres(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{B}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{U}");
 
         this.subtype.add(SubType.BIRD);
         this.subtype.add(SubType.WIZARD);

From 1724740cc640f9e3797f9e8e35964efeba11c360 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Thu, 18 Jun 2020 18:06:53 +0400
Subject: [PATCH 361/586] XML security (fixes #6662);

---
 .../cards/decks/importer/XmlDeckImporter.java | 90 +++++++++++--------
 1 file changed, 54 insertions(+), 36 deletions(-)

diff --git a/Mage/src/main/java/mage/cards/decks/importer/XmlDeckImporter.java b/Mage/src/main/java/mage/cards/decks/importer/XmlDeckImporter.java
index 21aef12f49..01e1d81862 100644
--- a/Mage/src/main/java/mage/cards/decks/importer/XmlDeckImporter.java
+++ b/Mage/src/main/java/mage/cards/decks/importer/XmlDeckImporter.java
@@ -1,52 +1,70 @@
 package mage.cards.decks.importer;
 
-import static javax.xml.xpath.XPathConstants.NODESET;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.xpath.XPathExpressionException;
-import javax.xml.xpath.XPathFactory;
-
 import org.w3c.dom.Document;
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 import org.xml.sax.SAXException;
 
+import javax.xml.XMLConstants;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import static javax.xml.xpath.XPathConstants.NODESET;
+
 public abstract class XmlDeckImporter extends DeckImporter {
 
-  private XPathFactory xpathFactory = XPathFactory.newInstance();
-  private DocumentBuilder builder = getDocumentBuilder();
+    private final XPathFactory xpathFactory = XPathFactory.newInstance();
+    private final DocumentBuilder builder = getDocumentBuilder();
 
-  protected List<Node> getNodes(Document doc, String xpathExpression) throws XPathExpressionException {
-    NodeList nodes =  (NodeList) xpathFactory.newXPath().evaluate(xpathExpression, doc, NODESET);
-    List<Node> list = new ArrayList<>();
-    for (int i = 0; i < nodes.getLength(); i++) {
-      list.add(nodes.item(i));
+    protected List<Node> getNodes(Document doc, String xpathExpression) throws XPathExpressionException {
+        NodeList nodes = (NodeList) xpathFactory.newXPath().evaluate(xpathExpression, doc, NODESET);
+        List<Node> list = new ArrayList<>();
+        for (int i = 0; i < nodes.getLength(); i++) {
+            list.add(nodes.item(i));
+        }
+        return list;
     }
-    return list;
-  }
 
-  private DocumentBuilder getDocumentBuilder() {
-    try {
-      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
-      return factory.newDocumentBuilder();
-    } catch (ParserConfigurationException e) {
-      throw new RuntimeException();
-    }
-  }
+    private DocumentBuilder getDocumentBuilder() {
+        try {
+            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
 
-  protected Document getXmlDocument(String filename) throws IOException {
-    try {
-      return builder.parse(new File(filename));
-    } catch (SAXException e) {
-      throw new IOException(e);
+            // security fix from https://stackoverflow.com/a/59736162/1276632 to disable external data download
+            // REDHAT
+            // https://www.blackhat.com/docs/us-15/materials/us-15-Wang-FileCry-The-New-Age-Of-XXE-java-wp.pdf
+            factory.setAttribute(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+            factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
+            factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
+            // OWASP
+            // https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html
+            factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
+            factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
+            factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
+            // Disable external DTDs as well
+            factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
+            // and these as well, per Timothy Morgan's 2014 paper: "XML Schema, DTD, and Entity Attacks"
+            factory.setXIncludeAware(false);
+            factory.setExpandEntityReferences(false);
+
+            return factory.newDocumentBuilder();
+        } catch (ParserConfigurationException e) {
+            throw new RuntimeException();
+        }
+    }
+
+    protected Document getXmlDocument(String filename) throws IOException {
+        try {
+            return builder.parse(new File(filename));
+        } catch (SAXException e) {
+            throw new IOException(e);
+        }
     }
-  }
 
 }

From 3264191bd5a744031fe030b819f445a83c963028 Mon Sep 17 00:00:00 2001
From: arcox <10953229+arcox@users.noreply.github.com>
Date: Thu, 18 Jun 2020 14:19:33 +0000
Subject: [PATCH 362/586] Fix issue #6659 - Choking Vines not blocking properly
 (#6661)

---
 Mage.Sets/src/mage/cards/c/ChokingVines.java | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/c/ChokingVines.java b/Mage.Sets/src/mage/cards/c/ChokingVines.java
index 285e78f3b1..91ab935276 100644
--- a/Mage.Sets/src/mage/cards/c/ChokingVines.java
+++ b/Mage.Sets/src/mage/cards/c/ChokingVines.java
@@ -79,11 +79,14 @@ class ChokingVinesEffect extends OneShotEffect {
     public boolean apply(Game game, Ability source) {
         Player controller = game.getPlayer(source.getControllerId());
         Targets targets = source.getTargets();
+
         if (controller != null && !targets.isEmpty()) {
             for (Target target : targets) {
-                CombatGroup combatGroup = game.getCombat().findGroup(target.getFirstTarget());
-                if (combatGroup != null) {
-                    combatGroup.setBlocked(true, game);
+                for (UUID id : target.getTargets()) {
+                    CombatGroup combatGroup = game.getCombat().findGroup(id);
+                    if (combatGroup != null) {
+                        combatGroup.setBlocked(true, game);
+                    }
                 }
             }
             return true;

From 89db297224b94b2cbde921b0edf65cd5309cf498 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 18 Jun 2020 09:57:39 -0400
Subject: [PATCH 363/586] updated JMP spoiler and reprints

---
 Mage.Sets/src/mage/sets/Jumpstart.java | 59 ++++++++++++++++++++++-
 Utils/mtg-cards-data.txt               | 65 +++++++++++++++++++++++++-
 2 files changed, 120 insertions(+), 4 deletions(-)

diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index d3484dd77d..e98a94b816 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -20,66 +20,121 @@ public final class Jumpstart extends ExpansionSet {
         this.blockName = "Jumpstart";
         this.hasBasicLands = true;
 
+        cards.add(new SetCardInfo("Aether Spellbomb", 456, Rarity.COMMON, mage.cards.a.AetherSpellbomb.class));
         cards.add(new SetCardInfo("Affectionate Indrik", 373, Rarity.UNCOMMON, mage.cards.a.AffectionateIndrik.class));
+        cards.add(new SetCardInfo("Aggressive Urge", 374, Rarity.COMMON, mage.cards.a.AggressiveUrge.class));
+        cards.add(new SetCardInfo("Agonizing Syphon", 199, Rarity.COMMON, mage.cards.a.AgonizingSyphon.class));
         cards.add(new SetCardInfo("Alloy Myr", 457, Rarity.COMMON, mage.cards.a.AlloyMyr.class));
+        cards.add(new SetCardInfo("Ambassador Oak", 375, Rarity.COMMON, mage.cards.a.AmbassadorOak.class));
+        cards.add(new SetCardInfo("Ancestral Statue", 458, Rarity.COMMON, mage.cards.a.AncestralStatue.class));
         cards.add(new SetCardInfo("Auger Spree", 449, Rarity.COMMON, mage.cards.a.AugerSpree.class));
+        cards.add(new SetCardInfo("Awakener Druid", 379, Rarity.UNCOMMON, mage.cards.a.AwakenerDruid.class));
         cards.add(new SetCardInfo("Bake into a Pie", 201, Rarity.COMMON, mage.cards.b.BakeIntoAPie.class));
+        cards.add(new SetCardInfo("Befuddle", 140, Rarity.COMMON, mage.cards.b.Befuddle.class));
         cards.add(new SetCardInfo("Black Cat", 203, Rarity.COMMON, mage.cards.b.BlackCat.class));
         cards.add(new SetCardInfo("Blighted Bat", 205, Rarity.COMMON, mage.cards.b.BlightedBat.class));
+        cards.add(new SetCardInfo("Blood Artist", 206, Rarity.UNCOMMON, mage.cards.b.BloodArtist.class));
         cards.add(new SetCardInfo("Blood Divination", 207, Rarity.UNCOMMON, mage.cards.b.BloodDivination.class));
+        cards.add(new SetCardInfo("Blood Host", 208, Rarity.UNCOMMON, mage.cards.b.BloodHost.class));
+        cards.add(new SetCardInfo("Bloodbond Vampire", 209, Rarity.UNCOMMON, mage.cards.b.BloodbondVampire.class));
         cards.add(new SetCardInfo("Bloodhunter Bat", 210, Rarity.COMMON, mage.cards.b.BloodhunterBat.class));
         cards.add(new SetCardInfo("Bogbrew Witch", 211, Rarity.RARE, mage.cards.b.BogbrewWitch.class));
         cards.add(new SetCardInfo("Bone Picker", 212, Rarity.UNCOMMON, mage.cards.b.BonePicker.class));
         cards.add(new SetCardInfo("Brushstrider", 381, Rarity.UNCOMMON, mage.cards.b.Brushstrider.class));
         cards.add(new SetCardInfo("Bubbling Cauldron", 460, Rarity.UNCOMMON, mage.cards.b.BubblingCauldron.class));
+        cards.add(new SetCardInfo("Buried Ruin", 491, Rarity.UNCOMMON, mage.cards.b.BuriedRuin.class));
         cards.add(new SetCardInfo("Cemetery Recruitment", 217, Rarity.COMMON, mage.cards.c.CemeteryRecruitment.class));
         cards.add(new SetCardInfo("Chamber Sentry", 461, Rarity.RARE, mage.cards.c.ChamberSentry.class));
+        cards.add(new SetCardInfo("Child of Night", 218, Rarity.COMMON, mage.cards.c.ChildOfNight.class));
+        cards.add(new SetCardInfo("Chromatic Sphere", 462, Rarity.COMMON, mage.cards.c.ChromaticSphere.class));
+        cards.add(new SetCardInfo("Crookclaw Transmuter", 145, Rarity.COMMON, mage.cards.c.CrookclawTransmuter.class));
+        cards.add(new SetCardInfo("Crushing Canopy", 386, Rarity.COMMON, mage.cards.c.CrushingCanopy.class));
         cards.add(new SetCardInfo("Dinrova Horror", 450, Rarity.UNCOMMON, mage.cards.d.DinrovaHorror.class));
+        cards.add(new SetCardInfo("Drana, Liberator of Malakir", 225, Rarity.MYTHIC, mage.cards.d.DranaLiberatorOfMalakir.class));
         cards.add(new SetCardInfo("Dutiful Attendant", 226, Rarity.COMMON, mage.cards.d.DutifulAttendant.class));
         cards.add(new SetCardInfo("Entomber Exarch", 227, Rarity.UNCOMMON, mage.cards.e.EntomberExarch.class));
+        cards.add(new SetCardInfo("Erratic Visionary", 150, Rarity.COMMON, mage.cards.e.ErraticVisionary.class));
+        cards.add(new SetCardInfo("Eternal Thirst", 229, Rarity.COMMON, mage.cards.e.EternalThirst.class));
+        cards.add(new SetCardInfo("Exclusion Mage", 153, Rarity.UNCOMMON, mage.cards.e.ExclusionMage.class));
+        cards.add(new SetCardInfo("Explore", 393, Rarity.COMMON, mage.cards.e.Explore.class));
+        cards.add(new SetCardInfo("Exquisite Blood", 231, Rarity.RARE, mage.cards.e.ExquisiteBlood.class));
+        cards.add(new SetCardInfo("Falkenrath Noble", 232, Rarity.UNCOMMON, mage.cards.f.FalkenrathNoble.class));
         cards.add(new SetCardInfo("Fanatical Firebrand", 315, Rarity.COMMON, mage.cards.f.FanaticalFirebrand.class));
+        cards.add(new SetCardInfo("Feral Hydra", 395, Rarity.UNCOMMON, mage.cards.f.FeralHydra.class));
+        cards.add(new SetCardInfo("Fertilid", 398, Rarity.COMMON, mage.cards.f.Fertilid.class));
         cards.add(new SetCardInfo("Festering Newt", 234, Rarity.COMMON, mage.cards.f.FesteringNewt.class));
         cards.add(new SetCardInfo("Flames of the Firebrand", 357, Rarity.UNCOMMON, mage.cards.f.FlamesOfTheFirebrand.class));
-        cards.add(new SetCardInfo("Forest", 76, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Forest", 70, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Fusion Elemental", 451, Rarity.UNCOMMON, mage.cards.f.FusionElemental.class));
         cards.add(new SetCardInfo("Ghoulcaller's Accomplice", 237, Rarity.COMMON, mage.cards.g.GhoulcallersAccomplice.class));
         cards.add(new SetCardInfo("Ghoulraiser", 238, Rarity.COMMON, mage.cards.g.Ghoulraiser.class));
         cards.add(new SetCardInfo("Gravewaker", 241, Rarity.RARE, mage.cards.g.Gravewaker.class));
-        cards.add(new SetCardInfo("Hungry Flames", 336, Rarity.UNCOMMON, mage.cards.h.HungryFlames.class));
+        cards.add(new SetCardInfo("Hedron Archive", 468, Rarity.UNCOMMON, mage.cards.h.HedronArchive.class));
+        cards.add(new SetCardInfo("Hungry Flames", 336, Rarity.COMMON, mage.cards.h.HungryFlames.class));
         cards.add(new SetCardInfo("Innocent Blood", 244, Rarity.COMMON, mage.cards.i.InnocentBlood.class));
         cards.add(new SetCardInfo("Ironroot Warlord", 452, Rarity.UNCOMMON, mage.cards.i.IronrootWarlord.class));
         cards.add(new SetCardInfo("Isamaru, Hound of Konda", 113, Rarity.RARE, mage.cards.i.IsamaruHoundOfKonda.class));
+        cards.add(new SetCardInfo("Island", 49, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Juggernaut", 471, Rarity.UNCOMMON, mage.cards.j.Juggernaut.class));
+        cards.add(new SetCardInfo("Kalastria Nightwatch", 245, Rarity.COMMON, mage.cards.k.KalastriaNightwatch.class));
         cards.add(new SetCardInfo("Kels, Fight Fixer", 15, Rarity.RARE, mage.cards.k.KelsFightFixer.class));
         cards.add(new SetCardInfo("Knight of the Tusk", 114, Rarity.COMMON, mage.cards.k.KnightOfTheTusk.class));
         cards.add(new SetCardInfo("Last Gasp", 247, Rarity.COMMON, mage.cards.l.LastGasp.class));
         cards.add(new SetCardInfo("Lawmage's Binding", 453, Rarity.COMMON, mage.cards.l.LawmagesBinding.class));
         cards.add(new SetCardInfo("Liliana's Elite", 250, Rarity.UNCOMMON, mage.cards.l.LilianasElite.class));
         cards.add(new SetCardInfo("Maelstrom Archangel", 454, Rarity.MYTHIC, mage.cards.m.MaelstromArchangel.class));
+        cards.add(new SetCardInfo("Mark of the Vampire", 254, Rarity.COMMON, mage.cards.m.MarkOfTheVampire.class));
+        cards.add(new SetCardInfo("Meteor Golem", 474, Rarity.UNCOMMON, mage.cards.m.MeteorGolem.class));
         cards.add(new SetCardInfo("Mire Triton", 257, Rarity.UNCOMMON, mage.cards.m.MireTriton.class));
         cards.add(new SetCardInfo("Mirrodin's Core", 492, Rarity.UNCOMMON, mage.cards.m.MirrodinsCore.class));
+        cards.add(new SetCardInfo("Nature's Way", 412, Rarity.UNCOMMON, mage.cards.n.NaturesWay.class));
         cards.add(new SetCardInfo("Nocturnal Feeder", 16, Rarity.COMMON, mage.cards.n.NocturnalFeeder.class));
         cards.add(new SetCardInfo("Nyxathid", 259, Rarity.RARE, mage.cards.n.Nyxathid.class));
+        cards.add(new SetCardInfo("Oracle of Mul Daya", 415, Rarity.RARE, mage.cards.o.OracleOfMulDaya.class));
+        cards.add(new SetCardInfo("Peel from Reality", 163, Rarity.COMMON, mage.cards.p.PeelFromReality.class));
         cards.add(new SetCardInfo("Pillar of Flame", 355, Rarity.RARE, mage.cards.p.PillarOfFlame.class));
         cards.add(new SetCardInfo("Plains", 45, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Primordial Sage", 422, Rarity.RARE, mage.cards.p.PrimordialSage.class));
         cards.add(new SetCardInfo("Prophetic Prism", 478, Rarity.COMMON, mage.cards.p.PropheticPrism.class));
         cards.add(new SetCardInfo("Pyroclastic Elemental", 356, Rarity.UNCOMMON, mage.cards.p.PyroclasticElemental.class));
         cards.add(new SetCardInfo("Raging Regisaur", 455, Rarity.UNCOMMON, mage.cards.r.RagingRegisaur.class));
         cards.add(new SetCardInfo("Reanimate", 270, Rarity.RARE, mage.cards.r.Reanimate.class));
         cards.add(new SetCardInfo("Release the Dogs", 4, Rarity.UNCOMMON, mage.cards.r.ReleaseTheDogs.class));
+        cards.add(new SetCardInfo("Riptide Laboratory", 494, Rarity.RARE, mage.cards.r.RiptideLaboratory.class));
         cards.add(new SetCardInfo("Rupture Spire", 495, Rarity.COMMON, mage.cards.r.RuptureSpire.class));
+        cards.add(new SetCardInfo("Sage's Row Savant", 171, Rarity.COMMON, mage.cards.s.SagesRowSavant.class));
+        cards.add(new SetCardInfo("Sangromancer", 272, Rarity.RARE, mage.cards.s.Sangromancer.class));
+        cards.add(new SetCardInfo("Sea Gate Oracle", 173, Rarity.COMMON, mage.cards.s.SeaGateOracle.class));
+        cards.add(new SetCardInfo("Sengir Vampire", 275, Rarity.UNCOMMON, mage.cards.s.SengirVampire.class));
         cards.add(new SetCardInfo("Settle the Score", 276, Rarity.UNCOMMON, mage.cards.s.SettleTheScore.class));
         cards.add(new SetCardInfo("Shambling Goblin", 277, Rarity.COMMON, mage.cards.s.ShamblingGoblin.class));
         cards.add(new SetCardInfo("Skittering Surveyor", 486, Rarity.COMMON, mage.cards.s.SkitteringSurveyor.class));
+        cards.add(new SetCardInfo("Soul of the Harvest", 432, Rarity.RARE, mage.cards.s.SoulOfTheHarvest.class));
+        cards.add(new SetCardInfo("Sporemound", 433, Rarity.COMMON, mage.cards.s.Sporemound.class));
+        cards.add(new SetCardInfo("Storm Sculptor", 179, Rarity.COMMON, mage.cards.s.StormSculptor.class));
         cards.add(new SetCardInfo("Swamp", 54, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Swarm of Bloodflies", 282, Rarity.UNCOMMON, mage.cards.s.SwarmOfBloodflies.class));
+        cards.add(new SetCardInfo("Sylvan Ranger", 435, Rarity.COMMON, mage.cards.s.SylvanRanger.class));
+        cards.add(new SetCardInfo("Talrand's Invocation", 182, Rarity.UNCOMMON, mage.cards.t.TalrandsInvocation.class));
+        cards.add(new SetCardInfo("Talrand, Sky Summoner", 181, Rarity.RARE, mage.cards.t.TalrandSkySummoner.class));
         cards.add(new SetCardInfo("Tempting Witch", 283, Rarity.UNCOMMON, mage.cards.t.TemptingWitch.class));
         cards.add(new SetCardInfo("Terramorphic Expanse", 78, Rarity.COMMON, mage.cards.t.TerramorphicExpanse.class));
+        cards.add(new SetCardInfo("Thirst for Knowledge", 183, Rarity.UNCOMMON, mage.cards.t.ThirstForKnowledge.class));
         cards.add(new SetCardInfo("Thriving Bluff", 33, Rarity.COMMON, mage.cards.t.ThrivingBluff.class));
         cards.add(new SetCardInfo("Thriving Grove", 34, Rarity.COMMON, mage.cards.t.ThrivingGrove.class));
         cards.add(new SetCardInfo("Thriving Heath", 35, Rarity.COMMON, mage.cards.t.ThrivingHeath.class));
         cards.add(new SetCardInfo("Thriving Isle", 36, Rarity.COMMON, mage.cards.t.ThrivingIsle.class));
         cards.add(new SetCardInfo("Thriving Moor", 37, Rarity.COMMON, mage.cards.t.ThrivingMoor.class));
+        cards.add(new SetCardInfo("Ulvenwald Hydra", 439, Rarity.MYTHIC, mage.cards.u.UlvenwaldHydra.class));
+        cards.add(new SetCardInfo("Vampire Neonate", 285, Rarity.COMMON, mage.cards.v.VampireNeonate.class));
+        cards.add(new SetCardInfo("Vastwood Zendikon", 440, Rarity.COMMON, mage.cards.v.VastwoodZendikon.class));
+        cards.add(new SetCardInfo("Verdant Embrace", 441, Rarity.RARE, mage.cards.v.VerdantEmbrace.class));
         cards.add(new SetCardInfo("Wailing Ghoul", 286, Rarity.COMMON, mage.cards.w.WailingGhoul.class));
+        cards.add(new SetCardInfo("Wall of Blossoms", 442, Rarity.UNCOMMON, mage.cards.w.WallOfBlossoms.class));
+        cards.add(new SetCardInfo("Winged Words", 196, Rarity.COMMON, mage.cards.w.WingedWords.class));
+        cards.add(new SetCardInfo("Wizard's Retort", 198, Rarity.UNCOMMON, mage.cards.w.WizardsRetort.class));
+        cards.add(new SetCardInfo("Woodborn Behemoth", 446, Rarity.UNCOMMON, mage.cards.w.WoodbornBehemoth.class));
         cards.add(new SetCardInfo("Young Pyromancer", 372, Rarity.UNCOMMON, mage.cards.y.YoungPyromancer.class));
+        cards.add(new SetCardInfo("Zendikar's Roil", 448, Rarity.UNCOMMON, mage.cards.z.ZendikarsRoil.class));
     }
 }
diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt
index 4e0002a837..211c0cd359 100644
--- a/Utils/mtg-cards-data.txt
+++ b/Utils/mtg-cards-data.txt
@@ -37726,55 +37726,108 @@ Wildwood Patrol|Core Set 2021|339|C|{2}{G}|Creature - Centaur Scout|4|2|Trample|
 Release the Dogs|Jumpstart|4|U|{3}{W}|Sorcery|||Create four 1/1 white Dog creature tokens.|
 Supply Runners|Jumpstart|7|U|{4}{W}|Creature - Dog|2|2|When Supply Runners enters the battlefield, put a +1/+1 counter on each other creature you control.|
 Trusty Retriever|Jumpstart|8|C|{3}{W}|Creature - Dog|2|3|When Trusty Retriever enters the battlefield, choose one —$• Put a +1/+1 counter on Trusty Retriever.$• Return target artifact or enchantment card from your graveyard to your hand.|
+Archaeomender|Jumpstart|9|C|{2}{U}|Creature - Human Wizard|2|3|When Archaeomender enters the battlefield, return target artifact card from your graveyard to your hand.|
+Scholar of the Lost Trove|Jumpstart|14|R|{5}{U}{U}|Creature - Sphinx|5|5|Flying$When Scholar of the Lost Trove enters the battlefield, you may cast target instant, sorcery, or artifact card from your graveyard without paying its mana cost. If an instant or sorcery spell cast this way would be put into your graveyard this turn, exile it instead.|
 Kels, Fight Fixer|Jumpstart|15|R|{2}{B}{B}|Legendary Creature - Azra Warlock|4|3|Menace$Whenever you sacrifice a creature, you may pay {U/B}. If you do, draw a card.${1}, Sacrifice a creature: Kels, Fight Fixer gains indestructible until end of turn.|
 Nocturnal Feeder|Jumpstart|16|C|{2}{B}|Creature - Vampire Rogue|2|1|Flying$When Nocturnal Feeder dies, each opponent loses 2 life and you gain 2 life.|
 Tinybones, Trinket Thief|Jumpstart|17|M|{1}{B}|Legendary Creature - Skeleton Rogue|1|2|At the beginning of each end step, if an opponent discarded a card this turn, you draw a card and you lose 1 life.${4}{B}{B}: Each opponent with no cards in hand loses 10 life.|
 Witch of the Moors|Jumpstart|18|R|{3}{B}{B}|Creature - Human Warlock|4|4|Deathtouch$At the beginning of your end step, if you gained life this turn, each opponent sacrifices a creature and you return up to one target creature card from your graveyard to your hand.|
+Lightning Phoenix|Jumpstart|21|R|{2}{R}|Creature - Phoenix|2|2|Flying, haste$Lightning Phoenix can't block.$At the beginning of your end step, if an opponent was dealt 3 or more damage this turn, you may pay {R}. If you do, return Lightning Phoenix from your graveyard to the battlefield.|
+Lightning Visionary|Jumpstart|22|C|{1}{R}|Creature - Minotaur Shaman|2|1|Prowess|
+Living Lightning|Jumpstart|23|U|{3}{R}|Creature - Elemental Shaman|3|2|When Living Lightning dies, return target instant or sorcery card from your graveyard to your hand.|
 Neyith of the Dire Hunt|Jumpstart|30|R|{2}{G}{G}|Legendary Creature - Human Warrior|3|3|Whenever one or more creatures you control fight or become blocked, draw a card.$At the beginning of combat on your turn, you may pay {2}{R/G}. If you do, double target creature's power until end of turn. That creature must be blocked this combat if able.|
+Lightning-Core Excavator|Jumpstart|32|C|{1}|Artifact Creature - Golem|0|3|{5}, {T}, Sacrifice Lightning-Core Excavator: It deals 3 damage to any target.|
 Thriving Bluff|Jumpstart|33|C||Land|||Thriving Bluff enters the battlefield tapped.$As Thriving Bluff enters the battlefield, choose a color other than red.${T}: Add {R} or one mana of the chosen color.|
 Thriving Grove|Jumpstart|34|C||Land|||Thriving Grove enters the battlefield tapped.$As Thriving Grove enters the battlefield, choose a color other than green.${T}: Add {G} or one mana of the chosen color.|
 Thriving Heath|Jumpstart|35|C||Land|||Thriving Heath enters the battlefield tapped.$As Thriving Heath enters the battlefield, choose a color other than white.${T}: Add {W} or one mana of the chosen color.|
 Thriving Isle|Jumpstart|36|C||Land|||Thriving Isle enters the battlefield tapped.$As Thriving Isle enters the battlefield, choose a color other than blue.${T}: Add {U} or one mana of the chosen color.|
 Thriving Moor|Jumpstart|37|C||Land|||Thriving Moor enters the battlefield tapped.$As Thriving Moor enters the battlefield, choose a color other than black.${T}: Add {B} or one mana of the chosen color.|
 Plains|Jumpstart|45|C||Basic Land - Plains|||({T}: Add {W}.)|
+Island|Jumpstart|49|C||Basic Land - Island|||({T}: Add {U}.)|
 Swamp|Jumpstart|54|C||Basic Land - Swamp|||({T}: Add {B}.)|
-Forest|Jumpstart|76|C||Basic Land - Forest|||({T}: Add {G}.)|
+Forest|Jumpstart|70|C||Basic Land - Forest|||({T}: Add {G}.)|
 Terramorphic Expanse|Jumpstart|78|C||Land|||{T}, Sacrifice Terramorphic Expanse: Search your library for a basic land card, put it onto the battlefield tapped, then shuffle your library.|
 Isamaru, Hound of Konda|Jumpstart|113|R|{W}|Legendary Creature - Hound|2|2||
 Knight of the Tusk|Jumpstart|114|C|{4}{W}{W}|Creature - Human Knight|3|7|Vigilance|
+Befuddle|Jumpstart|140|C|{2}{U}|Instant|||Target creature gets -4/-0 until end of turn.$Draw a card.|
+Crookclaw Transmuter|Jumpstart|145|C|{3}{U}|Creature - Bird Wizard|3|1|Flash$Flying$When Crookclaw Transmuter enters the battlefield, switch target creature's power and toughness until end of turn.|
+Erratic Visionary|Jumpstart|150|C|{1}{U}|Creature - Human Wizard|1|3|{1}{U}, {T}: Draw a card, then discard a card.|
+Exclusion Mage|Jumpstart|153|U|{2}{U}|Creature - Human Wizard|2|2|When Exclusion Mage enters the battlefield, return target creature an opponent controls to its owner's hand.|
+Peel from Reality|Jumpstart|163|C|{1}{U}|Instant|||Return target creature you control and target creature you don't control to their owners' hands.|
+Sage's Row Savant|Jumpstart|171|C|{1}{U}|Creature - Vedalken Wizard|2|1|When Sage's Row Savant enters the battlefield, scry 2.|
+Sea Gate Oracle|Jumpstart|173|C|{2}{U}|Creature - Human Wizard|1|3|When Sea Gate Oracle enters the battlefield, look at the top two cards of your library. Put one of them into your hand and the other on the bottom of your library.|
+Storm Sculptor|Jumpstart|179|C|{3}{U}|Creature - Merfolk Wizard|3|2|Storm Sculptor can't be blocked.$When Storm Sculptor enters the battlefield, return a creature you control to its owner's hand.|
+Talrand, Sky Summoner|Jumpstart|181|R|{2}{U}{U}|Legendary Creature - Merfolk Wizard|2|2|Whenever you cast an instant or sorcery spell, create a 2/2 blue Drake creature token with flying.|
+Talrand's Invocation|Jumpstart|182|U|{2}{U}{U}|Sorcery|||Create two 2/2 blue Drake creature tokens with flying.|
+Thirst for Knowledge|Jumpstart|183|U|{2}{U}|Instant|||Draw three cards. Then discard two cards unless you discard an artifact card.|
+Winged Words|Jumpstart|196|C|{2}{U}|Sorcery|||This spell costs {1} less to cast if you control a creature with flying.$Draw two cards.|
+Wizard's Retort|Jumpstart|198|U|{1}{U}{U}|Instant|||This spell costs {1} less to cast if you control a Wizard.$Counter target spell.|
+Agonizing Syphon|Jumpstart|199|C|{3}{B}|Sorcery|||Agonizing Syphon deals 3 damage to any target and you gain 3 life.|
 Bake into a Pie|Jumpstart|201|C|{2}{B}{B}|Instant|||Destroy target creature. Create a Food token.|
 Black Cat|Jumpstart|203|C|{1}{B}|Creature - Zombie Cat|1|1|When Black Cat dies, target opponent discards a card at random.|
 Blighted Bat|Jumpstart|205|C|{2}{B}|Creature - Zombie Bat|2|1|Flying${1}: Blighted Bat gains haste until end of turn.|
+Blood Artist|Jumpstart|206|U|{1}{B}|Creature - Vampire|0|1|Whenever Blood Artist or another creature dies, target player loses 1 life and you gain 1 life.|
 Blood Divination|Jumpstart|207|U|{3}{B}|Sorcery|||As an additional cost to cast this spell, sacrifice a creature.$Draw three cards.|
+Blood Host|Jumpstart|208|U|{3}{B}{B}|Creature - Vampire|3|3|{1}{B}, Sacrifice another creature: Put a +1/+1 counter on Blood Host and you gain 2 life.|
+Bloodbond Vampire|Jumpstart|209|U|{2}{B}{B}|Creature - Vampire Shaman Ally|3|3|Whenever you gain life, put a +1/+1 counter on Bloodbond Vampire.|
 Bloodhunter Bat|Jumpstart|210|C|{3}{B}|Creature - Bat|2|2|Flying$When Bloodhunter Bat enters the battlefield, target player loses 2 life and you gain 2 life.|
 Bogbrew Witch|Jumpstart|211|R|{3}{B}|Creature - Human Wizard|1|3|{2}, {T}: Search your library for a card named Festering Newt or Bubbling Cauldron, put it onto the battlefield tapped, then shuffle your library.|
 Bone Picker|Jumpstart|212|U|{3}{B}|Creature - Bird|3|2|This spell costs {3} less to cast if a creature died this turn.$Flying, deathtouch|
 Cemetery Recruitment|Jumpstart|217|C|{1}{B}|Sorcery|||Return target creature card from your graveyard to your hand. If it's a Zombie card, draw a card.|
+Child of Night|Jumpstart|218|C|{1}{B}|Creature - Vampire|2|1|Lifelink|
+Drana, Liberator of Malakir|Jumpstart|225|M|{1}{B}{B}|Legendary Creature - Vampire Ally|2|3|Flying, first strike$Whenever Drana, Liberator of Malakir deals combat damage to a player, put a +1/+1 counter on each attacking creature you control.|
 Dutiful Attendant|Jumpstart|226|C|{2}{B}|Creature - Human Warrior|1|2|When Dutiful Attendant dies, return another target creature card from your graveyard to your hand.|
 Entomber Exarch|Jumpstart|227|U|{2}{B}{B}|Creature - Cleric|2|2|When Entomber Exarch enters the battlefield, choose one —$• Return target creature card from your graveyard to your hand.$• Target opponent reveals their hand. You choose a noncreature card from it. That player discards that card.|
+Eternal Thirst|Jumpstart|229|C|{1}{B}|Enchantment - Aura|||Enchant creature$Enchanted creature has lifelink and "Whenever a creature an opponent controls dies, put a +1/+1 counter on this creature."|
+Exquisite Blood|Jumpstart|231|R|{4}{B}|Enchantment|||Whenever an opponent loses life, you gain that much life.|
+Falkenrath Noble|Jumpstart|232|U|{3}{B}|Creature - Vampire Noble|2|2|Flying$Whenever Falkenrath Noble or another creature dies, target player loses 1 life and you gain 1 life.|
 Festering Newt|Jumpstart|234|C|{B}|Creature - Salamander|1|1|When Festering Newt dies, target creature an opponent controls gets -1/-1 until end of turn. That creature gets -4/-4 instead if you control a creature named Bogbrew Witch.|
 Ghoulcaller's Accomplice|Jumpstart|237|C|{1}{B}|Creature - Human Rogue|2|2|{3}{B}, Exile Ghoulcaller's Accomplice from your graveyard: Create a 2/2 black Zombie creature token. Activate this ability only any time you could cast a sorcery.|
 Ghoulraiser|Jumpstart|238|C|{1}{B}{B}|Creature - Zombie|2|2|When Ghoulraiser enters the battlefield, return a Zombie card at random from your graveyard to your hand.|
 Gravewaker|Jumpstart|241|R|{4}{B}{B}|Creature - Bird Spirit|5|5|Flying${5}{B}{B}: Return target creature card from your graveyard to the battlefield tapped.|
 Innocent Blood|Jumpstart|244|C|{B}|Sorcery|||Each player sacrifices a creature.|
+Kalastria Nightwatch|Jumpstart|245|C|{4}{B}|Creature - Vampire Warrior Ally|4|5|Whenever you gain life, Kalastria Nightwatch gains flying until end of turn.|
 Last Gasp|Jumpstart|247|C|{1}{B}|Instant|||Target creature gets -3/-3 until end of turn.|
 Liliana's Elite|Jumpstart|250|U|{2}{B}|Creature - Zombie|1|1|Liliana's Elite gets +1/+1 for each creature card in your graveyard.|
+Mark of the Vampire|Jumpstart|254|C|{3}{B}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +2/+2 and has lifelink.|
 Mire Triton|Jumpstart|257|U|{1}{B}|Creature - Zombie Merfolk|2|1|Deathtouch$When Mire Triton enters the battlefield, put the top two cards of your library into your graveyard and you gain 2 life.|
 Nyxathid|Jumpstart|259|R|{1}{B}{B}|Creature - Elemental|7|7|As Nyxathid enters the battlefield, choose an opponent.$Nyxathid gets -1/-1 for each card in the chosen player's hand.|
 Reanimate|Jumpstart|270|R|{B}|Sorcery|||Put target creature card from a graveyard onto the battlefield under your control. You lose life equal to its converted mana cost.|
+Sangromancer|Jumpstart|272|R|{2}{B}{B}|Creature - Vampire Shaman|3|3|Flying$Whenever a creature an opponent controls dies, you may gain 3 life.$Whenever an opponent discards a card, you may gain 3 life.|
+Sengir Vampire|Jumpstart|275|U|{3}{B}{B}|Creature - Vampire|4|4|Flying$Whenever a creature dealt damage by Sengir Vampire this turn dies, put a +1/+1 counter on Sengir Vampire.|
 Settle the Score|Jumpstart|276|U|{2}{B}{B}|Sorcery|||Exile target creature. Put two loyalty counters on a planeswalker you control.|
 Shambling Goblin|Jumpstart|277|C|{B}|Creature - Zombie Goblin|1|1|When Shambling Goblin dies, target creature an opponent controls gets -1/-1 until end of turn.|
 Swarm of Bloodflies|Jumpstart|282|U|{4}{B}|Creature - Insect|0|0|Flying$Swarm of Bloodflies enters the battlefield with two +1/+1 counters on it.$Whenever another creature dies, put a +1/+1 counter on Swarm of Bloodflies.|
 Tempting Witch|Jumpstart|283|U|{2}{B}|Creature - Human Warlock|1|3|When Tempting Witch enters the battlefield, create a Food token.${2}, {T}, Sacrifice a Food: Target player loses 3 life.|
+Vampire Neonate|Jumpstart|285|C|{B}|Creature - Vampire|0|3|{2}, {T}: Each opponent loses 1 life and you gain 1 life.|
 Wailing Ghoul|Jumpstart|286|C|{1}{B}|Creature - Zombie|1|3|When Wailing Ghoul enters the battlefield, put the top two cards of your library into your graveyard.|
 Fanatical Firebrand|Jumpstart|315|C|{R}|Creature - Goblin Pirate|1|1|Haste${T}, Sacrifice Fanatical Firebrand: It deals 1 damage to any target.|
-Hungry Flames|Jumpstart|336|U|{2}{R}|Instant|||Hungry Flames deals 3 damage to target creature and 2 damage to target player or planeswalker.|
+Hungry Flames|Jumpstart|336|C|{2}{R}|Instant|||Hungry Flames deals 3 damage to target creature and 2 damage to target player or planeswalker.|
 Pillar of Flame|Jumpstart|355|R|{R}|Sorcery|||Pillar of Flame deals 2 damage to any target. If a creature dealt damage this way would die this turn, exile it instead.|
 Pyroclastic Elemental|Jumpstart|356|U|{3}{R}{R}|Creature - Elemental|5|4|{1}{R}{R}: Pyroclastic Elemental deals 1 damage to target player.|
 Flames of the Firebrand|Jumpstart|357|U|{2}{R}|Sorcery|||Flames of the Firebrand deals 3 damage divided as you choose among one, two, or three targets.|
 Young Pyromancer|Jumpstart|372|U|{1}{R}|Creature - Human Shaman|2|1|Whenever you cast an instant or sorcery spell, create a 1/1 red Elemental creature token.|
 Affectionate Indrik|Jumpstart|373|U|{5}{G}|Creature - Beast|4|4|When Affectionate Indrik enters the battlefield, you may have it fight target creature you don't control.|
+Aggressive Urge|Jumpstart|374|C|{1}{G}|Instant|||Target creature gets +1/+1 until end of turn.$Draw a card.|
+Ambassador Oak|Jumpstart|375|C|{3}{G}|Creature - Treefolk Warrior|3|3|When Ambassador Oak enters the battlefield, create a 1/1 green Elf Warrior creature token.|
+Awakener Druid|Jumpstart|379|U|{2}{G}|Creature - Human Druid|1|1|When Awakener Druid enters the battlefield, target Forest becomes a 4/5 green Treefolk creature for as long as Awakener Druid remains on the battlefield. It's still a land.|
 Brushstrider|Jumpstart|381|U|{1}{G}|Creature - Beast|3|1|Vigilance|
+Crushing Canopy|Jumpstart|386|C|{2}{G}|Instant|||Choose one —$• Destroy target creature with flying.$• Destroy target enchantment.|
+Explore|Jumpstart|393|C|{1}{G}|Sorcery|||You may play an additional land this turn.$Draw a card.|
+Feral Hydra|Jumpstart|395|U|{X}{G}|Creature - Hydra Beast|0|0|Feral Hydra enters the battlefield with X +1/+1 counters on it.${3}: Put a +1/+1 counter on Feral Hydra. Any player may activate this ability.|
+Fertilid|Jumpstart|398|C|{2}{G}|Creature - Elemental|0|0|Fertilid enters the battlefield with two +1/+1 counters on it.${1}{G}, Remove a +1/+1 counter from Fertilid: Target player searches their library for a basic land card, puts it onto the battlefield tapped, then shuffles their library.|
+Nature's Way|Jumpstart|412|U|{1}{G}|Sorcery|||Target creature you control gains vigilance and trample until end of turn. It deals damage equal to its power to target creature you don't control.|
+Oracle of Mul Daya|Jumpstart|415|R|{3}{G}|Creature - Elf Shaman|2|2|You may play an additional land on each of your turns.$Play with the top card of your library revealed.$You may play lands from the top of your library.|
+Primordial Sage|Jumpstart|422|R|{4}{G}{G}|Creature - Spirit|4|5|Whenever you cast a creature spell, you may draw a card.|
+Soul of the Harvest|Jumpstart|432|R|{4}{G}{G}|Creature - Elemental|6|6|Trample$Whenever another nontoken creature enters the battlefield under your control, you may draw a card.|
+Sporemound|Jumpstart|433|C|{3}{G}{G}|Creature - Fungus|3|3|Whenever a land enters the battlefield under your control, create a 1/1 green Saproling creature token.|
+Sylvan Ranger|Jumpstart|435|C|{1}{G}|Creature - Elf Scout|1|1|When Sylvan Ranger enters the battlefield, you may search your library for a basic land card, reveal it, put it into your hand, then shuffle your library.|
+Ulvenwald Hydra|Jumpstart|439|M|{4}{G}{G}|Creature - Hydra|*|*|Reach$Ulvenwald Hydra's power and toughness are each equal to the number of lands you control.$When Ulvenwald Hydra enters the battlefield, you may search your library for a land card, put it onto the battlefield tapped, then shuffle your library.|
+Vastwood Zendikon|Jumpstart|440|C|{4}{G}|Enchantment - Aura|||Enchant land$Enchanted land is a 6/4 green Elemental creature. It's still a land.$When enchanted land dies, return that card to its owner's hand.|
+Verdant Embrace|Jumpstart|441|R|{3}{G}{G}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +3/+3 and has "At the beginning of each upkeep, create a 1/1 green Saproling creature token."|
+Wall of Blossoms|Jumpstart|442|U|{1}{G}|Creature - Plant Wall|0|4|Defender$When Wall of Blossoms enters the battlefield, draw a card.|
+Woodborn Behemoth|Jumpstart|446|U|{3}{G}{G}|Creature - Elemental|4|4|As long as you control eight or more lands, Woodborn Behemoth gets +4/+4 and has trample.|
+Zendikar's Roil|Jumpstart|448|U|{3}{G}{G}|Enchantment|||Whenever a land enters the battlefield under your control, create a 2/2 green Elemental creature token.|
 Auger Spree|Jumpstart|449|C|{1}{B}{R}|Instant|||Target creature gets +4/-4 until end of turn.|
 Dinrova Horror|Jumpstart|450|U|{4}{U}{B}|Creature - Horror|4|4|When Dinrova Horror enters the battlefield, return target permanent to its owner's hand, then that player discards a card.|
 Fusion Elemental|Jumpstart|451|U|{W}{U}{B}{R}{G}|Creature - Elemental|8|8||
@@ -37782,10 +37835,18 @@ Ironroot Warlord|Jumpstart|452|U|{1}{G}{W}|Creature - Treefolk Soldier|*|5|Ironr
 Lawmage's Binding|Jumpstart|453|C|{1}{W}{U}|Enchantment - Aura|||Flash$Enchant creature$Enchanted creature can't attack or block, and its activated abilities can't be activated.|
 Maelstrom Archangel|Jumpstart|454|M|{W}{U}{B}{R}{G}|Creature - Angel|5|5|Flying$Whenever Maelstrom Archangel deals combat damage to a player, you may cast a nonland card from your hand without paying its mana cost.|
 Raging Regisaur|Jumpstart|455|U|{2}{R}{G}|Creature - Dinosaur|4|4|Whenever Raging Regisaur attacks, it deals 1 damage to any target.|
+Aether Spellbomb|Jumpstart|456|C|{1}|Artifact|||{U}, Sacrifice Aether Spellbomb: Return target creature to its owner's hand.${1}, Sacrifice Aether Spellbomb: Draw a card.|
 Alloy Myr|Jumpstart|457|C|{3}|Artifact Creature - Myr|2|2|{T}: Add one mana of any color.|
+Ancestral Statue|Jumpstart|458|C|{4}|Artifact Creature - Golem|3|4|When Ancestral Statue enters the battlefield, return a nonland permanent you control to its owner's hand.|
 Bubbling Cauldron|Jumpstart|460|U|{2}|Artifact|||{1}, {T}, Sacrifice a creature: You gain 4 life.${1}, {T}, Sacrifice a creature named Festering Newt: Each opponent loses 4 life. You gain life equal to the life lost this way.|
 Chamber Sentry|Jumpstart|461|R|{X}|Artifact Creature - Construct|0|0|Chamber Sentry enters the battlefield with a +1/+1 counter on it for each color of mana spent to cast it.${X}, {T}, Remove X +1/+1 counters from Chamber Sentry: It deals X damage to any target.${W}{U}{B}{R}{G}: Return Chamber Sentry from your graveyard to your hand.|
+Chromatic Sphere|Jumpstart|462|C|{1}|Artifact|||{1}, {T}, Sacrifice Chromatic Sphere: Add one mana of any color. Draw a card.|
+Hedron Archive|Jumpstart|468|U|{4}|Artifact|||{T}: Add {C}{C}.${2}, {T}, Sacrifice Hedron Archive: Draw two cards.|
+Juggernaut|Jumpstart|471|U|{4}|Artifact Creature - Juggernaut|5|3|Juggernaut attacks each combat if able.$Juggernaut can't be blocked by Walls.|
+Meteor Golem|Jumpstart|474|U|{7}|Artifact Creature - Golem|3|3|When Meteor Golem enters the battlefield, destroy target nonland permanent an opponent controls.|
 Prophetic Prism|Jumpstart|478|C|{2}|Artifact|||When Prophetic Prism enters the battlefield, draw a card.${1}, {T}: Add one mana of any color.|
 Skittering Surveyor|Jumpstart|486|C|{3}|Artifact Creature - Construct|1|2|When Skittering Surveyor enters the battlefield, you may search your library for a basic land card, reveal it, put it into your hand, then shuffle your library.|
+Buried Ruin|Jumpstart|491|U||Land|||{T}: Add {C}.${2}, {T}, Sacrifice Buried Ruin: Return target artifact card from your graveyard to your hand.|
 Mirrodin's Core|Jumpstart|492|U||Land|||{T}: Add {C}.${T}: Put a charge counter on Mirrodin's Core.${T}, Remove a charge counter from Mirrodin's Core: Add one mana of any color.|
+Riptide Laboratory|Jumpstart|494|R||Land|||{T}: Add {C}.${1}{U}, {T}: Return target Wizard you control to its owner's hand.|
 Rupture Spire|Jumpstart|495|C||Land|||Rupture Spire enters the battlefield tapped.$When Rupture Spire enters the battlefield, sacrifice it unless you pay {1}.${T}: Add one mana of any color.|

From f231bcb05b4828330a07b27cf80861a1431b5fa6 Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Thu, 18 Jun 2020 11:21:06 -0500
Subject: [PATCH 364/586] - Text fix

---
 .../java/mage/abilities/dynamicvalue/common/DomainValue.java    | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/DomainValue.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/DomainValue.java
index cdb7d1360b..c4bc6caaaa 100644
--- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/DomainValue.java
+++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/DomainValue.java
@@ -99,6 +99,6 @@ public class DomainValue implements DynamicValue {
 
     @Override
     public String getMessage() {
-        return "basic land type among lands " + (countTargetPlayer ? "they control" : "you control");
+        return "basic land types among lands " + (countTargetPlayer ? "they control" : "you control");
     }
 }

From db800f3a739e117a755492de443edbde85ace86b Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Thu, 18 Jun 2020 11:57:31 -0500
Subject: [PATCH 365/586] - Text fix

---
 Mage.Sets/src/mage/cards/d/DragonGrip.java | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/d/DragonGrip.java b/Mage.Sets/src/mage/cards/d/DragonGrip.java
index 9280f0c4a2..6637c97a21 100644
--- a/Mage.Sets/src/mage/cards/d/DragonGrip.java
+++ b/Mage.Sets/src/mage/cards/d/DragonGrip.java
@@ -32,9 +32,9 @@ public final class DragonGrip extends CardImpl {
 
         // Ferocious - If you control a creature with power 4 or greater, you may cast Dragon Grip as though it had flash.
         AsThoughEffect effect = new CastAsThoughItHadFlashSourceEffect(Duration.EndOfGame);
-        effect.setText("<i>Ferocious</i> &mdash; If you control a creature with power 4 or greater, you may cast Dragon Grip as though it had flash");
         this.addAbility(new SimpleStaticAbility(Zone.ALL, new ConditionalAsThoughEffect(effect,
-                FerociousCondition.instance))
+                FerociousCondition.instance).setText("<i>Ferocious</i> &mdash; If you control a creature with power 4 or greater, "
+                        + "you may cast Dragon Grip as though it had flash"))
                 .addHint(FerociousHint.instance));
 
         // Enchant creature

From 044b25415554b8fdf2ac5be1b8fa762ab386518b Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Thu, 18 Jun 2020 19:11:02 +0200
Subject: [PATCH 366/586] * Some more rework/clean up of the
 PlayFromNotOwnHandZone effects (fixes #6580).

---
 Mage.Sets/src/mage/cards/s/SinsOfThePast.java | 39 ++------------
 Mage.Sets/src/mage/cards/s/Spelljack.java     | 51 ++-----------------
 2 files changed, 6 insertions(+), 84 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/s/SinsOfThePast.java b/Mage.Sets/src/mage/cards/s/SinsOfThePast.java
index 05441948f1..35177017ea 100644
--- a/Mage.Sets/src/mage/cards/s/SinsOfThePast.java
+++ b/Mage.Sets/src/mage/cards/s/SinsOfThePast.java
@@ -20,6 +20,7 @@ import mage.target.common.TargetCardInYourGraveyard;
 import mage.target.targetpointer.FixedTarget;
 
 import java.util.UUID;
+import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect;
 
 /**
  * @author emerald000
@@ -65,8 +66,8 @@ class SinsOfThePastEffect extends OneShotEffect {
     public boolean apply(Game game, Ability source) {
         Card card = game.getCard(this.getTargetPointer().getFirst(game, source));
         if (card != null) {
-            ContinuousEffect effect = new SinsOfThePastCastFromGraveyardEffect();
-            effect.setTargetPointer(new FixedTarget(card.getId()));
+            ContinuousEffect effect = new PlayFromNotOwnHandZoneTargetEffect(Zone.GRAVEYARD, TargetController.YOU, Duration.EndOfTurn, true);;
+            effect.setTargetPointer(new FixedTarget(card, game));
             game.addEffect(effect, source);
             effect = new SinsOfThePastReplacementEffect(card.getId());
             game.addEffect(effect, source);
@@ -76,40 +77,6 @@ class SinsOfThePastEffect extends OneShotEffect {
     }
 }
 
-class SinsOfThePastCastFromGraveyardEffect extends AsThoughEffectImpl {
-
-    SinsOfThePastCastFromGraveyardEffect() {
-        super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.PlayForFree);
-    }
-
-    SinsOfThePastCastFromGraveyardEffect(final SinsOfThePastCastFromGraveyardEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        return true;
-    }
-
-    @Override
-    public SinsOfThePastCastFromGraveyardEffect copy() {
-        return new SinsOfThePastCastFromGraveyardEffect(this);
-    }
-
-    @Override
-    public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
-        if (objectId.equals(this.getTargetPointer().getFirst(game, source)) && affectedControllerId.equals(source.getControllerId())) {
-            Player player = game.getPlayer(affectedControllerId);
-            Card cardToCast = game.getCard(objectId);
-            if(player != null && cardToCast != null) {
-                player.setCastSourceIdWithAlternateMana(objectId, null, cardToCast.getSpellAbility().getCosts());
-                return true;
-            }
-        }
-        return false;
-    }
-}
-
 class SinsOfThePastReplacementEffect extends ReplacementEffectImpl {
 
     private final UUID cardId;
diff --git a/Mage.Sets/src/mage/cards/s/Spelljack.java b/Mage.Sets/src/mage/cards/s/Spelljack.java
index d15fcb8045..7ea471aee7 100644
--- a/Mage.Sets/src/mage/cards/s/Spelljack.java
+++ b/Mage.Sets/src/mage/cards/s/Spelljack.java
@@ -6,6 +6,7 @@ import mage.abilities.Ability;
 import mage.abilities.effects.AsThoughEffectImpl;
 import mage.abilities.effects.ContinuousEffect;
 import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect;
 import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -13,6 +14,7 @@ import mage.constants.AsThoughEffectType;
 import mage.constants.CardType;
 import mage.constants.Duration;
 import mage.constants.Outcome;
+import mage.constants.TargetController;
 import mage.constants.Zone;
 import mage.constants.ZoneDetail;
 import mage.game.Game;
@@ -71,7 +73,7 @@ class SpelljackEffect extends OneShotEffect {
             if (stackObject != null && game.getStack().counter(targetId, source.getSourceId(), game, Zone.EXILED, false, ZoneDetail.NONE)) {
                 Card card = ((Spell) stackObject).getCard();
                 if (card != null) {
-                    ContinuousEffect effect = new SpelljackCastFromExileEffect();
+                    ContinuousEffect effect = new PlayFromNotOwnHandZoneTargetEffect(Zone.EXILED, TargetController.YOU, Duration.Custom, true);
                     effect.setTargetPointer(new FixedTarget(card.getId(), game.getState().getZoneChangeCounter(card.getId())));
                     game.addEffect(effect, source);
                 }
@@ -81,50 +83,3 @@ class SpelljackEffect extends OneShotEffect {
         return false;
     }
 }
-
-class SpelljackCastFromExileEffect extends AsThoughEffectImpl {
-
-    SpelljackCastFromExileEffect() {
-        super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit);
-        staticText = "You may cast that card without paying its mana cost as long as it remains exiled";
-    }
-
-    SpelljackCastFromExileEffect(final SpelljackCastFromExileEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        return true;
-    }
-
-    @Override
-    public SpelljackCastFromExileEffect copy() {
-        return new SpelljackCastFromExileEffect(this);
-    }
-
-    @Override
-    public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) {
-        if (affectedControllerId.equals(source.getControllerId())) {
-            if (getTargetPointer().getFirst(game, source) == null) {
-                this.discard();
-                return false;
-            }
-            if (sourceId.equals(getTargetPointer().getFirst(game, source))) {
-                Card card = game.getCard(sourceId);
-                if (card != null) {
-                    if (game.getState().getZone(sourceId) == Zone.EXILED) {
-                        Player player = game.getPlayer(affectedControllerId);
-                        if(player != null) {
-                            player.setCastSourceIdWithAlternateMana(sourceId, null, card.getSpellAbility().getCosts());
-                        }
-                        return true;
-                    } else {
-                        this.discard();
-                    }
-                }
-            }
-        }
-        return false;
-    }
-}

From 3beaec45257c65bbc0ee938c0ba32e03e415da54 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Thu, 18 Jun 2020 19:12:58 +0200
Subject: [PATCH 367/586] * Fixed that effects with "as long as Intet remains
 on the battlefield" condition were reapplied as the card returned to
 battlefield. Some other minor fixes.

---
 Mage.Sets/src/mage/cards/a/AwakenerDruid.java |  1 +
 Mage.Sets/src/mage/cards/c/Charisma.java      |  6 ++-
 .../mage/cards/c/CytoplastManipulator.java    |  3 +-
 .../src/mage/cards/i/InfernalDenizen.java     |  3 +-
 .../src/mage/cards/i/IntetTheDreamer.java     | 22 ++++++----
 Mage.Sets/src/mage/cards/n/NekoTe.java        |  5 ++-
 .../src/mage/cards/s/ScarwoodBandits.java     |  3 +-
 Mage.Sets/src/mage/cards/s/SithMindseer.java  | 14 +++++--
 Mage.Sets/src/mage/cards/s/Somnophore.java    |  3 +-
 .../src/mage/cards/s/SowerOfTemptation.java   |  4 +-
 Mage.Sets/src/mage/cards/s/StromgaldSpy.java  | 16 ++++++--
 Mage.Sets/src/mage/cards/t/TheAkroanWar.java  |  5 ++-
 .../common/SourceRemainsInZoneCondition.java  | 40 +++++++++++++++++++
 .../ConditionalContinuousEffect.java          |  1 +
 Mage/src/main/java/mage/game/GameState.java   |  2 +-
 15 files changed, 102 insertions(+), 26 deletions(-)
 create mode 100644 Mage/src/main/java/mage/abilities/condition/common/SourceRemainsInZoneCondition.java

diff --git a/Mage.Sets/src/mage/cards/a/AwakenerDruid.java b/Mage.Sets/src/mage/cards/a/AwakenerDruid.java
index e2fa750bf9..a14bb44b09 100644
--- a/Mage.Sets/src/mage/cards/a/AwakenerDruid.java
+++ b/Mage.Sets/src/mage/cards/a/AwakenerDruid.java
@@ -96,6 +96,7 @@ class AwakenerDruidToken extends TokenImpl {
         super(token);
     }
 
+    @Override
     public AwakenerDruidToken copy() {
         return new AwakenerDruidToken(this);
     }
diff --git a/Mage.Sets/src/mage/cards/c/Charisma.java b/Mage.Sets/src/mage/cards/c/Charisma.java
index c2fe9091bc..235ab9e864 100644
--- a/Mage.Sets/src/mage/cards/c/Charisma.java
+++ b/Mage.Sets/src/mage/cards/c/Charisma.java
@@ -6,6 +6,7 @@ import mage.abilities.Ability;
 import mage.abilities.common.DealsDamageToACreatureAttachedTriggeredAbility;
 import mage.abilities.condition.Condition;
 import mage.abilities.condition.common.SourceOnBattlefieldCondition;
+import mage.abilities.condition.common.SourceRemainsInZoneCondition;
 import mage.abilities.decorator.ConditionalContinuousEffect;
 import mage.abilities.effects.common.AttachEffect;
 import mage.abilities.effects.common.continuous.GainControlTargetEffect;
@@ -16,6 +17,7 @@ import mage.constants.CardType;
 import mage.constants.SubType;
 import mage.constants.Duration;
 import mage.constants.Outcome;
+import mage.constants.Zone;
 import mage.target.TargetPermanent;
 import mage.target.common.TargetCreaturePermanent;
 
@@ -40,8 +42,8 @@ public final class Charisma extends CardImpl {
         this.addAbility(ability);
 
         // Whenever enchanted creature deals damage to a creature, gain control of the other creature for as long as Charisma remains on the battlefield.
-        Condition condition = SourceOnBattlefieldCondition.instance;
-        ConditionalContinuousEffect conditionalEffect = new ConditionalContinuousEffect(new GainControlTargetEffect(Duration.Custom), condition, rule);
+        ConditionalContinuousEffect conditionalEffect = new ConditionalContinuousEffect(new GainControlTargetEffect(Duration.Custom), 
+                new SourceRemainsInZoneCondition(Zone.BATTLEFIELD), rule);
         this.addAbility(new DealsDamageToACreatureAttachedTriggeredAbility(conditionalEffect, false, "enchanted creature", false, true));
         
     }
diff --git a/Mage.Sets/src/mage/cards/c/CytoplastManipulator.java b/Mage.Sets/src/mage/cards/c/CytoplastManipulator.java
index fa4db9dfc2..4954ae6e4a 100644
--- a/Mage.Sets/src/mage/cards/c/CytoplastManipulator.java
+++ b/Mage.Sets/src/mage/cards/c/CytoplastManipulator.java
@@ -6,6 +6,7 @@ import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.condition.common.SourceOnBattlefieldCondition;
+import mage.abilities.condition.common.SourceRemainsInZoneCondition;
 import mage.abilities.costs.common.TapSourceCost;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.decorator.ConditionalContinuousEffect;
@@ -47,7 +48,7 @@ public final class CytoplastManipulator extends CardImpl {
         // {U}, {tap}: Gain control of target creature with a +1/+1 counter on it for as long as Cytoplast Manipulator remains on the battlefield.
         ConditionalContinuousEffect effect = new ConditionalContinuousEffect(
                 new GainControlTargetEffect(Duration.Custom, true),
-                SourceOnBattlefieldCondition.instance,
+                new SourceRemainsInZoneCondition(Zone.BATTLEFIELD),
                 "gain control of target creature with a +1/+1 counter on it for as long as {this} remains on the battlefield");
         Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("{U}"));
         ability.addCost(new TapSourceCost());
diff --git a/Mage.Sets/src/mage/cards/i/InfernalDenizen.java b/Mage.Sets/src/mage/cards/i/InfernalDenizen.java
index 7b162869c3..d5005c10b5 100644
--- a/Mage.Sets/src/mage/cards/i/InfernalDenizen.java
+++ b/Mage.Sets/src/mage/cards/i/InfernalDenizen.java
@@ -7,6 +7,7 @@ import mage.abilities.Ability;
 import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.condition.common.SourceOnBattlefieldCondition;
+import mage.abilities.condition.common.SourceRemainsInZoneCondition;
 import mage.abilities.costs.common.TapSourceCost;
 import mage.abilities.decorator.ConditionalContinuousEffect;
 import mage.abilities.dynamicvalue.DynamicValue;
@@ -52,7 +53,7 @@ public final class InfernalDenizen extends CardImpl {
         // {tap}: Gain control of target creature for as long as Infernal Denizen remains on the battlefield.
         ConditionalContinuousEffect effect = new ConditionalContinuousEffect(
                 new GainControlTargetEffect(Duration.Custom, true),
-                SourceOnBattlefieldCondition.instance,
+                new SourceRemainsInZoneCondition(Zone.BATTLEFIELD),
                 "gain control of target creature for as long as {this} remains on the battlefield");
         Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new TapSourceCost());
         ability.addTarget(new TargetCreaturePermanent());
diff --git a/Mage.Sets/src/mage/cards/i/IntetTheDreamer.java b/Mage.Sets/src/mage/cards/i/IntetTheDreamer.java
index 247fcad509..c93e56b139 100644
--- a/Mage.Sets/src/mage/cards/i/IntetTheDreamer.java
+++ b/Mage.Sets/src/mage/cards/i/IntetTheDreamer.java
@@ -19,7 +19,7 @@ import mage.players.Player;
 import mage.util.CardUtil;
 
 import java.util.UUID;
-import mage.abilities.condition.common.SourceOnBattlefieldCondition;
+import mage.abilities.condition.common.SourceRemainsInZoneCondition;
 import mage.abilities.decorator.ConditionalAsThoughEffect;
 import mage.abilities.effects.ContinuousEffect;
 import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect;
@@ -83,11 +83,11 @@ class IntetTheDreamerExileEffect extends OneShotEffect {
                 card.setFaceDown(true, game);
                 controller.moveCardsToExile(card, source, game, false,
                         CardUtil.getExileZoneId(game, source.getSourceId(), sourceObject.getZoneChangeCounter(game)), // sourceObject must be used due to source not working correctly
-                        sourceObject.getIdName());
+                        sourceObject.getIdName() + " (" + sourceObject.getZoneChangeCounter(game) + ")");
                 card.setFaceDown(true, game);
                 ContinuousEffect effect = new ConditionalAsThoughEffect(
-                        new PlayFromNotOwnHandZoneTargetEffect(Zone.EXILED, TargetController.YOU, Duration.Custom, true), 
-                        SourceOnBattlefieldCondition.instance);
+                        new PlayFromNotOwnHandZoneTargetEffect(Zone.EXILED, TargetController.YOU, Duration.Custom, true),
+                        new SourceRemainsInZoneCondition(Zone.BATTLEFIELD));
                 effect.setTargetPointer(new FixedTarget(card, game));
                 game.getState().addEffect(effect, source);
                 game.getState().setValue("Exiled_IntetTheDreamer" + card.getId(), Boolean.TRUE); // TODO This value will never be removed
@@ -130,10 +130,16 @@ class IntetTheDreamerLookEffect extends AsThoughEffectImpl {
             Player controller = game.getPlayer(source.getControllerId());
             if (controller != null) {
                 Card card = game.getCard(objectId);
-                return (card != null
-                        && card.isFaceDown(game)
-                        && game.getExile().containsId(card.getId(), game)
-                        && Boolean.TRUE.equals(game.getState().getValue("Exiled_IntetTheDreamer" + card.getId())));
+                if (card != null) {
+                    if (card.isFaceDown(game)
+                            && game.getExile().containsId(card.getId(), game)
+                            && Boolean.TRUE.equals(game.getState().getValue("Exiled_IntetTheDreamer" + card.getId()))) {
+                        return true;
+                    } else {
+                        this.discard();
+                        game.getState().setValue("Exiled_IntetTheDreamer" + card.getId(), null);
+                    }
+                }
             }
         }
         return false;
diff --git a/Mage.Sets/src/mage/cards/n/NekoTe.java b/Mage.Sets/src/mage/cards/n/NekoTe.java
index 3b056ae55c..656ce23f3a 100644
--- a/Mage.Sets/src/mage/cards/n/NekoTe.java
+++ b/Mage.Sets/src/mage/cards/n/NekoTe.java
@@ -6,6 +6,7 @@ import mage.abilities.Ability;
 import mage.abilities.common.DealsDamageToACreatureAttachedTriggeredAbility;
 import mage.abilities.common.DealsDamageToAPlayerAttachedTriggeredAbility;
 import mage.abilities.condition.common.SourceOnBattlefieldCondition;
+import mage.abilities.condition.common.SourceRemainsInZoneCondition;
 import mage.abilities.costs.mana.GenericManaCost;
 import mage.abilities.decorator.ConditionalContinuousRuleModifyingEffect;
 import mage.abilities.effects.ContinuousRuleModifyingEffect;
@@ -19,6 +20,7 @@ import mage.constants.CardType;
 import mage.constants.SubType;
 import mage.constants.Duration;
 import mage.constants.Outcome;
+import mage.constants.Zone;
 
 /**
  *
@@ -33,7 +35,8 @@ public final class NekoTe extends CardImpl {
         // Whenever equipped creature deals damage to a creature, tap that creature. That creature doesn't untap during its controller's untap step for as long as Neko-Te remains on the battlefield.
         ContinuousRuleModifyingEffect skipUntapEffect = new DontUntapInControllersUntapStepTargetEffect(Duration.WhileOnBattlefield);
         skipUntapEffect.setText("That creature doesn't untap during its controller's untap step for as long as {this} remains on the battlefield");
-        ConditionalContinuousRuleModifyingEffect effect = new ConditionalContinuousRuleModifyingEffect(skipUntapEffect, SourceOnBattlefieldCondition.instance);
+        ConditionalContinuousRuleModifyingEffect effect = new ConditionalContinuousRuleModifyingEffect(skipUntapEffect, 
+                new SourceRemainsInZoneCondition(Zone.BATTLEFIELD));
         Ability ability = new DealsDamageToACreatureAttachedTriggeredAbility(new TapTargetEffect("that creature"), false, "equipped creature", false, true);
         ability.addEffect(effect);
         this.addAbility(ability);
diff --git a/Mage.Sets/src/mage/cards/s/ScarwoodBandits.java b/Mage.Sets/src/mage/cards/s/ScarwoodBandits.java
index fa59c4dcc5..31934619f8 100644
--- a/Mage.Sets/src/mage/cards/s/ScarwoodBandits.java
+++ b/Mage.Sets/src/mage/cards/s/ScarwoodBandits.java
@@ -26,6 +26,7 @@ import mage.target.common.TargetArtifactPermanent;
 import mage.util.CardUtil;
 
 import java.util.UUID;
+import mage.abilities.condition.common.SourceRemainsInZoneCondition;
 
 /**
  * @author L_J
@@ -47,7 +48,7 @@ public final class ScarwoodBandits extends CardImpl {
                 new DoUnlessAnyOpponentPaysEffect(
                         new ConditionalContinuousEffect(
                                 new GainControlTargetEffect(Duration.Custom, true),
-                                SourceOnBattlefieldCondition.instance,
+                                new SourceRemainsInZoneCondition(Zone.BATTLEFIELD),
                                 "gain control of target artifact for as long as {this} remains on the battlefield"),
                         new GenericManaCost(2)),
                 new ManaCostsImpl("{2}{G}"));
diff --git a/Mage.Sets/src/mage/cards/s/SithMindseer.java b/Mage.Sets/src/mage/cards/s/SithMindseer.java
index 74a76a635f..07fe6121a4 100644
--- a/Mage.Sets/src/mage/cards/s/SithMindseer.java
+++ b/Mage.Sets/src/mage/cards/s/SithMindseer.java
@@ -6,6 +6,8 @@ import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.condition.common.HateCondition;
+import mage.abilities.condition.common.SourceRemainsInZoneCondition;
+import mage.abilities.decorator.ConditionalContinuousEffect;
 import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
 import mage.abilities.effects.common.continuous.GainControlTargetEffect;
 import mage.cards.CardImpl;
@@ -13,6 +15,7 @@ import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.SubType;
 import mage.constants.Duration;
+import mage.constants.Zone;
 import mage.target.common.TargetCreaturePermanent;
 import mage.watchers.common.LifeLossOtherFromCombatWatcher;
 
@@ -29,11 +32,16 @@ public final class SithMindseer extends CardImpl {
         this.power = new MageInt(3);
         this.toughness = new MageInt(3);
 
-        // <i>Hate</i> &mdash; When Sith Mindseer enters the battlefield, if an opponent loses life from a source other than combat damage, gain control of target creature for as long as Sith Mindseer remains on the battlefield.
+        // <i>Hate</i> &mdash; When Sith Mindseer enters the battlefield, if an opponent loses life from a source other than combat damage,
+        // gain control of target creature for as long as Sith Mindseer remains on the battlefield.
         Ability ability = new ConditionalInterveningIfTriggeredAbility(
-                new EntersBattlefieldTriggeredAbility(new GainControlTargetEffect(Duration.WhileOnBattlefield)),
+                new EntersBattlefieldTriggeredAbility(new ConditionalContinuousEffect(
+                                new GainControlTargetEffect(Duration.Custom, true),
+                                new SourceRemainsInZoneCondition(Zone.BATTLEFIELD),
+                                "gain control of target creature for as long as {this} remains on the battlefield")),                        
                 HateCondition.instance,
-                "<i>Hate</i> &mdash; When Sith Mindseer enters the battlefield, if an opponent loses life from a source other than combat damage, gain control of target creature for as long as Sith Mindseer remains on the battlefield.");
+                "<i>Hate</i> &mdash; When {this} enters the battlefield, if an opponent loses life from a source other than combat damage,"
+                        + " gain control of target creature for as long as {this} remains on the battlefield.");
         ability.addTarget(new TargetCreaturePermanent());
         this.addAbility(ability, new LifeLossOtherFromCombatWatcher());
     }
diff --git a/Mage.Sets/src/mage/cards/s/Somnophore.java b/Mage.Sets/src/mage/cards/s/Somnophore.java
index 33a3b13a17..208db0abfb 100644
--- a/Mage.Sets/src/mage/cards/s/Somnophore.java
+++ b/Mage.Sets/src/mage/cards/s/Somnophore.java
@@ -6,6 +6,7 @@ import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.TriggeredAbilityImpl;
 import mage.abilities.condition.common.SourceOnBattlefieldCondition;
+import mage.abilities.condition.common.SourceRemainsInZoneCondition;
 import mage.abilities.decorator.ConditionalContinuousRuleModifyingEffect;
 import mage.abilities.effects.ContinuousRuleModifyingEffect;
 import mage.abilities.effects.Effect;
@@ -44,7 +45,7 @@ public final class Somnophore extends CardImpl {
         // Whenever Somnophore deals damage to a player, tap target creature that player controls. That creature doesn't untap during its controller's untap step for as long as Somnophore remains on the battlefield.
         ContinuousRuleModifyingEffect skipUntapEffect = new DontUntapInControllersUntapStepTargetEffect(Duration.WhileOnBattlefield);
         skipUntapEffect.setText("That creature doesn't untap during its controller's untap step for as long as {this} remains on the battlefield");
-        ConditionalContinuousRuleModifyingEffect effect = new ConditionalContinuousRuleModifyingEffect(skipUntapEffect, SourceOnBattlefieldCondition.instance);
+        ConditionalContinuousRuleModifyingEffect effect = new ConditionalContinuousRuleModifyingEffect(skipUntapEffect, new SourceRemainsInZoneCondition(Zone.BATTLEFIELD));
         Ability ability = new SomnophoreTriggeredAbility(new TapTargetEffect());
         ability.addTarget(new TargetCreaturePermanent());
         ability.addEffect(effect);
diff --git a/Mage.Sets/src/mage/cards/s/SowerOfTemptation.java b/Mage.Sets/src/mage/cards/s/SowerOfTemptation.java
index 3607841e7b..9800a91148 100644
--- a/Mage.Sets/src/mage/cards/s/SowerOfTemptation.java
+++ b/Mage.Sets/src/mage/cards/s/SowerOfTemptation.java
@@ -6,6 +6,7 @@ import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.condition.common.SourceOnBattlefieldCondition;
+import mage.abilities.condition.common.SourceRemainsInZoneCondition;
 import mage.abilities.decorator.ConditionalContinuousEffect;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.continuous.GainControlTargetEffect;
@@ -16,6 +17,7 @@ import mage.constants.CardType;
 import mage.constants.SubType;
 import mage.constants.Duration;
 import mage.constants.Outcome;
+import mage.constants.Zone;
 import mage.game.Game;
 import mage.target.common.TargetCreaturePermanent;
 
@@ -78,7 +80,7 @@ class SowerOfTemptationGainControlEffect extends OneShotEffect {
     public boolean apply(Game game, Ability source) {
         ConditionalContinuousEffect effect = new ConditionalContinuousEffect(
                 new GainControlTargetEffect(Duration.Custom),
-                SourceOnBattlefieldCondition.instance,
+                new SourceRemainsInZoneCondition(Zone.BATTLEFIELD),
                 "gain control of target creature for as long as {this} remains on the battlefield");
         game.addEffect(effect, source);
         return false;
diff --git a/Mage.Sets/src/mage/cards/s/StromgaldSpy.java b/Mage.Sets/src/mage/cards/s/StromgaldSpy.java
index 94ead75870..8bb0be3207 100644
--- a/Mage.Sets/src/mage/cards/s/StromgaldSpy.java
+++ b/Mage.Sets/src/mage/cards/s/StromgaldSpy.java
@@ -5,6 +5,8 @@ import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.AttacksAndIsNotBlockedTriggeredAbility;
+import mage.abilities.condition.common.SourceRemainsInZoneCondition;
+import mage.abilities.decorator.ConditionalContinuousEffect;
 import mage.abilities.effects.ContinuousEffectImpl;
 import mage.abilities.effects.common.continuous.AssignNoCombatDamageSourceEffect;
 import mage.constants.SubType;
@@ -15,6 +17,7 @@ import mage.constants.Duration;
 import mage.constants.Layer;
 import mage.constants.Outcome;
 import mage.constants.SubLayer;
+import mage.constants.Zone;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
 import mage.players.Player;
@@ -32,8 +35,14 @@ public final class StromgaldSpy extends CardImpl {
         this.power = new MageInt(2);
         this.toughness = new MageInt(4);
 
-        // Whenever Stromgald Spy attacks and isn't blocked, you may have defending player play with their hand revealed for as long as Stromgald Spy remains on the battlefield. If you do, Stromgald Spy assigns no combat damage this turn.
-        Ability ability = new AttacksAndIsNotBlockedTriggeredAbility(new StromgaldSpyEffect(), true, true);
+        // Whenever Stromgald Spy attacks and isn't blocked, you may have defending player play with their hand revealed
+        // for as long as Stromgald Spy remains on the battlefield. If you do, Stromgald Spy assigns no combat damage this turn.
+        Ability ability = new AttacksAndIsNotBlockedTriggeredAbility(
+                new ConditionalContinuousEffect(
+                                new StromgaldSpyEffect(),
+                                new SourceRemainsInZoneCondition(Zone.BATTLEFIELD),
+                                "you may have defending player play with their hand revealed for as long as {this} remains on the battlefield"),
+                true, true);
         ability.addEffect(new AssignNoCombatDamageSourceEffect(Duration.EndOfTurn, true));
         this.addAbility(ability);
     }
@@ -51,8 +60,7 @@ public final class StromgaldSpy extends CardImpl {
 class StromgaldSpyEffect extends ContinuousEffectImpl {
 
     public StromgaldSpyEffect() {
-        super(Duration.WhileOnBattlefield, Layer.PlayerEffects, SubLayer.NA, Outcome.Detriment);
-        this.staticText = "you may have defending player play with their hand revealed for as long as Stromgald Spy remains on the battlefield";
+        super(Duration.Custom, Layer.PlayerEffects, SubLayer.NA, Outcome.Detriment);
     }
 
     public StromgaldSpyEffect(final StromgaldSpyEffect effect) {
diff --git a/Mage.Sets/src/mage/cards/t/TheAkroanWar.java b/Mage.Sets/src/mage/cards/t/TheAkroanWar.java
index 2a5cac325b..19891d2612 100644
--- a/Mage.Sets/src/mage/cards/t/TheAkroanWar.java
+++ b/Mage.Sets/src/mage/cards/t/TheAkroanWar.java
@@ -18,6 +18,7 @@ import mage.game.Game;
 import mage.target.common.TargetCreaturePermanent;
 
 import java.util.UUID;
+import mage.abilities.condition.common.SourceRemainsInZoneCondition;
 
 /**
  * @author TheElk801
@@ -42,8 +43,8 @@ public final class TheAkroanWar extends CardImpl {
                 SagaChapter.CHAPTER_I,
                 new ConditionalContinuousEffect(
                         new GainControlTargetEffect(Duration.Custom, true),
-                        SourceOnBattlefieldCondition.instance, "gain control of target creature " +
-                        "for as long as {this} remains on the battlefield"
+                        new SourceRemainsInZoneCondition(Zone.BATTLEFIELD),
+                        "gain control of target creature for as long as {this} remains on the battlefield"
                 ), new TargetCreaturePermanent()
         );
 
diff --git a/Mage/src/main/java/mage/abilities/condition/common/SourceRemainsInZoneCondition.java b/Mage/src/main/java/mage/abilities/condition/common/SourceRemainsInZoneCondition.java
new file mode 100644
index 0000000000..2d4e6a3d03
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/condition/common/SourceRemainsInZoneCondition.java
@@ -0,0 +1,40 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package mage.abilities.condition.common;
+
+import mage.abilities.Ability;
+import mage.abilities.condition.Condition;
+import mage.constants.Zone;
+import mage.game.Game;
+
+/**
+ *
+ * @author LevelX2
+ */
+public class SourceRemainsInZoneCondition implements Condition {
+
+    private final Zone zone;
+    private int timesChangedZones = -1;
+
+    public SourceRemainsInZoneCondition(Zone zone) {
+        this.zone = zone;
+        this.timesChangedZones = -1;
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        if (timesChangedZones == -1) { // Only changed on first execution 
+            timesChangedZones = game.getState().getZoneChangeCounter(source.getSourceId());
+        }
+        return (timesChangedZones == game.getState().getZoneChangeCounter(source.getSourceId())
+                && zone.equals(game.getState().getZone(source.getSourceId())));
+    }
+
+    @Override
+    public String toString() {
+        return "for as long as {this} remains on the " + zone.toString();
+    }
+}
diff --git a/Mage/src/main/java/mage/abilities/decorator/ConditionalContinuousEffect.java b/Mage/src/main/java/mage/abilities/decorator/ConditionalContinuousEffect.java
index 1d0403aa9f..357d41588e 100644
--- a/Mage/src/main/java/mage/abilities/decorator/ConditionalContinuousEffect.java
+++ b/Mage/src/main/java/mage/abilities/decorator/ConditionalContinuousEffect.java
@@ -177,6 +177,7 @@ public class ConditionalContinuousEffect extends ContinuousEffectImpl {
 
     /**
      * Return all effects list, for tests only
+     * @return 
      */
     public List<ContinuousEffect> getAllEffects() {
         List<ContinuousEffect> res = new ArrayList<>();
diff --git a/Mage/src/main/java/mage/game/GameState.java b/Mage/src/main/java/mage/game/GameState.java
index f156e307f5..04f1e5d2a2 100644
--- a/Mage/src/main/java/mage/game/GameState.java
+++ b/Mage/src/main/java/mage/game/GameState.java
@@ -98,7 +98,7 @@ public class GameState implements Serializable, Copyable<GameState> {
     private Map<UUID, Integer> zoneChangeCounter = new HashMap<>();
     private Map<UUID, Card> copiedCards = new HashMap<>();
     private int permanentOrderNumber;
-    private Map<UUID, FilterCreaturePermanent> usePowerInsteadOfToughnessForDamageLethalityFilters = new HashMap<>();
+    private final Map<UUID, FilterCreaturePermanent> usePowerInsteadOfToughnessForDamageLethalityFilters = new HashMap<>();
     private final Set<MageObjectReference> commandersToStay = new HashSet<>();
 
     private int applyEffectsCounter; // Upcounting number of each applyEffects execution

From c41fc0284dcacc9ed27d64d00df1255ad4043cbf Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Thu, 18 Jun 2020 22:59:42 +0200
Subject: [PATCH 368/586] * Fixed missing check for restricting effects of
 activated abilities of permanents (fixes #6657). I guess that got lost by
 refactoring get playable abilities.

---
 Mage.Sets/src/mage/cards/s/SolRing.java       |  2 +
 .../CantUseActivatedAbilitiesTest.java        | 57 +++++++++++++++++++
 .../java/org/mage/test/player/TestPlayer.java |  4 +-
 .../main/java/mage/players/PlayerImpl.java    |  9 ++-
 4 files changed, 67 insertions(+), 5 deletions(-)
 create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/restriction/CantUseActivatedAbilitiesTest.java

diff --git a/Mage.Sets/src/mage/cards/s/SolRing.java b/Mage.Sets/src/mage/cards/s/SolRing.java
index 9b836f10fa..55431f5ba1 100644
--- a/Mage.Sets/src/mage/cards/s/SolRing.java
+++ b/Mage.Sets/src/mage/cards/s/SolRing.java
@@ -18,6 +18,8 @@ public final class SolRing extends CardImpl {
 
     public SolRing(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{1}");
+        
+        // Tap: Add {C}{C}
         this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.ColorlessMana(2), new TapSourceCost()));
     }
 
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/restriction/CantUseActivatedAbilitiesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/restriction/CantUseActivatedAbilitiesTest.java
new file mode 100644
index 0000000000..f5bc8b2eff
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/restriction/CantUseActivatedAbilitiesTest.java
@@ -0,0 +1,57 @@
+package org.mage.test.cards.restriction;
+
+import mage.constants.PhaseStep;
+import mage.constants.Zone;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestPlayerBase;
+
+/**
+ *
+ * @author LevelX2
+ */
+
+public class CantUseActivatedAbilitiesTest extends CardTestPlayerBase {
+
+    /**
+     * I can activate artifacts despite my opponent having Collector Ouphe or 
+     * Karn, the Great Creator in play. The artifact says it can't be activated, but that's a lie.
+     */
+    @Test
+    public void testCantActivateManaAbility() {
+        // Activated abilities of artifacts can't be activated.        
+        addCard(Zone.HAND, playerA, "Collector Ouphe"); // Creature  {1}{G}  2/2
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 2);        
+        // {T}: Add {C}{C}
+        addCard(Zone.BATTLEFIELD, playerB, "Sol Ring"); // Artifact
+             
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Collector Ouphe");
+                  
+        setStopAt(1, PhaseStep.END_COMBAT);                
+        setStrictChooseMode(true);
+        execute();        
+        assertAllCommandsUsed();
+        
+        // Sol Ring can't produce mana
+        Assert.assertTrue("PlayerB may not be able to produce any mana but he he can produce " + playerB.getManaAvailable(currentGame).toString(), playerB.getManaAvailable(currentGame).toString().equals("[]"));
+    }
+    
+   @Test
+    public void testCantActivateActivatedAbility() {
+        // Activated abilities of artifacts can't be activated.        
+        addCard(Zone.BATTLEFIELD, playerA, "Collector Ouphe"); // Creature  {1}{G}  2/2
+        
+        // {1}: Adarkar Sentinel gets +0/+1 until end of turn.
+        addCard(Zone.BATTLEFIELD, playerB, "Adarkar Sentinel"); // Artifact Creature — Soldier (3/3)
+        addCard(Zone.BATTLEFIELD, playerB, "Island");
+
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{1}: ");
+        
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+                
+        execute();
+
+        assertPowerToughness(playerB, "Adarkar Sentinel", 3, 3);
+
+    }
+}
diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
index d50d688844..d83cb859ff 100644
--- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
+++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
@@ -1165,9 +1165,9 @@ public class TestPlayer implements Player {
         }
 
         if (mustHave) {
-            Assert.assertEquals(action.getActionName() + " - permanent " + permanentName + " must have ability " + abilityClass, true, founded);
+            Assert.assertEquals(action.getActionName() + " - permanent " + permanentName + " must have the ability " + abilityClass, true, founded);
         } else {
-            Assert.assertEquals(action.getActionName() + " - permanent " + permanentName + " must have not ability " + abilityClass, false, founded);
+            Assert.assertEquals(action.getActionName() + " - permanent " + permanentName + " must not have the ability " + abilityClass, false, founded);
         }
     }
 
diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java
index c7005478da..742f456bb4 100644
--- a/Mage/src/main/java/mage/players/PlayerImpl.java
+++ b/Mage/src/main/java/mage/players/PlayerImpl.java
@@ -3365,11 +3365,14 @@ public abstract class PlayerImpl implements Player, Serializable {
             // activated abilities from battlefield objects
             if (fromAll || fromZone == Zone.BATTLEFIELD) {
                 for (Permanent permanent : game.getBattlefield().getAllActivePermanents(playerId)) {
+                    boolean canUseActivated = permanent.canUseActivatedAbilities(game);
                     List<ActivatedAbility> battlePlayable = new ArrayList<>();
                     getPlayableFromCardAll(game, Zone.BATTLEFIELD, permanent, availableMana, battlePlayable);
                     for (ActivatedAbility ability : battlePlayable) {
-                        activatedUnique.putIfAbsent(ability.toString(), ability);
-                        activatedAll.add(ability);
+                        if (ability instanceof SpecialAction || canUseActivated) {
+                            activatedUnique.putIfAbsent(ability.toString(), ability);
+                            activatedAll.add(ability);
+                        }
                     }
                 }
             }
@@ -4272,7 +4275,7 @@ public abstract class PlayerImpl implements Player, Serializable {
         cards.addAll(getLibrary().getTopCards(game, value));
         if (!cards.isEmpty()) {
             TargetCard target = new TargetCard(0, cards.size(), Zone.LIBRARY,
-                    new FilterCard("card" + (cards.size() == 1 ? "":"s") 
+                    new FilterCard("card" + (cards.size() == 1 ? "" : "s")
                             + " to PUT on the BOTTOM of your library (Scry)"));
             chooseTarget(Outcome.Benefit, cards, target, source, game);
             putCardsOnBottomOfLibrary(new CardsImpl(target.getTargets()), game, source, true);

From 266c25c876e39b3df7cdb2bbdad57ab71630dba8 Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Thu, 18 Jun 2020 16:26:55 -0500
Subject: [PATCH 369/586] - Text fix

---
 .../abilities/common/BeginningOfUpkeepTriggeredAbility.java    | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/Mage/src/main/java/mage/abilities/common/BeginningOfUpkeepTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BeginningOfUpkeepTriggeredAbility.java
index 7969e308b5..021787292e 100644
--- a/Mage/src/main/java/mage/abilities/common/BeginningOfUpkeepTriggeredAbility.java
+++ b/Mage/src/main/java/mage/abilities/common/BeginningOfUpkeepTriggeredAbility.java
@@ -1,4 +1,3 @@
-
 package mage.abilities.common;
 
 import mage.abilities.TriggeredAbilityImpl;
@@ -137,7 +136,7 @@ public class BeginningOfUpkeepTriggeredAbility extends TriggeredAbilityImpl {
             case OPPONENT:
                 return sb.insert(0, generateZoneString()).insert(0, "At the beginning of each opponent's upkeep, ").toString();
             case ANY:
-                return sb.insert(0, generateZoneString()).insert(0, "At the beginning of each upkeep, ").toString();
+                return sb.insert(0, generateZoneString()).insert(0, "At the beginning of each player's upkeep, ").toString();
             case ACTIVE:
                 return sb.insert(0, generateZoneString()).insert(0, "At the beginning of each player's upkeep, ").toString();
             case CONTROLLER_ATTACHED_TO:

From f6cec7396981a488cf4803dd9a8eb8e57515c136 Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Thu, 18 Jun 2020 16:55:12 -0500
Subject: [PATCH 370/586] - Text fix

---
 .../src/mage/cards/c/CrypticCommand.java      | 22 ++++++++++++-------
 1 file changed, 14 insertions(+), 8 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/c/CrypticCommand.java b/Mage.Sets/src/mage/cards/c/CrypticCommand.java
index 0e18a518ba..53e291976e 100644
--- a/Mage.Sets/src/mage/cards/c/CrypticCommand.java
+++ b/Mage.Sets/src/mage/cards/c/CrypticCommand.java
@@ -1,9 +1,9 @@
-
 package mage.cards.c;
 
 import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.Mode;
+import mage.abilities.effects.Effect;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.CounterTargetEffect;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
@@ -27,18 +27,21 @@ import mage.target.TargetSpell;
 public final class CrypticCommand extends CardImpl {
 
     public CrypticCommand(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{U}{U}{U}");
-
+        super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}{U}{U}");
 
         // Choose two -
         this.getSpellAbility().getModes().setMinModes(2);
         this.getSpellAbility().getModes().setMaxModes(2);
         // Counter target spell;
-        this.getSpellAbility().addEffect(new CounterTargetEffect());
+        Effect effect1 = new CounterTargetEffect();
+        effect1.setText("Counter target spell.");
+        this.getSpellAbility().addEffect(effect1);
         this.getSpellAbility().addTarget(new TargetSpell());
         // or return target permanent to its owner's hand;
         Mode mode = new Mode();
-        mode.addEffect(new ReturnToHandTargetEffect());
+        Effect effect2 = new ReturnToHandTargetEffect();
+        effect2.setText("Return target permanent to its owner's hand.");
+        mode.addEffect(effect2);
         mode.addTarget(new TargetPermanent());
         this.getSpellAbility().getModes().addMode(mode);
         // or tap all creatures your opponents control;
@@ -47,7 +50,9 @@ public final class CrypticCommand extends CardImpl {
         this.getSpellAbility().getModes().addMode(mode);
         // or draw a card.
         mode = new Mode();
-        mode.addEffect(new DrawCardSourceControllerEffect(1));
+        Effect effect3 = new DrawCardSourceControllerEffect(1);
+        mode.addEffect(effect3);
+        effect3.setText("Draw a card.");
         this.getSpellAbility().getModes().addMode(mode);
     }
 
@@ -64,13 +69,14 @@ public final class CrypticCommand extends CardImpl {
 class CrypticCommandEffect extends OneShotEffect {
 
     private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls");
+
     static {
-      filter.add(TargetController.OPPONENT.getControllerPredicate());
+        filter.add(TargetController.OPPONENT.getControllerPredicate());
     }
 
     public CrypticCommandEffect() {
         super(Outcome.Tap);
-        staticText = "tap all creatures your opponents control";
+        staticText = "Tap all creatures your opponents control";
     }
 
     public CrypticCommandEffect(final CrypticCommandEffect effect) {

From c6e803a4e953055c203039fadd2af7d3c6376c6f Mon Sep 17 00:00:00 2001
From: arcox <10953229+arcox@users.noreply.github.com>
Date: Thu, 18 Jun 2020 22:05:15 +0000
Subject: [PATCH 371/586] Implement Run Afoul from Core 2021 (#6664)

---
 Mage.Sets/src/mage/cards/r/RunAfoul.java | 41 ++++++++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java |  1 +
 2 files changed, 42 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/r/RunAfoul.java

diff --git a/Mage.Sets/src/mage/cards/r/RunAfoul.java b/Mage.Sets/src/mage/cards/r/RunAfoul.java
new file mode 100644
index 0000000000..db304c47c4
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/r/RunAfoul.java
@@ -0,0 +1,41 @@
+package mage.cards.r;
+
+import java.util.UUID;
+
+import mage.abilities.effects.common.SacrificeEffect;
+import mage.abilities.keyword.FlyingAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.filter.common.FilterCreaturePermanent;
+import mage.filter.predicate.mageobject.AbilityPredicate;
+import mage.target.common.TargetOpponent;
+
+/**
+ * @author arcox
+ */
+public final class RunAfoul extends CardImpl {
+
+    private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with flying");
+
+    static {
+        filter.add(new AbilityPredicate(FlyingAbility.class));
+    }
+
+    public RunAfoul(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{G}");
+
+        // Target opponent sacrifices a creature with flying.
+        this.getSpellAbility().addEffect(new SacrificeEffect(filter, 1, "Target opponent"));
+        this.getSpellAbility().addTarget(new TargetOpponent());
+    }
+
+    public RunAfoul(final RunAfoul card) {
+        super(card);
+    }
+
+    @Override
+    public RunAfoul copy() {
+        return new RunAfoul(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index c69e99a7dc..15cf5c49fb 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -204,6 +204,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Roaming Ghostlight", 65, Rarity.COMMON, mage.cards.r.RoamingGhostlight.class));
         cards.add(new SetCardInfo("Rousing Read", 67, Rarity.COMMON, mage.cards.r.RousingRead.class));
         cards.add(new SetCardInfo("Rugged Highlands", 249, Rarity.COMMON, mage.cards.r.RuggedHighlands.class));
+        cards.add(new SetCardInfo("Run Afoul", 201, Rarity.COMMON, mage.cards.r.RunAfoul.class));
         cards.add(new SetCardInfo("Runed Halo", 32, Rarity.RARE, mage.cards.r.RunedHalo.class));
         cards.add(new SetCardInfo("Sabertooth Mauler", 202, Rarity.COMMON, mage.cards.s.SabertoothMauler.class));
         cards.add(new SetCardInfo("Sanctum of Calm Waters", 68, Rarity.UNCOMMON, mage.cards.s.SanctumOfCalmWaters.class));

From 096d44320d621ffa9ea721a46e99adb57ab66cda Mon Sep 17 00:00:00 2001
From: arcox <10953229+arcox@users.noreply.github.com>
Date: Thu, 18 Jun 2020 22:06:00 +0000
Subject: [PATCH 372/586] Implement Gloom Sower from Core 2021 (#6665)

* Implement Gloom Sower from Core 2021

* Remove unused imports
---
 Mage.Sets/src/mage/cards/g/GloomSower.java | 81 ++++++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java   |  1 +
 2 files changed, 82 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/g/GloomSower.java

diff --git a/Mage.Sets/src/mage/cards/g/GloomSower.java b/Mage.Sets/src/mage/cards/g/GloomSower.java
new file mode 100644
index 0000000000..29db3f13c6
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/g/GloomSower.java
@@ -0,0 +1,81 @@
+package mage.cards.g;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.BecomesBlockedByCreatureTriggeredAbility;
+import mage.abilities.effects.Effect;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.GainLifeEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Outcome;
+import mage.constants.SubType;
+import mage.game.Game;
+import mage.game.permanent.Permanent;
+import mage.players.Player;
+
+import java.util.UUID;
+
+/**
+ * @author arcox
+ */
+public final class GloomSower extends CardImpl {
+
+    public GloomSower(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{B}{B}");
+
+        this.subtype.add(SubType.HORROR);
+        this.power = new MageInt(8);
+        this.toughness = new MageInt(6);
+
+        // Whenever Gloom Sower becomes blocked by a creature, that creature’s controller loses 2 life and you gain 2 life.
+        Ability ability = new BecomesBlockedByCreatureTriggeredAbility(new GloomSowerEffect(), false);
+        Effect effect = new GainLifeEffect(2);
+        effect.setText("and you gain 2 life");
+        ability.addEffect(effect);
+        this.addAbility(ability);
+    }
+
+    private GloomSower(final GloomSower card) {
+        super(card);
+    }
+
+    @Override
+    public GloomSower copy() {
+        return new GloomSower(this);
+    }
+}
+
+class GloomSowerEffect extends OneShotEffect {
+
+    GloomSowerEffect() {
+        super(Outcome.LoseLife);
+        staticText = "that creature's controller loses 2 life";
+    }
+
+    private GloomSowerEffect(final GloomSowerEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public GloomSowerEffect copy() {
+        return new GloomSowerEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
+        if (permanent == null) {
+            return false;
+        }
+
+        Player player = game.getPlayer(permanent.getControllerId());
+        if (player == null) {
+            return false;
+        }
+
+        player.loseLife(2, game, false);
+        return true;
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 15cf5c49fb..3aa59a143f 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -120,6 +120,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Garruk, Savage Herald", 336, Rarity.MYTHIC, mage.cards.g.GarrukSavageHerald.class));
         cards.add(new SetCardInfo("Garruk, Unleashed", 183, Rarity.MYTHIC, mage.cards.g.GarrukUnleashed.class));
         cards.add(new SetCardInfo("Ghostly Pilferer", 52, Rarity.RARE, mage.cards.g.GhostlyPilferer.class));
+        cards.add(new SetCardInfo("Gloom Sower", 100, Rarity.COMMON, mage.cards.g.GloomSower.class));
         cards.add(new SetCardInfo("Glorious Anthem", 21, Rarity.RARE, mage.cards.g.GloriousAnthem.class));
         cards.add(new SetCardInfo("Goblin Arsonist", 147, Rarity.COMMON, mage.cards.g.GoblinArsonist.class));
         cards.add(new SetCardInfo("Goblin Wizardry", 148, Rarity.COMMON, mage.cards.g.GoblinWizardry.class));

From ab5daa026fc99070850b311760ed49326d8ade5a Mon Sep 17 00:00:00 2001
From: arcox <10953229+arcox@users.noreply.github.com>
Date: Thu, 18 Jun 2020 22:06:56 +0000
Subject: [PATCH 373/586] Implement Gnarled Sage from Core 2021 (#6666)

---
 Mage.Sets/src/mage/cards/g/GnarledSage.java | 72 +++++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java    |  1 +
 2 files changed, 73 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/g/GnarledSage.java

diff --git a/Mage.Sets/src/mage/cards/g/GnarledSage.java b/Mage.Sets/src/mage/cards/g/GnarledSage.java
new file mode 100644
index 0000000000..8b5ff451c9
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/g/GnarledSage.java
@@ -0,0 +1,72 @@
+package mage.cards.g;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.condition.Condition;
+import mage.abilities.decorator.ConditionalContinuousEffect;
+import mage.abilities.effects.common.continuous.BoostSourceEffect;
+import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
+import mage.abilities.keyword.ReachAbility;
+import mage.abilities.keyword.VigilanceAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+import mage.game.Game;
+import mage.watchers.common.CardsDrawnThisTurnWatcher;
+
+import java.util.UUID;
+
+/**
+ * @author arcox
+ */
+public final class GnarledSage extends CardImpl {
+
+    public GnarledSage(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}{G}");
+
+        this.subtype.add(SubType.TREEFOLK);
+        this.subtype.add(SubType.DRUID);
+        this.power = new MageInt(4);
+        this.toughness = new MageInt(4);
+
+        this.addAbility(ReachAbility.getInstance());
+
+        // As long as you’ve drawn two or more cards this turn, Gnarled Sage gets +0/+2 and has vigilance.
+        Ability ability = new SimpleStaticAbility(
+                new ConditionalContinuousEffect(
+                        new BoostSourceEffect(0, 2, Duration.WhileOnBattlefield),
+                        GnarledSageCondition.instance,
+                        "As long as you've drawn two or more cards this turn, {this} gets +0/+2"
+                ));
+
+        ability.addEffect(new ConditionalContinuousEffect(
+                new GainAbilitySourceEffect(VigilanceAbility.getInstance(), Duration.WhileOnBattlefield),
+                GnarledSageCondition.instance,
+                "and has vigilance"
+        ));
+
+        this.addAbility(ability, new CardsDrawnThisTurnWatcher());
+    }
+
+    private GnarledSage(final GnarledSage card) {
+        super(card);
+    }
+
+    @Override
+    public GnarledSage copy() {
+        return new GnarledSage(this);
+    }
+}
+
+enum GnarledSageCondition implements Condition {
+    instance;
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        CardsDrawnThisTurnWatcher watcher = game.getState().getWatcher(CardsDrawnThisTurnWatcher.class);
+        return watcher != null && watcher.getCardsDrawnThisTurn(source.getControllerId()) > 1;
+    }
+}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 3aa59a143f..a91e9dbf19 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -122,6 +122,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Ghostly Pilferer", 52, Rarity.RARE, mage.cards.g.GhostlyPilferer.class));
         cards.add(new SetCardInfo("Gloom Sower", 100, Rarity.COMMON, mage.cards.g.GloomSower.class));
         cards.add(new SetCardInfo("Glorious Anthem", 21, Rarity.RARE, mage.cards.g.GloriousAnthem.class));
+        cards.add(new SetCardInfo("Gnarled Sage", 187, Rarity.COMMON, mage.cards.g.GnarledSage.class));
         cards.add(new SetCardInfo("Goblin Arsonist", 147, Rarity.COMMON, mage.cards.g.GoblinArsonist.class));
         cards.add(new SetCardInfo("Goblin Wizardry", 148, Rarity.COMMON, mage.cards.g.GoblinWizardry.class));
         cards.add(new SetCardInfo("Goremand", 101, Rarity.UNCOMMON, mage.cards.g.Goremand.class));

From b84e0c03f83436dca84014208058e88f62d00f5c Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Thu, 18 Jun 2020 17:14:32 -0500
Subject: [PATCH 374/586] - Fix test

---
 .../org/mage/test/cards/enchantments/OathOfLiegesTest.java    | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/OathOfLiegesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/OathOfLiegesTest.java
index 9efb008a8e..ea8f4bac68 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/OathOfLiegesTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/OathOfLiegesTest.java
@@ -151,14 +151,14 @@ public class OathOfLiegesTest extends CardTestPlayerBase {
         // turn 2 - B
         // oath A triggers for B and do nothing
         // copy oath A triggers for B and do nothing
-        setChoice(playerA, "At the beginning of each upkeep"); // two triggers on upkeep
+        setChoice(playerA, "At the beginning of each player's upkeep"); // two triggers on upkeep
         checkPermanentCount("A have 10 plains", 1, PhaseStep.END_TURN, playerA, "Plains", 10);
         checkPermanentCount("B have 12 plains", 1, PhaseStep.END_TURN, playerB, "Plains", 12);
 
         // turn 3 - A
         // oath A triggers for A and activates
         // copy oath A triggers for A and activates
-        setChoice(playerA, "At the beginning of each upkeep"); // two triggers on upkeep
+        setChoice(playerA, "At the beginning of each player's upkeep"); // two triggers on upkeep
         // 1
         addTarget(playerA, playerB); // who control more lands
         setChoice(playerA, "Yes"); // search library

From 83abaf5ef4fcbbf8eccc1cd790202c4cb0a865c7 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 18 Jun 2020 18:11:38 -0400
Subject: [PATCH 375/586] Implemented Aven Gagglemaster

---
 .../src/mage/cards/a/AvenGagglemaster.java    | 56 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 57 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/a/AvenGagglemaster.java

diff --git a/Mage.Sets/src/mage/cards/a/AvenGagglemaster.java b/Mage.Sets/src/mage/cards/a/AvenGagglemaster.java
new file mode 100644
index 0000000000..46f82ea96f
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/a/AvenGagglemaster.java
@@ -0,0 +1,56 @@
+package mage.cards.a;
+
+import mage.MageInt;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.dynamicvalue.DynamicValue;
+import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
+import mage.abilities.effects.common.GainLifeEffect;
+import mage.abilities.keyword.FlyingAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.filter.FilterPermanent;
+import mage.filter.common.FilterCreaturePermanent;
+import mage.filter.predicate.mageobject.AbilityPredicate;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class AvenGagglemaster extends CardImpl {
+
+    private static final FilterPermanent filter = new FilterCreaturePermanent();
+
+    static {
+        filter.add(new AbilityPredicate(FlyingAbility.class));
+    }
+
+    private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter, 2);
+
+    public AvenGagglemaster(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}{W}");
+
+        this.subtype.add(SubType.BIRD);
+        this.subtype.add(SubType.WARRIOR);
+        this.power = new MageInt(4);
+        this.toughness = new MageInt(3);
+
+        // Flying
+        this.addAbility(FlyingAbility.getInstance());
+
+        // When Aven Gagglemaster enters the battlefield, you gain 2 life for each creature you control with flying.
+        this.addAbility(new EntersBattlefieldTriggeredAbility(new GainLifeEffect(xValue)
+                .setText("you gain 2 life for each creature you control with flying")));
+    }
+
+    private AvenGagglemaster(final AvenGagglemaster card) {
+        super(card);
+    }
+
+    @Override
+    public AvenGagglemaster copy() {
+        return new AvenGagglemaster(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index a91e9dbf19..91536f727a 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -40,6 +40,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Angelic Ascension", 3, Rarity.UNCOMMON, mage.cards.a.AngelicAscension.class));
         cards.add(new SetCardInfo("Animal Sanctuary", 242, Rarity.RARE, mage.cards.a.AnimalSanctuary.class));
         cards.add(new SetCardInfo("Anointed Chorister", 4, Rarity.COMMON, mage.cards.a.AnointedChorister.class));
+        cards.add(new SetCardInfo("Aven Gagglemaster", 5, Rarity.UNCOMMON, mage.cards.a.AvenGagglemaster.class));
         cards.add(new SetCardInfo("Azusa, Lost but Seeking", 173, Rarity.RARE, mage.cards.a.AzusaLostButSeeking.class));
         cards.add(new SetCardInfo("Bad Deal", 89, Rarity.UNCOMMON, mage.cards.b.BadDeal.class));
         cards.add(new SetCardInfo("Baneslayer Angel", 6, Rarity.MYTHIC, mage.cards.b.BaneslayerAngel.class));

From 26cc800846f47aa4f6936cc5570f360d0b69da98 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 18 Jun 2020 18:25:08 -0400
Subject: [PATCH 376/586] Implemented Leafkin Avenger

---
 .../src/mage/cards/l/LeafkinAvenger.java      | 68 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 69 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/l/LeafkinAvenger.java

diff --git a/Mage.Sets/src/mage/cards/l/LeafkinAvenger.java b/Mage.Sets/src/mage/cards/l/LeafkinAvenger.java
new file mode 100644
index 0000000000..587e7d4249
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/l/LeafkinAvenger.java
@@ -0,0 +1,68 @@
+package mage.cards.l;
+
+import mage.MageInt;
+import mage.Mana;
+import mage.abilities.Ability;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.dynamicvalue.DynamicValue;
+import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
+import mage.abilities.dynamicvalue.common.SourcePermanentPowerCount;
+import mage.abilities.effects.common.DamageTargetEffect;
+import mage.abilities.mana.DynamicManaAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.ComparisonType;
+import mage.constants.SubType;
+import mage.filter.FilterPermanent;
+import mage.filter.common.FilterCreaturePermanent;
+import mage.filter.predicate.mageobject.PowerPredicate;
+import mage.target.common.TargetPlayerOrPlaneswalker;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class LeafkinAvenger extends CardImpl {
+
+    private static final FilterPermanent filter = new FilterCreaturePermanent("creature you control with power 4 or greater");
+
+    static {
+        filter.add(new PowerPredicate(ComparisonType.MORE_THAN, 3));
+    }
+
+    private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter);
+    private static final DynamicValue xValue2 = new SourcePermanentPowerCount(false);
+
+    public LeafkinAvenger(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{G}");
+
+        this.subtype.add(SubType.ELEMENTAL);
+        this.subtype.add(SubType.DRUID);
+        this.power = new MageInt(4);
+        this.toughness = new MageInt(3);
+
+        // {T}: Add {G} for each creature you control with power 4 or greater.
+        this.addAbility(new DynamicManaAbility(Mana.GreenMana(1), xValue));
+
+        // {7}{R}: Leafkin Avenger deals damage equal to its power to target player or planeswalker.
+        Ability ability = new SimpleActivatedAbility(
+                new DamageTargetEffect(xValue2)
+                        .setText("{this} deals damage equal to its power to target player or planeswalker"),
+                new ManaCostsImpl("{7}{R}")
+        );
+        ability.addTarget(new TargetPlayerOrPlaneswalker());
+        this.addAbility(ability);
+    }
+
+    private LeafkinAvenger(final LeafkinAvenger card) {
+        super(card);
+    }
+
+    @Override
+    public LeafkinAvenger copy() {
+        return new LeafkinAvenger(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 91536f727a..a213df1e94 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -149,6 +149,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Keen Glidemaster", 54, Rarity.COMMON, mage.cards.k.KeenGlidemaster.class));
         cards.add(new SetCardInfo("Keral Keep Disciples", 334, Rarity.UNCOMMON, mage.cards.k.KeralKeepDisciples.class));
         cards.add(new SetCardInfo("Kitesail Freebooter", 107, Rarity.UNCOMMON, mage.cards.k.KitesailFreebooter.class));
+        cards.add(new SetCardInfo("Leafkin Avenger", 220, Rarity.UNCOMMON, mage.cards.l.LeafkinAvenger.class));
         cards.add(new SetCardInfo("Legion's Judgment", 24, Rarity.COMMON, mage.cards.l.LegionsJudgment.class));
         cards.add(new SetCardInfo("Library Larcenist", 55, Rarity.COMMON, mage.cards.l.LibraryLarcenist.class));
         cards.add(new SetCardInfo("Life Goes On", 192, Rarity.COMMON, mage.cards.l.LifeGoesOn.class));

From c85ee88045f237c3847de4b51ea81c7d8123b606 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 18 Jun 2020 18:28:19 -0400
Subject: [PATCH 377/586] Implemented Ornery Dilophosaur

---
 .../src/mage/cards/o/OrneryDilophosaur.java   | 50 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 51 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/o/OrneryDilophosaur.java

diff --git a/Mage.Sets/src/mage/cards/o/OrneryDilophosaur.java b/Mage.Sets/src/mage/cards/o/OrneryDilophosaur.java
new file mode 100644
index 0000000000..fce5c2b72c
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/o/OrneryDilophosaur.java
@@ -0,0 +1,50 @@
+package mage.cards.o;
+
+import mage.MageInt;
+import mage.abilities.common.AttacksTriggeredAbility;
+import mage.abilities.condition.common.FerociousCondition;
+import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
+import mage.abilities.effects.common.continuous.BoostSourceEffect;
+import mage.abilities.hint.common.FerociousHint;
+import mage.abilities.keyword.DeathtouchAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class OrneryDilophosaur extends CardImpl {
+
+    public OrneryDilophosaur(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}");
+
+        this.subtype.add(SubType.DINOSAUR);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(2);
+
+        // Deathtouch
+        this.addAbility(DeathtouchAbility.getInstance());
+
+        // Whenever Ornery Dilophosaur attacks, if you control a creature with power 4 or greater, Ornery Dilophosaur gets +2/+2 until end of turn.
+        this.addAbility(new ConditionalInterveningIfTriggeredAbility(
+                new AttacksTriggeredAbility(
+                        new BoostSourceEffect(2, 2, Duration.EndOfTurn), false
+                ), FerociousCondition.instance, "Whenever {this} attacks, " +
+                "if you control a creature with power 4 or greater, {this} gets +2/+2 until end of turn."
+        ).addHint(FerociousHint.instance));
+    }
+
+    private OrneryDilophosaur(final OrneryDilophosaur card) {
+        super(card);
+    }
+
+    @Override
+    public OrneryDilophosaur copy() {
+        return new OrneryDilophosaur(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index a213df1e94..9b636f5c99 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -181,6 +181,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Obsessive Stitcher", 223, Rarity.UNCOMMON, mage.cards.o.ObsessiveStitcher.class));
         cards.add(new SetCardInfo("Onakke Ogre", 155, Rarity.COMMON, mage.cards.o.OnakkeOgre.class));
         cards.add(new SetCardInfo("Opt", 59, Rarity.COMMON, mage.cards.o.Opt.class));
+        cards.add(new SetCardInfo("Ornery Dilophosaur", 194, Rarity.COMMON, mage.cards.o.OrneryDilophosaur.class));
         cards.add(new SetCardInfo("Pack Leader", 29, Rarity.RARE, mage.cards.p.PackLeader.class));
         cards.add(new SetCardInfo("Palladium Myr", 234, Rarity.UNCOMMON, mage.cards.p.PalladiumMyr.class));
         cards.add(new SetCardInfo("Peer into the Abyss", 117, Rarity.RARE, mage.cards.p.PeerIntoTheAbyss.class));

From 34aa019cec4e04d497d9176458ed2efa4af910e1 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 18 Jun 2020 18:41:01 -0400
Subject: [PATCH 378/586] Implemented Rookie Mistake

---
 Mage.Sets/src/mage/cards/r/RookieMistake.java | 80 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 81 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/r/RookieMistake.java

diff --git a/Mage.Sets/src/mage/cards/r/RookieMistake.java b/Mage.Sets/src/mage/cards/r/RookieMistake.java
new file mode 100644
index 0000000000..9946d80a52
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/r/RookieMistake.java
@@ -0,0 +1,80 @@
+package mage.cards.r;
+
+import mage.abilities.Ability;
+import mage.abilities.effects.ContinuousEffect;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.continuous.BoostTargetEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.Outcome;
+import mage.game.Game;
+import mage.game.permanent.Permanent;
+import mage.target.TargetPermanent;
+import mage.target.common.TargetCreaturePermanent;
+import mage.target.targetpointer.FixedTarget;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class RookieMistake extends CardImpl {
+
+    public RookieMistake(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}");
+
+        // Until end of turn, target creature gets +0/+2 and another target creature gets -2/-0.
+        this.getSpellAbility().addEffect(new RookieMistakeEffect());
+        TargetPermanent target = new TargetCreaturePermanent();
+        target.setTargetTag(1);
+        this.getSpellAbility().addTarget(target.withChooseHint("+0/+2"));
+        target = new TargetCreaturePermanent();
+        target.setTargetTag(2);
+        this.getSpellAbility().addTarget(target.withChooseHint("-2/-0"));
+    }
+
+    private RookieMistake(final RookieMistake card) {
+        super(card);
+    }
+
+    @Override
+    public RookieMistake copy() {
+        return new RookieMistake(this);
+    }
+}
+
+class RookieMistakeEffect extends OneShotEffect {
+
+    RookieMistakeEffect() {
+        super(Outcome.BoostCreature);
+        this.staticText = "until end of turn, target creature gets +0/+2 and another target creature gets -2/-0";
+    }
+
+    private RookieMistakeEffect(final RookieMistakeEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public RookieMistakeEffect copy() {
+        return new RookieMistakeEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Permanent permanent = game.getPermanent(source.getFirstTarget());
+        if (permanent != null) {
+            ContinuousEffect effect = new BoostTargetEffect(0, 2, Duration.EndOfTurn);
+            effect.setTargetPointer(new FixedTarget(permanent, game));
+            game.addEffect(effect, source);
+        }
+        permanent = game.getPermanent(source.getTargets().get(1).getFirstTarget());
+        if (permanent != null) {
+            ContinuousEffect effect = new BoostTargetEffect(-2, 0, Duration.EndOfTurn);
+            effect.setTargetPointer(new FixedTarget(permanent, game));
+            game.addEffect(effect, source);
+        }
+        return true;
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 9b636f5c99..d031802fab 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -207,6 +207,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Rin and Seri, Inseparable", 278, Rarity.MYTHIC, mage.cards.r.RinAndSeriInseparable.class));
         cards.add(new SetCardInfo("Rise Again", 119, Rarity.COMMON, mage.cards.r.RiseAgain.class));
         cards.add(new SetCardInfo("Roaming Ghostlight", 65, Rarity.COMMON, mage.cards.r.RoamingGhostlight.class));
+        cards.add(new SetCardInfo("Rookie Mistake", 66, Rarity.COMMON, mage.cards.r.RookieMistake.class));
         cards.add(new SetCardInfo("Rousing Read", 67, Rarity.COMMON, mage.cards.r.RousingRead.class));
         cards.add(new SetCardInfo("Rugged Highlands", 249, Rarity.COMMON, mage.cards.r.RuggedHighlands.class));
         cards.add(new SetCardInfo("Run Afoul", 201, Rarity.COMMON, mage.cards.r.RunAfoul.class));

From 3a40b9dfb311638e29a9640bcb3178c9977349ac Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 18 Jun 2020 18:51:03 -0400
Subject: [PATCH 379/586] Implemented Kinetic Augur

---
 Mage.Sets/src/mage/cards/k/KineticAugur.java | 91 ++++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java     |  1 +
 2 files changed, 92 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/k/KineticAugur.java

diff --git a/Mage.Sets/src/mage/cards/k/KineticAugur.java b/Mage.Sets/src/mage/cards/k/KineticAugur.java
new file mode 100644
index 0000000000..458110b550
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/k/KineticAugur.java
@@ -0,0 +1,91 @@
+package mage.cards.k;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.dynamicvalue.DynamicValue;
+import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.continuous.SetPowerSourceEffect;
+import mage.abilities.keyword.TrampleAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.cards.CardsImpl;
+import mage.constants.*;
+import mage.filter.FilterCard;
+import mage.filter.StaticFilters;
+import mage.filter.common.FilterInstantOrSorceryCard;
+import mage.game.Game;
+import mage.players.Player;
+import mage.target.common.TargetDiscard;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class KineticAugur extends CardImpl {
+
+    private static final FilterCard filter = new FilterInstantOrSorceryCard("instant and sorcery cards");
+    private static final DynamicValue xValue = new CardsInControllerGraveyardCount(filter);
+
+    public KineticAugur(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}");
+
+        this.subtype.add(SubType.HUMAN);
+        this.subtype.add(SubType.SHAMAN);
+        this.power = new MageInt(0);
+        this.toughness = new MageInt(4);
+
+        // Trample
+        this.addAbility(TrampleAbility.getInstance());
+
+        // Kinetic Augur's power is equal to the number of instant and sorcery cards in your graveyard.
+        this.addAbility(new SimpleStaticAbility(Zone.ALL, new SetPowerSourceEffect(xValue, Duration.EndOfGame)));
+
+        // When Kinetic Augur enters the battlefield, discard up to two cards, then draw that many cards.
+        this.addAbility(new EntersBattlefieldTriggeredAbility(new KineticAugurEffect()));
+    }
+
+    private KineticAugur(final KineticAugur card) {
+        super(card);
+    }
+
+    @Override
+    public KineticAugur copy() {
+        return new KineticAugur(this);
+    }
+}
+
+class KineticAugurEffect extends OneShotEffect {
+
+    KineticAugurEffect() {
+        super(Outcome.Benefit);
+        staticText = "discard up to two cards, then draw that many cards";
+    }
+
+    private KineticAugurEffect(final KineticAugurEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public KineticAugurEffect copy() {
+        return new KineticAugurEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Player player = game.getPlayer(source.getControllerId());
+        if (player == null || player.getHand().isEmpty()) {
+            return false;
+        }
+        TargetDiscard target = new TargetDiscard(0, 2, StaticFilters.FILTER_CARD, player.getId());
+        player.choose(Outcome.AIDontUseIt, player.getHand(), target, game);
+        int discarded = player.discard(new CardsImpl(target.getTargets()), source, game).size();
+        if (discarded > 0) {
+            player.drawCards(discarded, source.getSourceId(), game);
+        }
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index d031802fab..ad41272646 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -148,6 +148,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Kaervek, the Spiteful", 106, Rarity.RARE, mage.cards.k.KaervekTheSpiteful.class));
         cards.add(new SetCardInfo("Keen Glidemaster", 54, Rarity.COMMON, mage.cards.k.KeenGlidemaster.class));
         cards.add(new SetCardInfo("Keral Keep Disciples", 334, Rarity.UNCOMMON, mage.cards.k.KeralKeepDisciples.class));
+        cards.add(new SetCardInfo("Kinetic Augur", 154, Rarity.UNCOMMON, mage.cards.k.KineticAugur.class));
         cards.add(new SetCardInfo("Kitesail Freebooter", 107, Rarity.UNCOMMON, mage.cards.k.KitesailFreebooter.class));
         cards.add(new SetCardInfo("Leafkin Avenger", 220, Rarity.UNCOMMON, mage.cards.l.LeafkinAvenger.class));
         cards.add(new SetCardInfo("Legion's Judgment", 24, Rarity.COMMON, mage.cards.l.LegionsJudgment.class));

From e1a396d145a989bbac5216edbaef92f9febf6967 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 18 Jun 2020 18:53:16 -0400
Subject: [PATCH 380/586] Implemented Secure the Scene

---
 .../src/mage/cards/s/SecureTheScene.java      | 68 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 69 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/s/SecureTheScene.java

diff --git a/Mage.Sets/src/mage/cards/s/SecureTheScene.java b/Mage.Sets/src/mage/cards/s/SecureTheScene.java
new file mode 100644
index 0000000000..60a79e6f4a
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/SecureTheScene.java
@@ -0,0 +1,68 @@
+package mage.cards.s;
+
+import mage.abilities.Ability;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.ExileTargetEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Outcome;
+import mage.game.Game;
+import mage.game.permanent.Permanent;
+import mage.game.permanent.token.SoldierToken;
+import mage.game.permanent.token.Token;
+import mage.target.common.TargetNonlandPermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class SecureTheScene extends CardImpl {
+
+    public SecureTheScene(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{W}");
+
+        // Exile target nonland permanent. Its controller creates a 1/1 white Soldier creature token.
+        this.getSpellAbility().addEffect(new ExileTargetEffect());
+        this.getSpellAbility().addEffect(new SecureTheSceneEffect());
+        this.getSpellAbility().addTarget(new TargetNonlandPermanent());
+    }
+
+    private SecureTheScene(final SecureTheScene card) {
+        super(card);
+    }
+
+    @Override
+    public SecureTheScene copy() {
+        return new SecureTheScene(this);
+    }
+}
+
+class SecureTheSceneEffect extends OneShotEffect {
+
+    private static final Token token = new SoldierToken();
+
+    SecureTheSceneEffect() {
+        super(Outcome.PutCreatureInPlay);
+        this.staticText = "Its controller creates a 1/1 white Soldier creature token";
+    }
+
+    private SecureTheSceneEffect(final SecureTheSceneEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public SecureTheSceneEffect copy() {
+        return new SecureTheSceneEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Permanent permanent = game.getPermanentOrLKIBattlefield(source.getFirstTarget());
+        if (permanent == null) {
+            return false;
+        }
+        return token.putOntoBattlefield(1, game, source.getSourceId(), permanent.getControllerId());
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index ad41272646..f7229c5ad3 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -220,6 +220,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Scorching Dragonfire", 158, Rarity.COMMON, mage.cards.s.ScorchingDragonfire.class));
         cards.add(new SetCardInfo("Scoured Barrens", 250, Rarity.COMMON, mage.cards.s.ScouredBarrens.class));
         cards.add(new SetCardInfo("Seasoned Hallowblade", 34, Rarity.UNCOMMON, mage.cards.s.SeasonedHallowblade.class));
+        cards.add(new SetCardInfo("Secure the Scene", 35, Rarity.COMMON, mage.cards.s.SecureTheScene.class));
         cards.add(new SetCardInfo("See the Truth", 69, Rarity.RARE, mage.cards.s.SeeTheTruth.class));
         cards.add(new SetCardInfo("Selfless Savior", 36, Rarity.UNCOMMON, mage.cards.s.SelflessSavior.class));
         cards.add(new SetCardInfo("Setessan Training", 205, Rarity.COMMON, mage.cards.s.SetessanTraining.class));

From dffd3cc86d638cf89d1b4fbb8c73d87acad96f5d Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 18 Jun 2020 18:56:42 -0400
Subject: [PATCH 381/586] Implemented Sanguine Indulgence

---
 .../src/mage/cards/s/SanguineIndulgence.java  | 51 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 52 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/s/SanguineIndulgence.java

diff --git a/Mage.Sets/src/mage/cards/s/SanguineIndulgence.java b/Mage.Sets/src/mage/cards/s/SanguineIndulgence.java
new file mode 100644
index 0000000000..d81bac3e94
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/SanguineIndulgence.java
@@ -0,0 +1,51 @@
+package mage.cards.s;
+
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.condition.Condition;
+import mage.abilities.condition.common.YouGainedLifeCondition;
+import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect;
+import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect;
+import mage.abilities.hint.ConditionHint;
+import mage.abilities.hint.Hint;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.ComparisonType;
+import mage.constants.Zone;
+import mage.filter.StaticFilters;
+import mage.target.common.TargetCardInYourGraveyard;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class SanguineIndulgence extends CardImpl {
+
+    private static final Condition condition = new YouGainedLifeCondition(ComparisonType.MORE_THAN, 2);
+    private static final Hint hint = new ConditionHint(condition, "You gained 3 or more life this turn");
+
+    public SanguineIndulgence(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{B}");
+
+        // This spell costs {3} less to cast if you've gained 3 or more life this turn.
+        this.addAbility(new SimpleStaticAbility(
+                Zone.ALL, new SpellCostReductionSourceEffect(3, condition)
+        ).addHint(hint));
+
+        // Return up to two target creature cards from your graveyard to your hand.
+        this.getSpellAbility().addEffect(new ReturnFromGraveyardToHandTargetEffect());
+        this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(
+                0, 2, StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD
+        ));
+    }
+
+    private SanguineIndulgence(final SanguineIndulgence card) {
+        super(card);
+    }
+
+    @Override
+    public SanguineIndulgence copy() {
+        return new SanguineIndulgence(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index f7229c5ad3..b9445adc03 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -216,6 +216,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Sabertooth Mauler", 202, Rarity.COMMON, mage.cards.s.SabertoothMauler.class));
         cards.add(new SetCardInfo("Sanctum of Calm Waters", 68, Rarity.UNCOMMON, mage.cards.s.SanctumOfCalmWaters.class));
         cards.add(new SetCardInfo("Sanctum of Shattered Heights", 157, Rarity.UNCOMMON, mage.cards.s.SanctumOfShatteredHeights.class));
+        cards.add(new SetCardInfo("Sanguine Indulgence", 121, Rarity.COMMON, mage.cards.s.SanguineIndulgence.class));
         cards.add(new SetCardInfo("Scavenging Ooze", 204, Rarity.RARE, mage.cards.s.ScavengingOoze.class));
         cards.add(new SetCardInfo("Scorching Dragonfire", 158, Rarity.COMMON, mage.cards.s.ScorchingDragonfire.class));
         cards.add(new SetCardInfo("Scoured Barrens", 250, Rarity.COMMON, mage.cards.s.ScouredBarrens.class));

From 0b2f44e1273d8158126b9bb2141a055c4b587d2a Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 18 Jun 2020 18:59:40 -0400
Subject: [PATCH 382/586] updated JMP spoiler and reprints

---
 Mage.Sets/src/mage/sets/Jumpstart.java | 107 ++++++++++++++++++++++-
 Utils/mtg-cards-data.txt               | 113 ++++++++++++++++++++++++-
 2 files changed, 214 insertions(+), 6 deletions(-)

diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index e98a94b816..1b29e3972a 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -21,12 +21,17 @@ public final class Jumpstart extends ExpansionSet {
         this.hasBasicLands = true;
 
         cards.add(new SetCardInfo("Aether Spellbomb", 456, Rarity.COMMON, mage.cards.a.AetherSpellbomb.class));
+        cards.add(new SetCardInfo("Affa Guard Hound", 81, Rarity.UNCOMMON, mage.cards.a.AffaGuardHound.class));
         cards.add(new SetCardInfo("Affectionate Indrik", 373, Rarity.UNCOMMON, mage.cards.a.AffectionateIndrik.class));
         cards.add(new SetCardInfo("Aggressive Urge", 374, Rarity.COMMON, mage.cards.a.AggressiveUrge.class));
         cards.add(new SetCardInfo("Agonizing Syphon", 199, Rarity.COMMON, mage.cards.a.AgonizingSyphon.class));
         cards.add(new SetCardInfo("Alloy Myr", 457, Rarity.COMMON, mage.cards.a.AlloyMyr.class));
         cards.add(new SetCardInfo("Ambassador Oak", 375, Rarity.COMMON, mage.cards.a.AmbassadorOak.class));
         cards.add(new SetCardInfo("Ancestral Statue", 458, Rarity.COMMON, mage.cards.a.AncestralStatue.class));
+        cards.add(new SetCardInfo("Arcane Encyclopedia", 459, Rarity.UNCOMMON, mage.cards.a.ArcaneEncyclopedia.class));
+        cards.add(new SetCardInfo("Ashmouth Hound", 290, Rarity.COMMON, mage.cards.a.AshmouthHound.class));
+        cards.add(new SetCardInfo("Assassin's Strike", 200, Rarity.UNCOMMON, mage.cards.a.AssassinsStrike.class));
+        cards.add(new SetCardInfo("Assault Formation", 378, Rarity.RARE, mage.cards.a.AssaultFormation.class));
         cards.add(new SetCardInfo("Auger Spree", 449, Rarity.COMMON, mage.cards.a.AugerSpree.class));
         cards.add(new SetCardInfo("Awakener Druid", 379, Rarity.UNCOMMON, mage.cards.a.AwakenerDruid.class));
         cards.add(new SetCardInfo("Bake into a Pie", 201, Rarity.COMMON, mage.cards.b.BakeIntoAPie.class));
@@ -38,103 +43,199 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Blood Host", 208, Rarity.UNCOMMON, mage.cards.b.BloodHost.class));
         cards.add(new SetCardInfo("Bloodbond Vampire", 209, Rarity.UNCOMMON, mage.cards.b.BloodbondVampire.class));
         cards.add(new SetCardInfo("Bloodhunter Bat", 210, Rarity.COMMON, mage.cards.b.BloodhunterBat.class));
+        cards.add(new SetCardInfo("Bloodrock Cyclops", 297, Rarity.COMMON, mage.cards.b.BloodrockCyclops.class));
         cards.add(new SetCardInfo("Bogbrew Witch", 211, Rarity.RARE, mage.cards.b.BogbrewWitch.class));
         cards.add(new SetCardInfo("Bone Picker", 212, Rarity.UNCOMMON, mage.cards.b.BonePicker.class));
+        cards.add(new SetCardInfo("Bone Splinters", 213, Rarity.COMMON, mage.cards.b.BoneSplinters.class));
+        cards.add(new SetCardInfo("Brindle Shoat", 380, Rarity.UNCOMMON, mage.cards.b.BrindleShoat.class));
         cards.add(new SetCardInfo("Brushstrider", 381, Rarity.UNCOMMON, mage.cards.b.Brushstrider.class));
         cards.add(new SetCardInfo("Bubbling Cauldron", 460, Rarity.UNCOMMON, mage.cards.b.BubblingCauldron.class));
+        cards.add(new SetCardInfo("Burglar Rat", 214, Rarity.COMMON, mage.cards.b.BurglarRat.class));
         cards.add(new SetCardInfo("Buried Ruin", 491, Rarity.UNCOMMON, mage.cards.b.BuriedRuin.class));
+        cards.add(new SetCardInfo("Cadaver Imp", 215, Rarity.COMMON, mage.cards.c.CadaverImp.class));
+        cards.add(new SetCardInfo("Carven Caryatid", 382, Rarity.UNCOMMON, mage.cards.c.CarvenCaryatid.class));
+        cards.add(new SetCardInfo("Cathar's Companion", 94, Rarity.COMMON, mage.cards.c.CatharsCompanion.class));
+        cards.add(new SetCardInfo("Cauldron Familiar", 216, Rarity.COMMON, mage.cards.c.CauldronFamiliar.class));
         cards.add(new SetCardInfo("Cemetery Recruitment", 217, Rarity.COMMON, mage.cards.c.CemeteryRecruitment.class));
         cards.add(new SetCardInfo("Chamber Sentry", 461, Rarity.RARE, mage.cards.c.ChamberSentry.class));
         cards.add(new SetCardInfo("Child of Night", 218, Rarity.COMMON, mage.cards.c.ChildOfNight.class));
         cards.add(new SetCardInfo("Chromatic Sphere", 462, Rarity.COMMON, mage.cards.c.ChromaticSphere.class));
+        cards.add(new SetCardInfo("Cinder Elemental", 304, Rarity.UNCOMMON, mage.cards.c.CinderElemental.class));
+        cards.add(new SetCardInfo("Cloudreader Sphinx", 143, Rarity.COMMON, mage.cards.c.CloudreaderSphinx.class));
+        cards.add(new SetCardInfo("Commune with Dinosaurs", 384, Rarity.COMMON, mage.cards.c.CommuneWithDinosaurs.class));
+        cards.add(new SetCardInfo("Craterhoof Behemoth", 385, Rarity.MYTHIC, mage.cards.c.CraterhoofBehemoth.class));
         cards.add(new SetCardInfo("Crookclaw Transmuter", 145, Rarity.COMMON, mage.cards.c.CrookclawTransmuter.class));
+        cards.add(new SetCardInfo("Crow of Dark Tidings", 221, Rarity.COMMON, mage.cards.c.CrowOfDarkTidings.class));
         cards.add(new SetCardInfo("Crushing Canopy", 386, Rarity.COMMON, mage.cards.c.CrushingCanopy.class));
+        cards.add(new SetCardInfo("Curiosity", 147, Rarity.UNCOMMON, mage.cards.c.Curiosity.class));
+        cards.add(new SetCardInfo("Dawntreader Elk", 387, Rarity.COMMON, mage.cards.d.DawntreaderElk.class));
+        cards.add(new SetCardInfo("Death's Approach", 222, Rarity.COMMON, mage.cards.d.DeathsApproach.class));
         cards.add(new SetCardInfo("Dinrova Horror", 450, Rarity.UNCOMMON, mage.cards.d.DinrovaHorror.class));
+        cards.add(new SetCardInfo("Drainpipe Vermin", 224, Rarity.COMMON, mage.cards.d.DrainpipeVermin.class));
         cards.add(new SetCardInfo("Drana, Liberator of Malakir", 225, Rarity.MYTHIC, mage.cards.d.DranaLiberatorOfMalakir.class));
+        cards.add(new SetCardInfo("Dreamstone Hedron", 464, Rarity.UNCOMMON, mage.cards.d.DreamstoneHedron.class));
+        cards.add(new SetCardInfo("Drover of the Mighty", 388, Rarity.UNCOMMON, mage.cards.d.DroverOfTheMighty.class));
         cards.add(new SetCardInfo("Dutiful Attendant", 226, Rarity.COMMON, mage.cards.d.DutifulAttendant.class));
+        cards.add(new SetCardInfo("Dwynen's Elite", 389, Rarity.UNCOMMON, mage.cards.d.DwynensElite.class));
+        cards.add(new SetCardInfo("Elemental Uprising", 390, Rarity.COMMON, mage.cards.e.ElementalUprising.class));
+        cards.add(new SetCardInfo("Elvish Archdruid", 391, Rarity.RARE, mage.cards.e.ElvishArchdruid.class));
+        cards.add(new SetCardInfo("Enlarge", 392, Rarity.UNCOMMON, mage.cards.e.Enlarge.class));
         cards.add(new SetCardInfo("Entomber Exarch", 227, Rarity.UNCOMMON, mage.cards.e.EntomberExarch.class));
         cards.add(new SetCardInfo("Erratic Visionary", 150, Rarity.COMMON, mage.cards.e.ErraticVisionary.class));
+        cards.add(new SetCardInfo("Eternal Taskmaster", 228, Rarity.UNCOMMON, mage.cards.e.EternalTaskmaster.class));
         cards.add(new SetCardInfo("Eternal Thirst", 229, Rarity.COMMON, mage.cards.e.EternalThirst.class));
+        cards.add(new SetCardInfo("Exclude", 152, Rarity.UNCOMMON, mage.cards.e.Exclude.class));
         cards.add(new SetCardInfo("Exclusion Mage", 153, Rarity.UNCOMMON, mage.cards.e.ExclusionMage.class));
+        cards.add(new SetCardInfo("Exhume", 230, Rarity.COMMON, mage.cards.e.Exhume.class));
         cards.add(new SetCardInfo("Explore", 393, Rarity.COMMON, mage.cards.e.Explore.class));
         cards.add(new SetCardInfo("Exquisite Blood", 231, Rarity.RARE, mage.cards.e.ExquisiteBlood.class));
+        cards.add(new SetCardInfo("Fa'adiyah Seer", 394, Rarity.COMMON, mage.cards.f.FaadiyahSeer.class));
         cards.add(new SetCardInfo("Falkenrath Noble", 232, Rarity.UNCOMMON, mage.cards.f.FalkenrathNoble.class));
         cards.add(new SetCardInfo("Fanatical Firebrand", 315, Rarity.COMMON, mage.cards.f.FanaticalFirebrand.class));
+        cards.add(new SetCardInfo("Fell Specter", 233, Rarity.UNCOMMON, mage.cards.f.FellSpecter.class));
         cards.add(new SetCardInfo("Feral Hydra", 395, Rarity.UNCOMMON, mage.cards.f.FeralHydra.class));
         cards.add(new SetCardInfo("Fertilid", 398, Rarity.COMMON, mage.cards.f.Fertilid.class));
         cards.add(new SetCardInfo("Festering Newt", 234, Rarity.COMMON, mage.cards.f.FesteringNewt.class));
-        cards.add(new SetCardInfo("Flames of the Firebrand", 357, Rarity.UNCOMMON, mage.cards.f.FlamesOfTheFirebrand.class));
+        cards.add(new SetCardInfo("Flames of the Firebrand", 317, Rarity.UNCOMMON, mage.cards.f.FlamesOfTheFirebrand.class));
         cards.add(new SetCardInfo("Forest", 70, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Funeral Rites", 235, Rarity.COMMON, mage.cards.f.FuneralRites.class));
         cards.add(new SetCardInfo("Fusion Elemental", 451, Rarity.UNCOMMON, mage.cards.f.FusionElemental.class));
+        cards.add(new SetCardInfo("Gargoyle Sentinel", 465, Rarity.UNCOMMON, mage.cards.g.GargoyleSentinel.class));
+        cards.add(new SetCardInfo("Ghalta, Primal Hunger", 399, Rarity.RARE, mage.cards.g.GhaltaPrimalHunger.class));
+        cards.add(new SetCardInfo("Ghirapur Guide", 400, Rarity.UNCOMMON, mage.cards.g.GhirapurGuide.class));
+        cards.add(new SetCardInfo("Ghoulcaller Gisa", 236, Rarity.MYTHIC, mage.cards.g.GhoulcallerGisa.class));
         cards.add(new SetCardInfo("Ghoulcaller's Accomplice", 237, Rarity.COMMON, mage.cards.g.GhoulcallersAccomplice.class));
         cards.add(new SetCardInfo("Ghoulraiser", 238, Rarity.COMMON, mage.cards.g.Ghoulraiser.class));
+        cards.add(new SetCardInfo("Gingerbrute", 466, Rarity.COMMON, mage.cards.g.Gingerbrute.class));
+        cards.add(new SetCardInfo("Grave Bramble", 401, Rarity.COMMON, mage.cards.g.GraveBramble.class));
         cards.add(new SetCardInfo("Gravewaker", 241, Rarity.RARE, mage.cards.g.Gravewaker.class));
+        cards.add(new SetCardInfo("Grim Lavamancer", 331, Rarity.RARE, mage.cards.g.GrimLavamancer.class));
         cards.add(new SetCardInfo("Hedron Archive", 468, Rarity.UNCOMMON, mage.cards.h.HedronArchive.class));
         cards.add(new SetCardInfo("Hungry Flames", 336, Rarity.COMMON, mage.cards.h.HungryFlames.class));
+        cards.add(new SetCardInfo("Hunter's Insight", 402, Rarity.UNCOMMON, mage.cards.h.HuntersInsight.class));
         cards.add(new SetCardInfo("Innocent Blood", 244, Rarity.COMMON, mage.cards.i.InnocentBlood.class));
         cards.add(new SetCardInfo("Ironroot Warlord", 452, Rarity.UNCOMMON, mage.cards.i.IronrootWarlord.class));
+        cards.add(new SetCardInfo("Irresistible Prey", 406, Rarity.UNCOMMON, mage.cards.i.IrresistiblePrey.class));
         cards.add(new SetCardInfo("Isamaru, Hound of Konda", 113, Rarity.RARE, mage.cards.i.IsamaruHoundOfKonda.class));
         cards.add(new SetCardInfo("Island", 49, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Jousting Dummy", 470, Rarity.COMMON, mage.cards.j.JoustingDummy.class));
         cards.add(new SetCardInfo("Juggernaut", 471, Rarity.UNCOMMON, mage.cards.j.Juggernaut.class));
         cards.add(new SetCardInfo("Kalastria Nightwatch", 245, Rarity.COMMON, mage.cards.k.KalastriaNightwatch.class));
         cards.add(new SetCardInfo("Kels, Fight Fixer", 15, Rarity.RARE, mage.cards.k.KelsFightFixer.class));
         cards.add(new SetCardInfo("Knight of the Tusk", 114, Rarity.COMMON, mage.cards.k.KnightOfTheTusk.class));
         cards.add(new SetCardInfo("Last Gasp", 247, Rarity.COMMON, mage.cards.l.LastGasp.class));
         cards.add(new SetCardInfo("Lawmage's Binding", 453, Rarity.COMMON, mage.cards.l.LawmagesBinding.class));
+        cards.add(new SetCardInfo("Leaf Gilder", 408, Rarity.COMMON, mage.cards.l.LeafGilder.class));
+        cards.add(new SetCardInfo("Leave in the Dust", 156, Rarity.COMMON, mage.cards.l.LeaveInTheDust.class));
         cards.add(new SetCardInfo("Liliana's Elite", 250, Rarity.UNCOMMON, mage.cards.l.LilianasElite.class));
+        cards.add(new SetCardInfo("Lurking Predators", 410, Rarity.RARE, mage.cards.l.LurkingPredators.class));
+        cards.add(new SetCardInfo("Macabre Waltz", 252, Rarity.COMMON, mage.cards.m.MacabreWaltz.class));
         cards.add(new SetCardInfo("Maelstrom Archangel", 454, Rarity.MYTHIC, mage.cards.m.MaelstromArchangel.class));
+        cards.add(new SetCardInfo("Magma Jet", 346, Rarity.UNCOMMON, mage.cards.m.MagmaJet.class));
+        cards.add(new SetCardInfo("Magmaquake", 347, Rarity.RARE, mage.cards.m.Magmaquake.class));
+        cards.add(new SetCardInfo("Malakir Familiar", 253, Rarity.UNCOMMON, mage.cards.m.MalakirFamiliar.class));
+        cards.add(new SetCardInfo("Marauder's Axe", 473, Rarity.COMMON, mage.cards.m.MaraudersAxe.class));
         cards.add(new SetCardInfo("Mark of the Vampire", 254, Rarity.COMMON, mage.cards.m.MarkOfTheVampire.class));
         cards.add(new SetCardInfo("Meteor Golem", 474, Rarity.UNCOMMON, mage.cards.m.MeteorGolem.class));
+        cards.add(new SetCardInfo("Miasmic Mummy", 256, Rarity.COMMON, mage.cards.m.MiasmicMummy.class));
         cards.add(new SetCardInfo("Mire Triton", 257, Rarity.UNCOMMON, mage.cards.m.MireTriton.class));
         cards.add(new SetCardInfo("Mirrodin's Core", 492, Rarity.UNCOMMON, mage.cards.m.MirrodinsCore.class));
+        cards.add(new SetCardInfo("Molten Ravager", 351, Rarity.COMMON, mage.cards.m.MoltenRavager.class));
+        cards.add(new SetCardInfo("Momentous Fall", 411, Rarity.RARE, mage.cards.m.MomentousFall.class));
+        cards.add(new SetCardInfo("Mountain", 64, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Narcolepsy", 159, Rarity.COMMON, mage.cards.n.Narcolepsy.class));
         cards.add(new SetCardInfo("Nature's Way", 412, Rarity.UNCOMMON, mage.cards.n.NaturesWay.class));
+        cards.add(new SetCardInfo("New Horizons", 414, Rarity.COMMON, mage.cards.n.NewHorizons.class));
         cards.add(new SetCardInfo("Nocturnal Feeder", 16, Rarity.COMMON, mage.cards.n.NocturnalFeeder.class));
         cards.add(new SetCardInfo("Nyxathid", 259, Rarity.RARE, mage.cards.n.Nyxathid.class));
+        cards.add(new SetCardInfo("Oneirophage", 162, Rarity.UNCOMMON, mage.cards.o.Oneirophage.class));
         cards.add(new SetCardInfo("Oracle of Mul Daya", 415, Rarity.RARE, mage.cards.o.OracleOfMulDaya.class));
+        cards.add(new SetCardInfo("Orazca Frillback", 416, Rarity.COMMON, mage.cards.o.OrazcaFrillback.class));
+        cards.add(new SetCardInfo("Overgrown Battlement", 417, Rarity.UNCOMMON, mage.cards.o.OvergrownBattlement.class));
+        cards.add(new SetCardInfo("Pacifism", 125, Rarity.COMMON, mage.cards.p.Pacifism.class));
         cards.add(new SetCardInfo("Peel from Reality", 163, Rarity.COMMON, mage.cards.p.PeelFromReality.class));
-        cards.add(new SetCardInfo("Pillar of Flame", 355, Rarity.RARE, mage.cards.p.PillarOfFlame.class));
-        cards.add(new SetCardInfo("Plains", 45, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Penumbra Bobcat", 418, Rarity.COMMON, mage.cards.p.PenumbraBobcat.class));
+        cards.add(new SetCardInfo("Phyrexian Rager", 266, Rarity.COMMON, mage.cards.p.PhyrexianRager.class));
+        cards.add(new SetCardInfo("Phyrexian Tower", 493, Rarity.RARE, mage.cards.p.PhyrexianTower.class));
+        cards.add(new SetCardInfo("Pillar of Flame", 355, Rarity.COMMON, mage.cards.p.PillarOfFlame.class));
+        cards.add(new SetCardInfo("Plains", 41, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Prescient Chimera", 164, Rarity.COMMON, mage.cards.p.PrescientChimera.class));
+        cards.add(new SetCardInfo("Presence of Gond", 420, Rarity.COMMON, mage.cards.p.PresenceOfGond.class));
         cards.add(new SetCardInfo("Primordial Sage", 422, Rarity.RARE, mage.cards.p.PrimordialSage.class));
         cards.add(new SetCardInfo("Prophetic Prism", 478, Rarity.COMMON, mage.cards.p.PropheticPrism.class));
         cards.add(new SetCardInfo("Pyroclastic Elemental", 356, Rarity.UNCOMMON, mage.cards.p.PyroclasticElemental.class));
         cards.add(new SetCardInfo("Raging Regisaur", 455, Rarity.UNCOMMON, mage.cards.r.RagingRegisaur.class));
+        cards.add(new SetCardInfo("Rampaging Brontodon", 423, Rarity.RARE, mage.cards.r.RampagingBrontodon.class));
+        cards.add(new SetCardInfo("Ravenous Baloth", 424, Rarity.RARE, mage.cards.r.RavenousBaloth.class));
+        cards.add(new SetCardInfo("Ravenous Chupacabra", 269, Rarity.UNCOMMON, mage.cards.r.RavenousChupacabra.class));
         cards.add(new SetCardInfo("Reanimate", 270, Rarity.RARE, mage.cards.r.Reanimate.class));
         cards.add(new SetCardInfo("Release the Dogs", 4, Rarity.UNCOMMON, mage.cards.r.ReleaseTheDogs.class));
+        cards.add(new SetCardInfo("Rhystic Study", 169, Rarity.RARE, mage.cards.r.RhysticStudy.class));
         cards.add(new SetCardInfo("Riptide Laboratory", 494, Rarity.RARE, mage.cards.r.RiptideLaboratory.class));
+        cards.add(new SetCardInfo("Rise of the Dark Realms", 271, Rarity.MYTHIC, mage.cards.r.RiseOfTheDarkRealms.class));
+        cards.add(new SetCardInfo("Roving Keep", 480, Rarity.COMMON, mage.cards.r.RovingKeep.class));
+        cards.add(new SetCardInfo("Rumbling Baloth", 426, Rarity.COMMON, mage.cards.r.RumblingBaloth.class));
+        cards.add(new SetCardInfo("Runed Servitor", 481, Rarity.COMMON, mage.cards.r.RunedServitor.class));
         cards.add(new SetCardInfo("Rupture Spire", 495, Rarity.COMMON, mage.cards.r.RuptureSpire.class));
         cards.add(new SetCardInfo("Sage's Row Savant", 171, Rarity.COMMON, mage.cards.s.SagesRowSavant.class));
         cards.add(new SetCardInfo("Sangromancer", 272, Rarity.RARE, mage.cards.s.Sangromancer.class));
+        cards.add(new SetCardInfo("Savage Stomp", 427, Rarity.UNCOMMON, mage.cards.s.SavageStomp.class));
+        cards.add(new SetCardInfo("Scarecrone", 482, Rarity.RARE, mage.cards.s.Scarecrone.class));
+        cards.add(new SetCardInfo("Scourge of Nel Toth", 274, Rarity.RARE, mage.cards.s.ScourgeOfNelToth.class));
+        cards.add(new SetCardInfo("Scuttlemutt", 484, Rarity.UNCOMMON, mage.cards.s.Scuttlemutt.class));
         cards.add(new SetCardInfo("Sea Gate Oracle", 173, Rarity.COMMON, mage.cards.s.SeaGateOracle.class));
+        cards.add(new SetCardInfo("Seismic Elemental", 362, Rarity.UNCOMMON, mage.cards.s.SeismicElemental.class));
+        cards.add(new SetCardInfo("Selvala, Heart of the Wilds", 429, Rarity.MYTHIC, mage.cards.s.SelvalaHeartOfTheWilds.class));
         cards.add(new SetCardInfo("Sengir Vampire", 275, Rarity.UNCOMMON, mage.cards.s.SengirVampire.class));
         cards.add(new SetCardInfo("Settle the Score", 276, Rarity.UNCOMMON, mage.cards.s.SettleTheScore.class));
         cards.add(new SetCardInfo("Shambling Goblin", 277, Rarity.COMMON, mage.cards.s.ShamblingGoblin.class));
+        cards.add(new SetCardInfo("Sharding Sphinx", 176, Rarity.RARE, mage.cards.s.ShardingSphinx.class));
+        cards.add(new SetCardInfo("Sheoldred, Whispering One", 278, Rarity.MYTHIC, mage.cards.s.SheoldredWhisperingOne.class));
+        cards.add(new SetCardInfo("Signpost Scarecrow", 485, Rarity.COMMON, mage.cards.s.SignpostScarecrow.class));
+        cards.add(new SetCardInfo("Silhana Wayfinder", 430, Rarity.UNCOMMON, mage.cards.s.SilhanaWayfinder.class));
         cards.add(new SetCardInfo("Skittering Surveyor", 486, Rarity.COMMON, mage.cards.s.SkitteringSurveyor.class));
+        cards.add(new SetCardInfo("Somberwald Stag", 431, Rarity.UNCOMMON, mage.cards.s.SomberwaldStag.class));
         cards.add(new SetCardInfo("Soul of the Harvest", 432, Rarity.RARE, mage.cards.s.SoulOfTheHarvest.class));
+        cards.add(new SetCardInfo("Spitting Earth", 364, Rarity.COMMON, mage.cards.s.SpittingEarth.class));
         cards.add(new SetCardInfo("Sporemound", 433, Rarity.COMMON, mage.cards.s.Sporemound.class));
         cards.add(new SetCardInfo("Storm Sculptor", 179, Rarity.COMMON, mage.cards.s.StormSculptor.class));
+        cards.add(new SetCardInfo("Suspicious Bookcase", 487, Rarity.UNCOMMON, mage.cards.s.SuspiciousBookcase.class));
         cards.add(new SetCardInfo("Swamp", 54, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Swarm of Bloodflies", 282, Rarity.UNCOMMON, mage.cards.s.SwarmOfBloodflies.class));
+        cards.add(new SetCardInfo("Sylvan Brushstrider", 434, Rarity.COMMON, mage.cards.s.SylvanBrushstrider.class));
         cards.add(new SetCardInfo("Sylvan Ranger", 435, Rarity.COMMON, mage.cards.s.SylvanRanger.class));
         cards.add(new SetCardInfo("Talrand's Invocation", 182, Rarity.UNCOMMON, mage.cards.t.TalrandsInvocation.class));
         cards.add(new SetCardInfo("Talrand, Sky Summoner", 181, Rarity.RARE, mage.cards.t.TalrandSkySummoner.class));
         cards.add(new SetCardInfo("Tempting Witch", 283, Rarity.UNCOMMON, mage.cards.t.TemptingWitch.class));
         cards.add(new SetCardInfo("Terramorphic Expanse", 78, Rarity.COMMON, mage.cards.t.TerramorphicExpanse.class));
+        cards.add(new SetCardInfo("Terrarion", 488, Rarity.COMMON, mage.cards.t.Terrarion.class));
         cards.add(new SetCardInfo("Thirst for Knowledge", 183, Rarity.UNCOMMON, mage.cards.t.ThirstForKnowledge.class));
+        cards.add(new SetCardInfo("Thragtusk", 436, Rarity.RARE, mage.cards.t.Thragtusk.class));
         cards.add(new SetCardInfo("Thriving Bluff", 33, Rarity.COMMON, mage.cards.t.ThrivingBluff.class));
         cards.add(new SetCardInfo("Thriving Grove", 34, Rarity.COMMON, mage.cards.t.ThrivingGrove.class));
         cards.add(new SetCardInfo("Thriving Heath", 35, Rarity.COMMON, mage.cards.t.ThrivingHeath.class));
         cards.add(new SetCardInfo("Thriving Isle", 36, Rarity.COMMON, mage.cards.t.ThrivingIsle.class));
         cards.add(new SetCardInfo("Thriving Moor", 37, Rarity.COMMON, mage.cards.t.ThrivingMoor.class));
+        cards.add(new SetCardInfo("Thundering Spineback", 437, Rarity.UNCOMMON, mage.cards.t.ThunderingSpineback.class));
+        cards.add(new SetCardInfo("Tithebearer Giant", 284, Rarity.COMMON, mage.cards.t.TithebearerGiant.class));
         cards.add(new SetCardInfo("Ulvenwald Hydra", 439, Rarity.MYTHIC, mage.cards.u.UlvenwaldHydra.class));
         cards.add(new SetCardInfo("Vampire Neonate", 285, Rarity.COMMON, mage.cards.v.VampireNeonate.class));
         cards.add(new SetCardInfo("Vastwood Zendikon", 440, Rarity.COMMON, mage.cards.v.VastwoodZendikon.class));
+        cards.add(new SetCardInfo("Vedalken Archmage", 187, Rarity.RARE, mage.cards.v.VedalkenArchmage.class));
         cards.add(new SetCardInfo("Verdant Embrace", 441, Rarity.RARE, mage.cards.v.VerdantEmbrace.class));
+        cards.add(new SetCardInfo("Volcanic Fallout", 368, Rarity.UNCOMMON, mage.cards.v.VolcanicFallout.class));
         cards.add(new SetCardInfo("Wailing Ghoul", 286, Rarity.COMMON, mage.cards.w.WailingGhoul.class));
         cards.add(new SetCardInfo("Wall of Blossoms", 442, Rarity.UNCOMMON, mage.cards.w.WallOfBlossoms.class));
+        cards.add(new SetCardInfo("Wall of Vines", 443, Rarity.COMMON, mage.cards.w.WallOfVines.class));
+        cards.add(new SetCardInfo("Warmonger's Chariot", 490, Rarity.UNCOMMON, mage.cards.w.WarmongersChariot.class));
+        cards.add(new SetCardInfo("Wight of Precinct Six", 287, Rarity.COMMON, mage.cards.w.WightOfPrecinctSix.class));
+        cards.add(new SetCardInfo("Wildheart Invoker", 444, Rarity.COMMON, mage.cards.w.WildheartInvoker.class));
         cards.add(new SetCardInfo("Winged Words", 196, Rarity.COMMON, mage.cards.w.WingedWords.class));
         cards.add(new SetCardInfo("Wizard's Retort", 198, Rarity.UNCOMMON, mage.cards.w.WizardsRetort.class));
         cards.add(new SetCardInfo("Woodborn Behemoth", 446, Rarity.UNCOMMON, mage.cards.w.WoodbornBehemoth.class));
+        cards.add(new SetCardInfo("Wren's Run Vanquisher", 447, Rarity.UNCOMMON, mage.cards.w.WrensRunVanquisher.class));
         cards.add(new SetCardInfo("Young Pyromancer", 372, Rarity.UNCOMMON, mage.cards.y.YoungPyromancer.class));
         cards.add(new SetCardInfo("Zendikar's Roil", 448, Rarity.UNCOMMON, mage.cards.z.ZendikarsRoil.class));
+        cards.add(new SetCardInfo("Zombie Infestation", 288, Rarity.UNCOMMON, mage.cards.z.ZombieInfestation.class));
     }
 }
diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt
index 211c0cd359..8c51d89f3b 100644
--- a/Utils/mtg-cards-data.txt
+++ b/Utils/mtg-cards-data.txt
@@ -37723,10 +37723,14 @@ Garruk, Savage Herald|Core Set 2021|336|M|{4}{G}{G}|Legendary Planeswalker - Gar
 Garruk's Warsteed|Core Set 2021|337|R|{3}{G}{G}|Creature - Rhino|3|5|Vigilance$When Garruk's Warsteed enters the battlefield, you may search your library and/or graveyard for a card named Garruk, Savage Herald, reveal it, and put it into your hand. If you search your library this way, shuffle it.|
 Predatory Wurm|Core Set 2021|338|U|{3}{G}|Creature - Wurm|4|4|Vigilance$Predatory Wurm gets +2/+2 as long as you control a Garruk planeswalker.|
 Wildwood Patrol|Core Set 2021|339|C|{2}{G}|Creature - Centaur Scout|4|2|Trample|
+Blessed Sanctuary|Jumpstart|1|R|{3}{W}{W}|Enchantment|||Prevent all noncombat damage that would be dealt to you and creatures you control.$Whenever a nontoken creature enters the battlefield under your control, create a 2/2 white Unicorn creature token.|
+Brightmare|Jumpstart|2|C|{2}{W}|Creature - Unicorn|2|3|When Brightmare enters the battlefield, tap up to one target creature. You gain life equal to that creature's power.|
+Emiel the Blessed|Jumpstart|3|M|{2}{W}{W}|Legendary Creature - Unicorn|4|4|{3}: Exile another target creature you control, then return it to the battlefield under its owner's control.$Whenever another creature enters the battlefield under your control, you may pay {G/W}. If you do, put a +1/+1 counter on it. If it's a Unicorn, put two +1/+1 counters on it instead.|
 Release the Dogs|Jumpstart|4|U|{3}{W}|Sorcery|||Create four 1/1 white Dog creature tokens.|
 Supply Runners|Jumpstart|7|U|{4}{W}|Creature - Dog|2|2|When Supply Runners enters the battlefield, put a +1/+1 counter on each other creature you control.|
 Trusty Retriever|Jumpstart|8|C|{3}{W}|Creature - Dog|2|3|When Trusty Retriever enters the battlefield, choose one —$• Put a +1/+1 counter on Trusty Retriever.$• Return target artifact or enchantment card from your graveyard to your hand.|
 Archaeomender|Jumpstart|9|C|{2}{U}|Creature - Human Wizard|2|3|When Archaeomender enters the battlefield, return target artifact card from your graveyard to your hand.|
+Ormos, Archive Keeper|Jumpstart|13|R|{4}{U}{U}|Legendary Creature - Sphinx|5|5|Flying$If you would draw a card while your library has no cards in it, instead put five +1/+1 counters on Ormos, Archive Keeper.${1}{U}{U}, Discard three cards with different names: Draw five cards.|
 Scholar of the Lost Trove|Jumpstart|14|R|{5}{U}{U}|Creature - Sphinx|5|5|Flying$When Scholar of the Lost Trove enters the battlefield, you may cast target instant, sorcery, or artifact card from your graveyard without paying its mana cost. If an instant or sorcery spell cast this way would be put into your graveyard this turn, exile it instead.|
 Kels, Fight Fixer|Jumpstart|15|R|{2}{B}{B}|Legendary Creature - Azra Warlock|4|3|Menace$Whenever you sacrifice a creature, you may pay {U/B}. If you do, draw a card.${1}, Sacrifice a creature: Kels, Fight Fixer gains indestructible until end of turn.|
 Nocturnal Feeder|Jumpstart|16|C|{2}{B}|Creature - Vampire Rogue|2|1|Flying$When Nocturnal Feeder dies, each opponent loses 2 life and you gain 2 life.|
@@ -37735,34 +37739,51 @@ Witch of the Moors|Jumpstart|18|R|{3}{B}{B}|Creature - Human Warlock|4|4|Deathto
 Lightning Phoenix|Jumpstart|21|R|{2}{R}|Creature - Phoenix|2|2|Flying, haste$Lightning Phoenix can't block.$At the beginning of your end step, if an opponent was dealt 3 or more damage this turn, you may pay {R}. If you do, return Lightning Phoenix from your graveyard to the battlefield.|
 Lightning Visionary|Jumpstart|22|C|{1}{R}|Creature - Minotaur Shaman|2|1|Prowess|
 Living Lightning|Jumpstart|23|U|{3}{R}|Creature - Elemental Shaman|3|2|When Living Lightning dies, return target instant or sorcery card from your graveyard to your hand.|
+Allosaurus Shepherd|Jumpstart|28|M|{G}|Creature - Elf Shaman|1|1|Allosaurus Shepherd can't be countered.$Green spells you control can't be countered.${4}{G}{G}: Until end of turn, each Elf creature you control has base power and toughness 5/5 and becomes a Dinosaur in addition to its other creature types.|
 Neyith of the Dire Hunt|Jumpstart|30|R|{2}{G}{G}|Legendary Creature - Human Warrior|3|3|Whenever one or more creatures you control fight or become blocked, draw a card.$At the beginning of combat on your turn, you may pay {2}{R/G}. If you do, double target creature's power until end of turn. That creature must be blocked this combat if able.|
+Towering Titan|Jumpstart|31|M|{4}{G}{G}|Creature - Giant|0|0|Towering Titan enters the battlefield with X +1/+1 counters on it, where X is the total toughness of other creatures you control.$Sacrifice a creature with defender: All creatures gain trample until end of turn.|
 Lightning-Core Excavator|Jumpstart|32|C|{1}|Artifact Creature - Golem|0|3|{5}, {T}, Sacrifice Lightning-Core Excavator: It deals 3 damage to any target.|
 Thriving Bluff|Jumpstart|33|C||Land|||Thriving Bluff enters the battlefield tapped.$As Thriving Bluff enters the battlefield, choose a color other than red.${T}: Add {R} or one mana of the chosen color.|
 Thriving Grove|Jumpstart|34|C||Land|||Thriving Grove enters the battlefield tapped.$As Thriving Grove enters the battlefield, choose a color other than green.${T}: Add {G} or one mana of the chosen color.|
 Thriving Heath|Jumpstart|35|C||Land|||Thriving Heath enters the battlefield tapped.$As Thriving Heath enters the battlefield, choose a color other than white.${T}: Add {W} or one mana of the chosen color.|
 Thriving Isle|Jumpstart|36|C||Land|||Thriving Isle enters the battlefield tapped.$As Thriving Isle enters the battlefield, choose a color other than blue.${T}: Add {U} or one mana of the chosen color.|
 Thriving Moor|Jumpstart|37|C||Land|||Thriving Moor enters the battlefield tapped.$As Thriving Moor enters the battlefield, choose a color other than black.${T}: Add {B} or one mana of the chosen color.|
-Plains|Jumpstart|45|C||Basic Land - Plains|||({T}: Add {W}.)|
+Plains|Jumpstart|41|C||Basic Land - Plains|||({T}: Add {W}.)|
 Island|Jumpstart|49|C||Basic Land - Island|||({T}: Add {U}.)|
 Swamp|Jumpstart|54|C||Basic Land - Swamp|||({T}: Add {B}.)|
+Mountain|Jumpstart|64|C||Basic Land - Mountain|||({T}: Add {R}.)|
 Forest|Jumpstart|70|C||Basic Land - Forest|||({T}: Add {G}.)|
 Terramorphic Expanse|Jumpstart|78|C||Land|||{T}, Sacrifice Terramorphic Expanse: Search your library for a basic land card, put it onto the battlefield tapped, then shuffle your library.|
+Affa Guard Hound|Jumpstart|81|U|{2}{W}|Creature - Hound|2|2|Flash$When Affa Guard Hound enters the battlefield, target creature gets +0/+3 until end of turn.|
+Cathar's Companion|Jumpstart|94|C|{2}{W}|Creature - Hound|3|1|Whenever you cast a noncreature spell, Cathar's Companion gains indestructible until end of turn.|
 Isamaru, Hound of Konda|Jumpstart|113|R|{W}|Legendary Creature - Hound|2|2||
 Knight of the Tusk|Jumpstart|114|C|{4}{W}{W}|Creature - Human Knight|3|7|Vigilance|
+Pacifism|Jumpstart|125|C|{1}{W}|Enchantment - Aura|||Enchant creature$Enchanted creature can't attack or block.|
 Befuddle|Jumpstart|140|C|{2}{U}|Instant|||Target creature gets -4/-0 until end of turn.$Draw a card.|
+Cloudreader Sphinx|Jumpstart|143|C|{4}{U}|Creature - Sphinx|3|4|Flying$When Cloudreader Sphinx enters the battlefield, scry 2.|
 Crookclaw Transmuter|Jumpstart|145|C|{3}{U}|Creature - Bird Wizard|3|1|Flash$Flying$When Crookclaw Transmuter enters the battlefield, switch target creature's power and toughness until end of turn.|
+Curiosity|Jumpstart|147|U|{U}|Enchantment - Aura|||Enchant creature$Whenever enchanted creature deals damage to an opponent, you may draw a card.|
 Erratic Visionary|Jumpstart|150|C|{1}{U}|Creature - Human Wizard|1|3|{1}{U}, {T}: Draw a card, then discard a card.|
+Exclude|Jumpstart|152|U|{2}{U}|Instant|||Counter target creature spell.$Draw a card.|
 Exclusion Mage|Jumpstart|153|U|{2}{U}|Creature - Human Wizard|2|2|When Exclusion Mage enters the battlefield, return target creature an opponent controls to its owner's hand.|
+Leave in the Dust|Jumpstart|156|C|{3}{U}|Instant|||Return target nonland permanent to its owner's hand.$Draw a card.|
+Narcolepsy|Jumpstart|159|C|{1}{U}|Enchantment - Aura|||Enchant creature$At the beginning of each upkeep, if enchanted creature is untapped, tap it.|
+Oneirophage|Jumpstart|162|U|{3}{U}|Creature - Squid Illusion|1|2|Flying$Whenever you draw a card, put a +1/+1 counter on Oneirophage.|
 Peel from Reality|Jumpstart|163|C|{1}{U}|Instant|||Return target creature you control and target creature you don't control to their owners' hands.|
+Prescient Chimera|Jumpstart|164|C|{3}{U}{U}|Creature - Chimera|3|4|Flying$Whenever you cast an instant or sorcery spell, scry 1.|
+Rhystic Study|Jumpstart|169|R|{2}{U}|Enchantment|||Whenever an opponent casts a spell, you may draw a card unless that player pays {1}.|
 Sage's Row Savant|Jumpstart|171|C|{1}{U}|Creature - Vedalken Wizard|2|1|When Sage's Row Savant enters the battlefield, scry 2.|
 Sea Gate Oracle|Jumpstart|173|C|{2}{U}|Creature - Human Wizard|1|3|When Sea Gate Oracle enters the battlefield, look at the top two cards of your library. Put one of them into your hand and the other on the bottom of your library.|
+Sharding Sphinx|Jumpstart|176|R|{4}{U}{U}|Artifact Creature - Sphinx|4|4|Flying$Whenever an artifact creature you control deals combat damage to a player, you may create a 1/1 blue Thopter artifact creature token with flying.|
 Storm Sculptor|Jumpstart|179|C|{3}{U}|Creature - Merfolk Wizard|3|2|Storm Sculptor can't be blocked.$When Storm Sculptor enters the battlefield, return a creature you control to its owner's hand.|
 Talrand, Sky Summoner|Jumpstart|181|R|{2}{U}{U}|Legendary Creature - Merfolk Wizard|2|2|Whenever you cast an instant or sorcery spell, create a 2/2 blue Drake creature token with flying.|
 Talrand's Invocation|Jumpstart|182|U|{2}{U}{U}|Sorcery|||Create two 2/2 blue Drake creature tokens with flying.|
 Thirst for Knowledge|Jumpstart|183|U|{2}{U}|Instant|||Draw three cards. Then discard two cards unless you discard an artifact card.|
+Vedalken Archmage|Jumpstart|187|R|{2}{U}{U}|Creature - Vedalken Wizard|0|2|Whenever you cast an artifact spell, draw a card.|
 Winged Words|Jumpstart|196|C|{2}{U}|Sorcery|||This spell costs {1} less to cast if you control a creature with flying.$Draw two cards.|
 Wizard's Retort|Jumpstart|198|U|{1}{U}{U}|Instant|||This spell costs {1} less to cast if you control a Wizard.$Counter target spell.|
 Agonizing Syphon|Jumpstart|199|C|{3}{B}|Sorcery|||Agonizing Syphon deals 3 damage to any target and you gain 3 life.|
+Assassin's Strike|Jumpstart|200|U|{4}{B}{B}|Sorcery|||Destroy target creature. Its controller discards a card.|
 Bake into a Pie|Jumpstart|201|C|{2}{B}{B}|Instant|||Destroy target creature. Create a Food token.|
 Black Cat|Jumpstart|203|C|{1}{B}|Creature - Zombie Cat|1|1|When Black Cat dies, target opponent discards a card at random.|
 Blighted Bat|Jumpstart|205|C|{2}{B}|Creature - Zombie Bat|2|1|Flying${1}: Blighted Bat gains haste until end of turn.|
@@ -37773,15 +37794,27 @@ Bloodbond Vampire|Jumpstart|209|U|{2}{B}{B}|Creature - Vampire Shaman Ally|3|3|W
 Bloodhunter Bat|Jumpstart|210|C|{3}{B}|Creature - Bat|2|2|Flying$When Bloodhunter Bat enters the battlefield, target player loses 2 life and you gain 2 life.|
 Bogbrew Witch|Jumpstart|211|R|{3}{B}|Creature - Human Wizard|1|3|{2}, {T}: Search your library for a card named Festering Newt or Bubbling Cauldron, put it onto the battlefield tapped, then shuffle your library.|
 Bone Picker|Jumpstart|212|U|{3}{B}|Creature - Bird|3|2|This spell costs {3} less to cast if a creature died this turn.$Flying, deathtouch|
+Bone Splinters|Jumpstart|213|C|{B}|Sorcery|||As an additional cost to cast this spell, sacrifice a creature.$Destroy target creature.|
+Burglar Rat|Jumpstart|214|C|{1}{B}|Creature - Rat|1|1|When Burglar Rat enters the battlefield, each opponent discards a card.|
+Cadaver Imp|Jumpstart|215|C|{1}{B}{B}|Creature - Imp|1|1|Flying$When Cadaver Imp enters the battlefield, you may return target creature card from your graveyard to your hand.|
+Cauldron Familiar|Jumpstart|216|C|{B}|Creature - Cat|1|1|When Cauldron Familiar enters the battlefield, each opponent loses 1 life and you gain 1 life.$Sacrifice a Food: Return Cauldron Familiar from your graveyard to the battlefield.|
 Cemetery Recruitment|Jumpstart|217|C|{1}{B}|Sorcery|||Return target creature card from your graveyard to your hand. If it's a Zombie card, draw a card.|
 Child of Night|Jumpstart|218|C|{1}{B}|Creature - Vampire|2|1|Lifelink|
+Crow of Dark Tidings|Jumpstart|221|C|{2}{B}|Creature - Zombie Bird|2|1|Flying$When Crow of Dark Tidings enters the battlefield or dies, put the top two cards of your library into your graveyard.|
+Death's Approach|Jumpstart|222|C|{B}|Enchantment - Aura|||Enchant creature$Enchanted creature gets -X/-X, where X is the number of creature cards in its controller's graveyard.|
+Drainpipe Vermin|Jumpstart|224|C|{B}|Creature - Rat|1|1|When Drainpipe Vermin dies, you may pay {B}. If you do, target player discards a card.|
 Drana, Liberator of Malakir|Jumpstart|225|M|{1}{B}{B}|Legendary Creature - Vampire Ally|2|3|Flying, first strike$Whenever Drana, Liberator of Malakir deals combat damage to a player, put a +1/+1 counter on each attacking creature you control.|
 Dutiful Attendant|Jumpstart|226|C|{2}{B}|Creature - Human Warrior|1|2|When Dutiful Attendant dies, return another target creature card from your graveyard to your hand.|
 Entomber Exarch|Jumpstart|227|U|{2}{B}{B}|Creature - Cleric|2|2|When Entomber Exarch enters the battlefield, choose one —$• Return target creature card from your graveyard to your hand.$• Target opponent reveals their hand. You choose a noncreature card from it. That player discards that card.|
+Eternal Taskmaster|Jumpstart|228|U|{1}{B}|Creature - Zombie|2|3|Eternal Taskmaster enters the battlefield tapped.$Whenever Eternal Taskmaster attacks, you may pay {2}{B}. If you do, return target creature card from your graveyard to your hand.|
 Eternal Thirst|Jumpstart|229|C|{1}{B}|Enchantment - Aura|||Enchant creature$Enchanted creature has lifelink and "Whenever a creature an opponent controls dies, put a +1/+1 counter on this creature."|
+Exhume|Jumpstart|230|C|{1}{B}|Sorcery|||Each player puts a creature card from their graveyard onto the battlefield.|
 Exquisite Blood|Jumpstart|231|R|{4}{B}|Enchantment|||Whenever an opponent loses life, you gain that much life.|
 Falkenrath Noble|Jumpstart|232|U|{3}{B}|Creature - Vampire Noble|2|2|Flying$Whenever Falkenrath Noble or another creature dies, target player loses 1 life and you gain 1 life.|
+Fell Specter|Jumpstart|233|U|{3}{B}|Creature - Specter|1|3|Flying$When Fell Specter enters the battlefield, target opponent discards a card.$Whenever an opponent discards a card, that player loses 2 life.|
 Festering Newt|Jumpstart|234|C|{B}|Creature - Salamander|1|1|When Festering Newt dies, target creature an opponent controls gets -1/-1 until end of turn. That creature gets -4/-4 instead if you control a creature named Bogbrew Witch.|
+Funeral Rites|Jumpstart|235|C|{2}{B}|Sorcery|||You draw two cards, lose 2 life, and put the top two cards of your library into your graveyard.|
+Ghoulcaller Gisa|Jumpstart|236|M|{3}{B}{B}|Legendary Creature - Human Wizard|3|4|{B}, {T}, Sacrifice another creature: Create X 2/2 black Zombie creature tokens, where X is the sacrificed creature's power.|
 Ghoulcaller's Accomplice|Jumpstart|237|C|{1}{B}|Creature - Human Rogue|2|2|{3}{B}, Exile Ghoulcaller's Accomplice from your graveyard: Create a 2/2 black Zombie creature token. Activate this ability only any time you could cast a sorcery.|
 Ghoulraiser|Jumpstart|238|C|{1}{B}{B}|Creature - Zombie|2|2|When Ghoulraiser enters the battlefield, return a Zombie card at random from your graveyard to your hand.|
 Gravewaker|Jumpstart|241|R|{4}{B}{B}|Creature - Bird Spirit|5|5|Flying${5}{B}{B}: Return target creature card from your graveyard to the battlefield tapped.|
@@ -37789,44 +37822,103 @@ Innocent Blood|Jumpstart|244|C|{B}|Sorcery|||Each player sacrifices a creature.|
 Kalastria Nightwatch|Jumpstart|245|C|{4}{B}|Creature - Vampire Warrior Ally|4|5|Whenever you gain life, Kalastria Nightwatch gains flying until end of turn.|
 Last Gasp|Jumpstart|247|C|{1}{B}|Instant|||Target creature gets -3/-3 until end of turn.|
 Liliana's Elite|Jumpstart|250|U|{2}{B}|Creature - Zombie|1|1|Liliana's Elite gets +1/+1 for each creature card in your graveyard.|
+Macabre Waltz|Jumpstart|252|C|{1}{B}|Sorcery|||Return up to two target creature cards from your graveyard to your hand, then discard a card.|
+Malakir Familiar|Jumpstart|253|U|{2}{B}|Creature - Bat|2|1|Flying, deathtouch$Whenever you gain life, Malakir Familiar gets +1/+1 until end of turn.|
 Mark of the Vampire|Jumpstart|254|C|{3}{B}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +2/+2 and has lifelink.|
+Miasmic Mummy|Jumpstart|256|C|{1}{B}|Creature - Zombie Jackal|2|2|When Miasmic Mummy enters the battlefield, each player discards a card.|
 Mire Triton|Jumpstart|257|U|{1}{B}|Creature - Zombie Merfolk|2|1|Deathtouch$When Mire Triton enters the battlefield, put the top two cards of your library into your graveyard and you gain 2 life.|
 Nyxathid|Jumpstart|259|R|{1}{B}{B}|Creature - Elemental|7|7|As Nyxathid enters the battlefield, choose an opponent.$Nyxathid gets -1/-1 for each card in the chosen player's hand.|
+Phyrexian Rager|Jumpstart|266|C|{2}{B}|Creature - Horror|2|2|When Phyrexian Rager enters the battlefield, you draw a card and you lose 1 life.|
+Ravenous Chupacabra|Jumpstart|269|U|{2}{B}{B}|Creature - Beast Horror|2|2|When Ravenous Chupacabra enters the battlefield, destroy target creature an opponent controls.|
 Reanimate|Jumpstart|270|R|{B}|Sorcery|||Put target creature card from a graveyard onto the battlefield under your control. You lose life equal to its converted mana cost.|
+Rise of the Dark Realms|Jumpstart|271|M|{7}{B}{B}|Sorcery|||Put all creature cards from all graveyards onto the battlefield under your control.|
 Sangromancer|Jumpstart|272|R|{2}{B}{B}|Creature - Vampire Shaman|3|3|Flying$Whenever a creature an opponent controls dies, you may gain 3 life.$Whenever an opponent discards a card, you may gain 3 life.|
+Scourge of Nel Toth|Jumpstart|274|R|{5}{B}{B}|Creature - Zombie Dragon|6|6|Flying$You may cast Scourge of Nel Toth from your graveyard by paying {B}{B} and sacrificing two creatures rather than paying its mana cost.|
 Sengir Vampire|Jumpstart|275|U|{3}{B}{B}|Creature - Vampire|4|4|Flying$Whenever a creature dealt damage by Sengir Vampire this turn dies, put a +1/+1 counter on Sengir Vampire.|
 Settle the Score|Jumpstart|276|U|{2}{B}{B}|Sorcery|||Exile target creature. Put two loyalty counters on a planeswalker you control.|
 Shambling Goblin|Jumpstart|277|C|{B}|Creature - Zombie Goblin|1|1|When Shambling Goblin dies, target creature an opponent controls gets -1/-1 until end of turn.|
+Sheoldred, Whispering One|Jumpstart|278|M|{5}{B}{B}|Legendary Creature - Praetor|6|6|Swampwalk$At the beginning of your upkeep, return target creature card from your graveyard to the battlefield.$At the beginning of each opponent's upkeep, that player sacrifices a creature.|
 Swarm of Bloodflies|Jumpstart|282|U|{4}{B}|Creature - Insect|0|0|Flying$Swarm of Bloodflies enters the battlefield with two +1/+1 counters on it.$Whenever another creature dies, put a +1/+1 counter on Swarm of Bloodflies.|
 Tempting Witch|Jumpstart|283|U|{2}{B}|Creature - Human Warlock|1|3|When Tempting Witch enters the battlefield, create a Food token.${2}, {T}, Sacrifice a Food: Target player loses 3 life.|
+Tithebearer Giant|Jumpstart|284|C|{5}{B}|Creature - Giant Warrior|4|5|When Tithebearer Giant enters the battlefield, you draw a card and you lose 1 life.|
 Vampire Neonate|Jumpstart|285|C|{B}|Creature - Vampire|0|3|{2}, {T}: Each opponent loses 1 life and you gain 1 life.|
 Wailing Ghoul|Jumpstart|286|C|{1}{B}|Creature - Zombie|1|3|When Wailing Ghoul enters the battlefield, put the top two cards of your library into your graveyard.|
+Wight of Precinct Six|Jumpstart|287|C|{1}{B}|Creature - Zombie|1|1|Wight of Precinct Six gets +1/+1 for each creature card in your opponents' graveyards.|
+Zombie Infestation|Jumpstart|288|U|{1}{B}|Enchantment|||Discard two cards: Create a 2/2 black Zombie creature token.|
+Ashmouth Hound|Jumpstart|290|C|{1}{R}|Creature - Elemental Hound|2|1|Whenever Ashmouth Hound blocks or becomes blocked by a creature, Ashmouth Hound deals 1 damage to that creature.|
+Bloodrock Cyclops|Jumpstart|297|C|{2}{R}|Creature - Cyclops|3|3|Bloodrock Cyclops attacks each combat if able.|
+Cinder Elemental|Jumpstart|304|U|{3}{R}|Creature - Elemental|2|2|{X}{R}, {T}, Sacrifice Cinder Elemental: It deals X damage to any target.|
 Fanatical Firebrand|Jumpstart|315|C|{R}|Creature - Goblin Pirate|1|1|Haste${T}, Sacrifice Fanatical Firebrand: It deals 1 damage to any target.|
+Flames of the Firebrand|Jumpstart|317|U|{2}{R}|Sorcery|||Flames of the Firebrand deals 3 damage divided as you choose among one, two, or three targets.|
+Grim Lavamancer|Jumpstart|331|R|{R}|Creature - Human Wizard|1|1|{R}, {T}, Exile two cards from your graveyard: Grim Lavamancer deals 2 damage to any target.|
 Hungry Flames|Jumpstart|336|C|{2}{R}|Instant|||Hungry Flames deals 3 damage to target creature and 2 damage to target player or planeswalker.|
-Pillar of Flame|Jumpstart|355|R|{R}|Sorcery|||Pillar of Flame deals 2 damage to any target. If a creature dealt damage this way would die this turn, exile it instead.|
+Magma Jet|Jumpstart|346|U|{1}{R}|Instant|||Magma Jet deals 2 damage to any target. Scry 2.|
+Magmaquake|Jumpstart|347|R|{X}{R}{R}|Instant|||Magmaquake deals X damage to each creature without flying and each planeswalker.|
+Molten Ravager|Jumpstart|351|C|{2}{R}|Creature - Elemental|0|4|{R}: Molten Ravager gets +1/+0 until end of turn.|
+Pillar of Flame|Jumpstart|355|C|{R}|Sorcery|||Pillar of Flame deals 2 damage to any target. If a creature dealt damage this way would die this turn, exile it instead.|
 Pyroclastic Elemental|Jumpstart|356|U|{3}{R}{R}|Creature - Elemental|5|4|{1}{R}{R}: Pyroclastic Elemental deals 1 damage to target player.|
-Flames of the Firebrand|Jumpstart|357|U|{2}{R}|Sorcery|||Flames of the Firebrand deals 3 damage divided as you choose among one, two, or three targets.|
+Seismic Elemental|Jumpstart|362|U|{3}{R}{R}|Creature - Elemental|4|4|When Seismic Elemental enters the battlefield, creatures without flying can't block this turn.|
+Spitting Earth|Jumpstart|364|C|{1}{R}|Sorcery|||Spitting Earth deals damage to target creature equal to the number of Mountains you control.|
+Volcanic Fallout|Jumpstart|368|U|{1}{R}{R}|Instant|||This spell can't be countered.$Volcanic Fallout deals 2 damage to each creature and each player.|
 Young Pyromancer|Jumpstart|372|U|{1}{R}|Creature - Human Shaman|2|1|Whenever you cast an instant or sorcery spell, create a 1/1 red Elemental creature token.|
 Affectionate Indrik|Jumpstart|373|U|{5}{G}|Creature - Beast|4|4|When Affectionate Indrik enters the battlefield, you may have it fight target creature you don't control.|
 Aggressive Urge|Jumpstart|374|C|{1}{G}|Instant|||Target creature gets +1/+1 until end of turn.$Draw a card.|
 Ambassador Oak|Jumpstart|375|C|{3}{G}|Creature - Treefolk Warrior|3|3|When Ambassador Oak enters the battlefield, create a 1/1 green Elf Warrior creature token.|
+Assault Formation|Jumpstart|378|R|{1}{G}|Enchantment|||Each creature you control assigns combat damage equal to its toughness rather than its power.${G}: Target creature with defender can attack this turn as though it didn't have defender.${2}{G}: Creatures you control get +0/+1 until end of turn.|
 Awakener Druid|Jumpstart|379|U|{2}{G}|Creature - Human Druid|1|1|When Awakener Druid enters the battlefield, target Forest becomes a 4/5 green Treefolk creature for as long as Awakener Druid remains on the battlefield. It's still a land.|
+Brindle Shoat|Jumpstart|380|U|{1}{G}|Creature - Boar|1|1|When Brindle Shoat dies, create a 3/3 green Boar creature token.|
 Brushstrider|Jumpstart|381|U|{1}{G}|Creature - Beast|3|1|Vigilance|
+Carven Caryatid|Jumpstart|382|U|{1}{G}{G}|Creature - Spirit|2|5|Defender$When Carven Caryatid enters the battlefield, draw a card.|
+Commune with Dinosaurs|Jumpstart|384|C|{G}|Sorcery|||Look at the top five cards of your library. You may reveal a Dinosaur or land card from among them and put it into your hand. Put the rest on the bottom of your library in any order.|
+Craterhoof Behemoth|Jumpstart|385|M|{5}{G}{G}{G}|Creature - Beast|5|5|Haste$When Craterhoof Behemoth enters the battlefield, creatures you control gain trample and get +X/+X until end of turn, where X is the number of creatures you control.|
 Crushing Canopy|Jumpstart|386|C|{2}{G}|Instant|||Choose one —$• Destroy target creature with flying.$• Destroy target enchantment.|
+Dawntreader Elk|Jumpstart|387|C|{1}{G}|Creature - Elk|2|2|{G}, Sacrifice Dawntreader Elk: Search your library for a basic land card, put it onto the battlefield tapped, then shuffle your library.|
+Drover of the Mighty|Jumpstart|388|U|{1}{G}|Creature - Human Druid|1|1|Drover of the Mighty gets +2/+2 as long as you control a Dinosaur.${T}: Add one mana of any color.|
+Dwynen's Elite|Jumpstart|389|U|{1}{G}|Creature - Elf Warrior|2|2|When Dwynen's Elite enters the battlefield, if you control another Elf, create a 1/1 green Elf Warrior creature token.|
+Elemental Uprising|Jumpstart|390|C|{1}{G}|Instant|||Target land you control becomes a 4/4 Elemental creature with haste until end of turn. It's still a land. It must be blocked this turn if able.|
+Elvish Archdruid|Jumpstart|391|R|{1}{G}{G}|Creature - Elf Druid|2|2|Other Elf creatures you control get +1/+1.${T}: Add {G} for each Elf you control.|
+Enlarge|Jumpstart|392|U|{3}{G}{G}|Sorcery|||Target creature gets +7/+7 and gains trample until end of turn. It must be blocked this turn if able.|
 Explore|Jumpstart|393|C|{1}{G}|Sorcery|||You may play an additional land this turn.$Draw a card.|
+Fa'adiyah Seer|Jumpstart|394|C|{1}{G}|Creature - Human Shaman|1|1|{T}: Draw a card and reveal it. If it isn't a land card, discard it.|
 Feral Hydra|Jumpstart|395|U|{X}{G}|Creature - Hydra Beast|0|0|Feral Hydra enters the battlefield with X +1/+1 counters on it.${3}: Put a +1/+1 counter on Feral Hydra. Any player may activate this ability.|
 Fertilid|Jumpstart|398|C|{2}{G}|Creature - Elemental|0|0|Fertilid enters the battlefield with two +1/+1 counters on it.${1}{G}, Remove a +1/+1 counter from Fertilid: Target player searches their library for a basic land card, puts it onto the battlefield tapped, then shuffles their library.|
+Ghalta, Primal Hunger|Jumpstart|399|R|{10}{G}{G}|Legendary Creature - Elder Dinosaur|12|12|This spell costs {X} less to cast, where X is the total power of creatures you control.$Trample|
+Ghirapur Guide|Jumpstart|400|U|{2}{G}|Creature - Elf Scout|3|2|{2}{G}: Target creature you control can't be blocked by creatures with power 2 or less this turn.|
+Grave Bramble|Jumpstart|401|C|{1}{G}{G}|Creature - Plant|3|4|Defender, protection from Zombies|
+Hunter's Insight|Jumpstart|402|U|{2}{G}|Instant|||Choose target creature you control. Whenever that creature deals combat damage to a player or planeswalker this turn, draw that many cards.|
+Irresistible Prey|Jumpstart|406|U|{G}|Sorcery|||Target creature must be blocked this turn if able.$Draw a card.|
+Leaf Gilder|Jumpstart|408|C|{1}{G}|Creature - Elf Druid|2|1|{T}: Add {G}.|
+Lurking Predators|Jumpstart|410|R|{4}{G}{G}|Enchantment|||Whenever an opponent casts a spell, reveal the top card of your library. If it's a creature card, put it onto the battlefield. Otherwise, you may put that card on the bottom of your library.|
+Momentous Fall|Jumpstart|411|R|{2}{G}{G}|Instant|||As an additional cost to cast this spell, sacrifice a creature.$You draw cards equal to the sacrificed creature's power, then you gain life equal to its toughness.|
 Nature's Way|Jumpstart|412|U|{1}{G}|Sorcery|||Target creature you control gains vigilance and trample until end of turn. It deals damage equal to its power to target creature you don't control.|
+New Horizons|Jumpstart|414|C|{2}{G}|Enchantment - Aura|||Enchant land$When New Horizons enters the battlefield, put a +1/+1 counter on target creature you control.$Enchanted land has "{T}: Add two mana of any one color."|
 Oracle of Mul Daya|Jumpstart|415|R|{3}{G}|Creature - Elf Shaman|2|2|You may play an additional land on each of your turns.$Play with the top card of your library revealed.$You may play lands from the top of your library.|
+Orazca Frillback|Jumpstart|416|C|{2}{G}|Creature - Dinosaur|4|2||
+Overgrown Battlement|Jumpstart|417|U|{1}{G}|Creature - Wall|0|4|Defender${T}: Add {G} for each creature with defender you control.|
+Penumbra Bobcat|Jumpstart|418|C|{2}{G}|Creature - Cat|2|1|When Penumbra Bobcat dies, create a 2/1 black Cat creature token.|
+Presence of Gond|Jumpstart|420|C|{2}{G}|Enchantment - Aura|||Enchant creature$Enchanted creature has "{T}: Create a 1/1 green Elf Warrior creature token."|
 Primordial Sage|Jumpstart|422|R|{4}{G}{G}|Creature - Spirit|4|5|Whenever you cast a creature spell, you may draw a card.|
+Rampaging Brontodon|Jumpstart|423|R|{5}{G}{G}|Creature - Dinosaur|7|7|Trample$Whenever Rampaging Brontodon attacks, it gets +1/+1 until end of turn for each land you control.|
+Ravenous Baloth|Jumpstart|424|R|{2}{G}{G}|Creature - Beast|4|4|Sacrifice a Beast: You gain 4 life.|
+Rumbling Baloth|Jumpstart|426|C|{2}{G}{G}|Creature - Beast|4|4||
+Savage Stomp|Jumpstart|427|U|{2}{G}|Sorcery|||This spell costs {2} less to cast if it targets a Dinosaur you control.$Put a +1/+1 counter on target creature you control. Then that creature fights target creature you don't control.|
+Selvala, Heart of the Wilds|Jumpstart|429|M|{1}{G}{G}|Legendary Creature - Elf Scout|2|3|Whenever another creature enters the battlefield, its controller may draw a card if its power is greater than each other creature's power.${G}, {T}: Add X mana in any combination of colors, where X is the greatest power among creatures you control.|
+Silhana Wayfinder|Jumpstart|430|U|{1}{G}|Creature - Elf Scout|2|1|When Silhana Wayfinder enters the battlefield, look at the top four cards of your library. You may reveal a creature or land card from among them and put it on top of your library. Put the rest on the bottom of your library in a random order.|
+Somberwald Stag|Jumpstart|431|U|{3}{G}{G}|Creature - Elk|4|3|When Somberwald Stag enters the battlefield, you may have it fight target creature you don't control.|
 Soul of the Harvest|Jumpstart|432|R|{4}{G}{G}|Creature - Elemental|6|6|Trample$Whenever another nontoken creature enters the battlefield under your control, you may draw a card.|
 Sporemound|Jumpstart|433|C|{3}{G}{G}|Creature - Fungus|3|3|Whenever a land enters the battlefield under your control, create a 1/1 green Saproling creature token.|
+Sylvan Brushstrider|Jumpstart|434|C|{2}{G}|Creature - Beast|3|2|When Sylvan Brushstrider enters the battlefield, you gain 2 life.|
 Sylvan Ranger|Jumpstart|435|C|{1}{G}|Creature - Elf Scout|1|1|When Sylvan Ranger enters the battlefield, you may search your library for a basic land card, reveal it, put it into your hand, then shuffle your library.|
+Thragtusk|Jumpstart|436|R|{4}{G}|Creature - Beast|5|3|When Thragtusk enters the battlefield, you gain 5 life.$When Thragtusk leaves the battlefield, create a 3/3 green Beast creature token.|
+Thundering Spineback|Jumpstart|437|U|{5}{G}{G}|Creature - Dinosaur|5|5|Other Dinosaurs you control get +1/+1.${5}{G}: Create a 3/3 green Dinosaur creature token with trample.|
 Ulvenwald Hydra|Jumpstart|439|M|{4}{G}{G}|Creature - Hydra|*|*|Reach$Ulvenwald Hydra's power and toughness are each equal to the number of lands you control.$When Ulvenwald Hydra enters the battlefield, you may search your library for a land card, put it onto the battlefield tapped, then shuffle your library.|
 Vastwood Zendikon|Jumpstart|440|C|{4}{G}|Enchantment - Aura|||Enchant land$Enchanted land is a 6/4 green Elemental creature. It's still a land.$When enchanted land dies, return that card to its owner's hand.|
 Verdant Embrace|Jumpstart|441|R|{3}{G}{G}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +3/+3 and has "At the beginning of each upkeep, create a 1/1 green Saproling creature token."|
 Wall of Blossoms|Jumpstart|442|U|{1}{G}|Creature - Plant Wall|0|4|Defender$When Wall of Blossoms enters the battlefield, draw a card.|
+Wall of Vines|Jumpstart|443|C|{G}|Creature - Plant Wall|0|3|Defender$Reach|
+Wildheart Invoker|Jumpstart|444|C|{2}{G}{G}|Creature - Elf Shaman|4|3|{8}: Target creature gets +5/+5 and gains trample until end of turn.|
 Woodborn Behemoth|Jumpstart|446|U|{3}{G}{G}|Creature - Elemental|4|4|As long as you control eight or more lands, Woodborn Behemoth gets +4/+4 and has trample.|
+Wren's Run Vanquisher|Jumpstart|447|U|{1}{G}|Creature - Elf Warrior|3|3|As an additional cost to cast this spell, reveal an Elf card from your hand or pay {3}.$Deathtouch|
 Zendikar's Roil|Jumpstart|448|U|{3}{G}{G}|Enchantment|||Whenever a land enters the battlefield under your control, create a 2/2 green Elemental creature token.|
 Auger Spree|Jumpstart|449|C|{1}{B}{R}|Instant|||Target creature gets +4/-4 until end of turn.|
 Dinrova Horror|Jumpstart|450|U|{4}{U}{B}|Creature - Horror|4|4|When Dinrova Horror enters the battlefield, return target permanent to its owner's hand, then that player discards a card.|
@@ -37838,15 +37930,30 @@ Raging Regisaur|Jumpstart|455|U|{2}{R}{G}|Creature - Dinosaur|4|4|Whenever Ragin
 Aether Spellbomb|Jumpstart|456|C|{1}|Artifact|||{U}, Sacrifice Aether Spellbomb: Return target creature to its owner's hand.${1}, Sacrifice Aether Spellbomb: Draw a card.|
 Alloy Myr|Jumpstart|457|C|{3}|Artifact Creature - Myr|2|2|{T}: Add one mana of any color.|
 Ancestral Statue|Jumpstart|458|C|{4}|Artifact Creature - Golem|3|4|When Ancestral Statue enters the battlefield, return a nonland permanent you control to its owner's hand.|
+Arcane Encyclopedia|Jumpstart|459|U|{3}|Artifact|||{3}, {T}: Draw a card.|
 Bubbling Cauldron|Jumpstart|460|U|{2}|Artifact|||{1}, {T}, Sacrifice a creature: You gain 4 life.${1}, {T}, Sacrifice a creature named Festering Newt: Each opponent loses 4 life. You gain life equal to the life lost this way.|
 Chamber Sentry|Jumpstart|461|R|{X}|Artifact Creature - Construct|0|0|Chamber Sentry enters the battlefield with a +1/+1 counter on it for each color of mana spent to cast it.${X}, {T}, Remove X +1/+1 counters from Chamber Sentry: It deals X damage to any target.${W}{U}{B}{R}{G}: Return Chamber Sentry from your graveyard to your hand.|
 Chromatic Sphere|Jumpstart|462|C|{1}|Artifact|||{1}, {T}, Sacrifice Chromatic Sphere: Add one mana of any color. Draw a card.|
+Dreamstone Hedron|Jumpstart|464|U|{6}|Artifact|||{T}: Add {C}{C}{C}.${3}, {T}, Sacrifice Dreamstone Hedron: Draw three cards.|
+Gargoyle Sentinel|Jumpstart|465|U|{3}|Artifact Creature - Gargoyle|3|3|Defender${3}: Until end of turn, Gargoyle Sentinel loses defender and gains flying.|
+Gingerbrute|Jumpstart|466|C|{1}|Artifact Creature - Food Golem|1|1|Haste${1}: Gingerbrute can't be blocked this turn except by creatures with haste.${2}, {T}, Sacrifice Gingerbrute: You gain 3 life.|
 Hedron Archive|Jumpstart|468|U|{4}|Artifact|||{T}: Add {C}{C}.${2}, {T}, Sacrifice Hedron Archive: Draw two cards.|
+Jousting Dummy|Jumpstart|470|C|{2}|Artifact Creature - Scarecrow Knight|2|1|{3}: Jousting Dummy gets +1/+0 until end of turn.|
 Juggernaut|Jumpstart|471|U|{4}|Artifact Creature - Juggernaut|5|3|Juggernaut attacks each combat if able.$Juggernaut can't be blocked by Walls.|
+Marauder's Axe|Jumpstart|473|C|{2}|Artifact - Equipment|||Equipped creature gets +2/+0.$Equip {2}|
 Meteor Golem|Jumpstart|474|U|{7}|Artifact Creature - Golem|3|3|When Meteor Golem enters the battlefield, destroy target nonland permanent an opponent controls.|
 Prophetic Prism|Jumpstart|478|C|{2}|Artifact|||When Prophetic Prism enters the battlefield, draw a card.${1}, {T}: Add one mana of any color.|
+Roving Keep|Jumpstart|480|C|{7}|Artifact Creature - Wall|5|7|Defender${7}: Roving Keep gets +2/+0 and gains trample until end of turn. It can attack this turn as though it didn't have defender.|
+Runed Servitor|Jumpstart|481|C|{2}|Artifact Creature - Construct|2|2|When Runed Servitor dies, each player draws a card.|
+Scarecrone|Jumpstart|482|R|{3}|Artifact Creature - Scarecrow|1|2|{1}, Sacrifice a Scarecrow: Draw a card.${4}, {T}: Return target artifact creature card from your graveyard to the battlefield.|
+Scuttlemutt|Jumpstart|484|U|{3}|Artifact Creature - Scarecrow|2|2|{T}: Add one mana of any color.${T}: Target creature becomes the color or colors of your choice until end of turn.|
+Signpost Scarecrow|Jumpstart|485|C|{4}|Artifact Creature - Scarecrow|2|4|Vigilance${2}: Add one mana of any color.|
 Skittering Surveyor|Jumpstart|486|C|{3}|Artifact Creature - Construct|1|2|When Skittering Surveyor enters the battlefield, you may search your library for a basic land card, reveal it, put it into your hand, then shuffle your library.|
+Suspicious Bookcase|Jumpstart|487|U|{2}|Artifact Creature - Wall|0|4|Defender${3}, {T}: Target creature can't be blocked this turn.|
+Terrarion|Jumpstart|488|C|{1}|Artifact|||Terrarion enters the battlefield tapped.${2}, {T}, Sacrifice Terrarion: Add two mana in any combination of colors.$When Terrarion is put into a graveyard from the battlefield, draw a card.|
+Warmonger's Chariot|Jumpstart|490|U|{2}|Artifact - Equipment|||Equipped creature gets +2/+2.$As long as equipped creature has defender, it can attack as though it didn't have defender.$Equip {3}|
 Buried Ruin|Jumpstart|491|U||Land|||{T}: Add {C}.${2}, {T}, Sacrifice Buried Ruin: Return target artifact card from your graveyard to your hand.|
 Mirrodin's Core|Jumpstart|492|U||Land|||{T}: Add {C}.${T}: Put a charge counter on Mirrodin's Core.${T}, Remove a charge counter from Mirrodin's Core: Add one mana of any color.|
+Phyrexian Tower|Jumpstart|493|R||Legendary Land|||{T}: Add {C}.${T}, Sacrifice a creature: Add {B}{B}.|
 Riptide Laboratory|Jumpstart|494|R||Land|||{T}: Add {C}.${1}{U}, {T}: Return target Wizard you control to its owner's hand.|
 Rupture Spire|Jumpstart|495|C||Land|||Rupture Spire enters the battlefield tapped.$When Rupture Spire enters the battlefield, sacrifice it unless you pay {1}.${T}: Add one mana of any color.|

From 410a20d33d9bf945256ede4fdc81c4efc9bcfbe7 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 18 Jun 2020 20:28:14 -0400
Subject: [PATCH 383/586] Implemented Lightning-Core Excavator

---
 .../mage/cards/l/LightningCoreExcavator.java  | 48 +++++++++++++++++++
 Mage.Sets/src/mage/sets/Jumpstart.java        |  1 +
 2 files changed, 49 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/l/LightningCoreExcavator.java

diff --git a/Mage.Sets/src/mage/cards/l/LightningCoreExcavator.java b/Mage.Sets/src/mage/cards/l/LightningCoreExcavator.java
new file mode 100644
index 0000000000..bceff2ac0f
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/l/LightningCoreExcavator.java
@@ -0,0 +1,48 @@
+package mage.cards.l;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.common.SacrificeSourceCost;
+import mage.abilities.costs.common.TapSourceCost;
+import mage.abilities.costs.mana.GenericManaCost;
+import mage.abilities.effects.common.DamageTargetEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.target.common.TargetAnyTarget;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class LightningCoreExcavator extends CardImpl {
+
+    public LightningCoreExcavator(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{1}");
+
+        this.subtype.add(SubType.GOLEM);
+        this.power = new MageInt(0);
+        this.toughness = new MageInt(3);
+
+        // {5}, {T}, Sacrifice Lightning-Core Excavator: It deals 3 damage to any target.
+        Ability ability = new SimpleActivatedAbility(
+                new DamageTargetEffect(3, "it"), new GenericManaCost(5)
+        );
+        ability.addCost(new TapSourceCost());
+        ability.addCost(new SacrificeSourceCost());
+        ability.addTarget(new TargetAnyTarget());
+        this.addAbility(ability);
+    }
+
+    private LightningCoreExcavator(final LightningCoreExcavator card) {
+        super(card);
+    }
+
+    @Override
+    public LightningCoreExcavator copy() {
+        return new LightningCoreExcavator(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index 1b29e3972a..2e639361a0 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -127,6 +127,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Lawmage's Binding", 453, Rarity.COMMON, mage.cards.l.LawmagesBinding.class));
         cards.add(new SetCardInfo("Leaf Gilder", 408, Rarity.COMMON, mage.cards.l.LeafGilder.class));
         cards.add(new SetCardInfo("Leave in the Dust", 156, Rarity.COMMON, mage.cards.l.LeaveInTheDust.class));
+        cards.add(new SetCardInfo("Lightning-Core Excavator", 32, Rarity.COMMON, mage.cards.l.LightningCoreExcavator.class));
         cards.add(new SetCardInfo("Liliana's Elite", 250, Rarity.UNCOMMON, mage.cards.l.LilianasElite.class));
         cards.add(new SetCardInfo("Lurking Predators", 410, Rarity.RARE, mage.cards.l.LurkingPredators.class));
         cards.add(new SetCardInfo("Macabre Waltz", 252, Rarity.COMMON, mage.cards.m.MacabreWaltz.class));

From 9876fa9ef907518a051b167085f7469401c9be1e Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 18 Jun 2020 20:28:50 -0400
Subject: [PATCH 384/586] Implemented Lightning Visionary

---
 .../src/mage/cards/l/LightningVisionary.java  | 37 +++++++++++++++++++
 Mage.Sets/src/mage/sets/Jumpstart.java        |  1 +
 2 files changed, 38 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/l/LightningVisionary.java

diff --git a/Mage.Sets/src/mage/cards/l/LightningVisionary.java b/Mage.Sets/src/mage/cards/l/LightningVisionary.java
new file mode 100644
index 0000000000..282585748b
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/l/LightningVisionary.java
@@ -0,0 +1,37 @@
+package mage.cards.l;
+
+import mage.MageInt;
+import mage.abilities.keyword.ProwessAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class LightningVisionary extends CardImpl {
+
+    public LightningVisionary(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}");
+
+        this.subtype.add(SubType.MINOTAUR);
+        this.subtype.add(SubType.SHAMAN);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(1);
+
+        // Prowess
+        this.addAbility(new ProwessAbility());
+    }
+
+    private LightningVisionary(final LightningVisionary card) {
+        super(card);
+    }
+
+    @Override
+    public LightningVisionary copy() {
+        return new LightningVisionary(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index 2e639361a0..0f9321ede6 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -127,6 +127,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Lawmage's Binding", 453, Rarity.COMMON, mage.cards.l.LawmagesBinding.class));
         cards.add(new SetCardInfo("Leaf Gilder", 408, Rarity.COMMON, mage.cards.l.LeafGilder.class));
         cards.add(new SetCardInfo("Leave in the Dust", 156, Rarity.COMMON, mage.cards.l.LeaveInTheDust.class));
+        cards.add(new SetCardInfo("Lightning Visionary", 22, Rarity.COMMON, mage.cards.l.LightningVisionary.class));
         cards.add(new SetCardInfo("Lightning-Core Excavator", 32, Rarity.COMMON, mage.cards.l.LightningCoreExcavator.class));
         cards.add(new SetCardInfo("Liliana's Elite", 250, Rarity.UNCOMMON, mage.cards.l.LilianasElite.class));
         cards.add(new SetCardInfo("Lurking Predators", 410, Rarity.RARE, mage.cards.l.LurkingPredators.class));

From 79e098bb3ba9dd537504aa2f1300d7f92f1354a3 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 18 Jun 2020 20:31:36 -0400
Subject: [PATCH 385/586] Implemented Living Lightning

---
 .../src/mage/cards/l/LivingLightning.java     | 47 +++++++++++++++++++
 Mage.Sets/src/mage/sets/Jumpstart.java        |  1 +
 2 files changed, 48 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/l/LivingLightning.java

diff --git a/Mage.Sets/src/mage/cards/l/LivingLightning.java b/Mage.Sets/src/mage/cards/l/LivingLightning.java
new file mode 100644
index 0000000000..2b887c82c4
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/l/LivingLightning.java
@@ -0,0 +1,47 @@
+package mage.cards.l;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.filter.FilterCard;
+import mage.filter.common.FilterInstantOrSorceryCard;
+import mage.target.common.TargetCardInYourGraveyard;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class LivingLightning extends CardImpl {
+
+    private static final FilterCard filter
+            = new FilterInstantOrSorceryCard("instant or sorcery card from your graveyard");
+
+    public LivingLightning(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}");
+
+        this.subtype.add(SubType.ELEMENTAL);
+        this.subtype.add(SubType.SHAMAN);
+        this.power = new MageInt(3);
+        this.toughness = new MageInt(2);
+
+        // When Living Lightning dies, return target instant or sorcery card from your graveyard to your hand.
+        Ability ability = new DiesTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect());
+        ability.addTarget(new TargetCardInYourGraveyard(filter));
+        this.addAbility(ability);
+    }
+
+    private LivingLightning(final LivingLightning card) {
+        super(card);
+    }
+
+    @Override
+    public LivingLightning copy() {
+        return new LivingLightning(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index 0f9321ede6..4964a0e5c6 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -130,6 +130,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Lightning Visionary", 22, Rarity.COMMON, mage.cards.l.LightningVisionary.class));
         cards.add(new SetCardInfo("Lightning-Core Excavator", 32, Rarity.COMMON, mage.cards.l.LightningCoreExcavator.class));
         cards.add(new SetCardInfo("Liliana's Elite", 250, Rarity.UNCOMMON, mage.cards.l.LilianasElite.class));
+        cards.add(new SetCardInfo("Living Lightning", 23, Rarity.UNCOMMON, mage.cards.l.LivingLightning.class));
         cards.add(new SetCardInfo("Lurking Predators", 410, Rarity.RARE, mage.cards.l.LurkingPredators.class));
         cards.add(new SetCardInfo("Macabre Waltz", 252, Rarity.COMMON, mage.cards.m.MacabreWaltz.class));
         cards.add(new SetCardInfo("Maelstrom Archangel", 454, Rarity.MYTHIC, mage.cards.m.MaelstromArchangel.class));

From 52be5d7a170b1fc469ea741987b4eaff31df3dd0 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 18 Jun 2020 20:35:14 -0400
Subject: [PATCH 386/586] Implemented Supply Runners

---
 Mage.Sets/src/mage/cards/s/SupplyRunners.java | 50 +++++++++++++++++++
 Mage.Sets/src/mage/sets/Jumpstart.java        |  1 +
 2 files changed, 51 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/s/SupplyRunners.java

diff --git a/Mage.Sets/src/mage/cards/s/SupplyRunners.java b/Mage.Sets/src/mage/cards/s/SupplyRunners.java
new file mode 100644
index 0000000000..72ed35e23b
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/SupplyRunners.java
@@ -0,0 +1,50 @@
+package mage.cards.s;
+
+import mage.MageInt;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.effects.common.counter.AddCountersAllEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.counters.CounterType;
+import mage.filter.FilterPermanent;
+import mage.filter.common.FilterControlledCreaturePermanent;
+import mage.filter.predicate.permanent.AnotherPredicate;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class SupplyRunners extends CardImpl {
+
+    private static final FilterPermanent filter
+            = new FilterControlledCreaturePermanent("other creature you control");
+
+    static {
+        filter.add(AnotherPredicate.instance);
+    }
+
+    public SupplyRunners(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}");
+
+        this.subtype.add(SubType.DOG);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(2);
+
+        // When Supply Runners enters the battlefield, put a +1/+1 counter on each other creature you control.
+        this.addAbility(new EntersBattlefieldTriggeredAbility(
+                new AddCountersAllEffect(CounterType.P1P1.createInstance(), filter)
+        ));
+    }
+
+    private SupplyRunners(final SupplyRunners card) {
+        super(card);
+    }
+
+    @Override
+    public SupplyRunners copy() {
+        return new SupplyRunners(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index 4964a0e5c6..a95158adfe 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -202,6 +202,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Spitting Earth", 364, Rarity.COMMON, mage.cards.s.SpittingEarth.class));
         cards.add(new SetCardInfo("Sporemound", 433, Rarity.COMMON, mage.cards.s.Sporemound.class));
         cards.add(new SetCardInfo("Storm Sculptor", 179, Rarity.COMMON, mage.cards.s.StormSculptor.class));
+        cards.add(new SetCardInfo("Supply Runners", 7, Rarity.UNCOMMON, mage.cards.s.SupplyRunners.class));
         cards.add(new SetCardInfo("Suspicious Bookcase", 487, Rarity.UNCOMMON, mage.cards.s.SuspiciousBookcase.class));
         cards.add(new SetCardInfo("Swamp", 54, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Swarm of Bloodflies", 282, Rarity.UNCOMMON, mage.cards.s.SwarmOfBloodflies.class));

From 40667a207fac2b8c6628546ffa5ebfd3342b795f Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 18 Jun 2020 20:43:07 -0400
Subject: [PATCH 387/586] Implemented Towering Titan

---
 Mage.Sets/src/mage/cards/t/ToweringTitan.java | 100 ++++++++++++++++++
 Mage.Sets/src/mage/sets/Jumpstart.java        |   1 +
 2 files changed, 101 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/t/ToweringTitan.java

diff --git a/Mage.Sets/src/mage/cards/t/ToweringTitan.java b/Mage.Sets/src/mage/cards/t/ToweringTitan.java
new file mode 100644
index 0000000000..777e7118a3
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/t/ToweringTitan.java
@@ -0,0 +1,100 @@
+package mage.cards.t;
+
+import mage.MageInt;
+import mage.MageObject;
+import mage.abilities.Ability;
+import mage.abilities.common.EntersBattlefieldAbility;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.common.SacrificeTargetCost;
+import mage.abilities.dynamicvalue.DynamicValue;
+import mage.abilities.effects.Effect;
+import mage.abilities.effects.common.continuous.GainAbilityAllEffect;
+import mage.abilities.effects.common.counter.AddCountersSourceEffect;
+import mage.abilities.keyword.DefenderAbility;
+import mage.abilities.keyword.TrampleAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+import mage.counters.CounterType;
+import mage.filter.StaticFilters;
+import mage.filter.common.FilterControlledCreaturePermanent;
+import mage.filter.common.FilterControlledPermanent;
+import mage.filter.predicate.mageobject.AbilityPredicate;
+import mage.game.Game;
+import mage.target.common.TargetControlledPermanent;
+
+import java.util.Objects;
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class ToweringTitan extends CardImpl {
+
+    private static final FilterControlledPermanent filter
+            = new FilterControlledCreaturePermanent("a creature with defender");
+
+    static {
+        filter.add(new AbilityPredicate(DefenderAbility.class));
+    }
+
+    public ToweringTitan(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}{G}");
+
+        this.subtype.add(SubType.GIANT);
+        this.power = new MageInt(0);
+        this.toughness = new MageInt(0);
+
+        // Towering Titan enters the battlefield with X +1/+1 counters on it, where X is the total toughness of other creatures you control.
+        this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(
+                CounterType.P1P1.createInstance(), ToweringTitanCount.instance, false
+        ), "with X +1/+1 counters on it, where X is the total toughness of other creatures you control"));
+
+        // Sacrifice a creature with defender: All creatures gain trample until end of turn.
+        this.addAbility(new SimpleActivatedAbility(
+                new GainAbilityAllEffect(
+                        TrampleAbility.getInstance(), Duration.EndOfTurn,
+                        StaticFilters.FILTER_PERMANENT_CREATURE
+                ).setText("All creatures gain trample until end of turn"),
+                new SacrificeTargetCost(new TargetControlledPermanent(filter))
+        ));
+    }
+
+    private ToweringTitan(final ToweringTitan card) {
+        super(card);
+    }
+
+    @Override
+    public ToweringTitan copy() {
+        return new ToweringTitan(this);
+    }
+}
+
+enum ToweringTitanCount implements DynamicValue {
+    instance;
+
+    @Override
+    public int calculate(Game game, Ability sourceAbility, Effect effect) {
+        return game.getBattlefield()
+                .getActivePermanents(
+                        StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE,
+                        sourceAbility.getControllerId(), sourceAbility.getSourceId(), game
+                ).stream()
+                .filter(Objects::nonNull)
+                .map(MageObject::getToughness)
+                .mapToInt(MageInt::getValue)
+                .sum();
+    }
+
+    @Override
+    public ToweringTitanCount copy() {
+        return null;
+    }
+
+    @Override
+    public String getMessage() {
+        return "";
+    }
+}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index a95158adfe..437e72c3f7 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -222,6 +222,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Thriving Moor", 37, Rarity.COMMON, mage.cards.t.ThrivingMoor.class));
         cards.add(new SetCardInfo("Thundering Spineback", 437, Rarity.UNCOMMON, mage.cards.t.ThunderingSpineback.class));
         cards.add(new SetCardInfo("Tithebearer Giant", 284, Rarity.COMMON, mage.cards.t.TithebearerGiant.class));
+        cards.add(new SetCardInfo("Towering Titan", 31, Rarity.MYTHIC, mage.cards.t.ToweringTitan.class));
         cards.add(new SetCardInfo("Ulvenwald Hydra", 439, Rarity.MYTHIC, mage.cards.u.UlvenwaldHydra.class));
         cards.add(new SetCardInfo("Vampire Neonate", 285, Rarity.COMMON, mage.cards.v.VampireNeonate.class));
         cards.add(new SetCardInfo("Vastwood Zendikon", 440, Rarity.COMMON, mage.cards.v.VastwoodZendikon.class));

From d6f87d29c6d8ff6a50bb887ca8ff48388438eaea Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 18 Jun 2020 20:46:16 -0400
Subject: [PATCH 388/586] Implemented Trusty Retriever

---
 .../src/mage/cards/t/TrustyRetriever.java     | 54 +++++++++++++++++++
 Mage.Sets/src/mage/sets/Jumpstart.java        |  1 +
 2 files changed, 55 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/t/TrustyRetriever.java

diff --git a/Mage.Sets/src/mage/cards/t/TrustyRetriever.java b/Mage.Sets/src/mage/cards/t/TrustyRetriever.java
new file mode 100644
index 0000000000..aab1fa609e
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/t/TrustyRetriever.java
@@ -0,0 +1,54 @@
+package mage.cards.t;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.Mode;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect;
+import mage.abilities.effects.common.counter.AddCountersSourceEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.counters.CounterType;
+import mage.filter.FilterCard;
+import mage.filter.common.FilterArtifactOrEnchantmentCard;
+import mage.target.common.TargetCardInYourGraveyard;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class TrustyRetriever extends CardImpl {
+
+    private static final FilterCard filter
+            = new FilterArtifactOrEnchantmentCard("artifact or enchantment card from your graveyard");
+
+    public TrustyRetriever(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}");
+
+        this.subtype.add(SubType.DOG);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(3);
+
+        // When Trusty Retriever enters the battlefield, choose one —
+        // • Put a +1/+1 counter on Trusty Retriever.
+        Ability ability = new EntersBattlefieldTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()));
+
+        // • Return target artifact or enchantment card from your graveyard to your hand.
+        Mode mode = new Mode(new ReturnFromGraveyardToHandTargetEffect());
+        mode.addTarget(new TargetCardInYourGraveyard(filter));
+        ability.addMode(mode);
+        this.addAbility(ability);
+    }
+
+    private TrustyRetriever(final TrustyRetriever card) {
+        super(card);
+    }
+
+    @Override
+    public TrustyRetriever copy() {
+        return new TrustyRetriever(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index 437e72c3f7..3bf2435503 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -223,6 +223,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Thundering Spineback", 437, Rarity.UNCOMMON, mage.cards.t.ThunderingSpineback.class));
         cards.add(new SetCardInfo("Tithebearer Giant", 284, Rarity.COMMON, mage.cards.t.TithebearerGiant.class));
         cards.add(new SetCardInfo("Towering Titan", 31, Rarity.MYTHIC, mage.cards.t.ToweringTitan.class));
+        cards.add(new SetCardInfo("Trusty Retriever", 8, Rarity.COMMON, mage.cards.t.TrustyRetriever.class));
         cards.add(new SetCardInfo("Ulvenwald Hydra", 439, Rarity.MYTHIC, mage.cards.u.UlvenwaldHydra.class));
         cards.add(new SetCardInfo("Vampire Neonate", 285, Rarity.COMMON, mage.cards.v.VampireNeonate.class));
         cards.add(new SetCardInfo("Vastwood Zendikon", 440, Rarity.COMMON, mage.cards.v.VastwoodZendikon.class));

From ae32bc2b77a2775813e68a662ba9aa8284e106b2 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 18 Jun 2020 20:50:30 -0400
Subject: [PATCH 389/586] Implemented Brightmare

---
 Mage.Sets/src/mage/cards/b/Brightmare.java | 76 ++++++++++++++++++++++
 Mage.Sets/src/mage/sets/Jumpstart.java     |  1 +
 2 files changed, 77 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/b/Brightmare.java

diff --git a/Mage.Sets/src/mage/cards/b/Brightmare.java b/Mage.Sets/src/mage/cards/b/Brightmare.java
new file mode 100644
index 0000000000..5dfc133b5d
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/b/Brightmare.java
@@ -0,0 +1,76 @@
+package mage.cards.b;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.effects.OneShotEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Outcome;
+import mage.constants.SubType;
+import mage.game.Game;
+import mage.game.permanent.Permanent;
+import mage.players.Player;
+import mage.target.common.TargetCreaturePermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class Brightmare extends CardImpl {
+
+    public Brightmare(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}");
+
+        this.subtype.add(SubType.UNICORN);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(3);
+
+        // When Brightmare enters the battlefield, tap up to one target creature. You gain life equal to that creature's power.
+        Ability ability = new EntersBattlefieldTriggeredAbility(new BrightmareEffect());
+        ability.addTarget(new TargetCreaturePermanent(0, 1));
+        this.addAbility(ability);
+    }
+
+    private Brightmare(final Brightmare card) {
+        super(card);
+    }
+
+    @Override
+    public Brightmare copy() {
+        return new Brightmare(this);
+    }
+}
+
+class BrightmareEffect extends OneShotEffect {
+
+    BrightmareEffect() {
+        super(Outcome.Benefit);
+        staticText = "tap up to one target creature. You gain life equal to that creature's power";
+    }
+
+    private BrightmareEffect(final BrightmareEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public BrightmareEffect copy() {
+        return new BrightmareEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Permanent permanent = game.getPermanent(source.getFirstTarget());
+        if (permanent == null) {
+            return false;
+        }
+        permanent.tap(game);
+        Player player = game.getPlayer(source.getControllerId());
+        if (player != null) {
+            player.gainLife(permanent.getPower().getValue(), game, source);
+        }
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index 3bf2435503..5566783192 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -47,6 +47,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Bogbrew Witch", 211, Rarity.RARE, mage.cards.b.BogbrewWitch.class));
         cards.add(new SetCardInfo("Bone Picker", 212, Rarity.UNCOMMON, mage.cards.b.BonePicker.class));
         cards.add(new SetCardInfo("Bone Splinters", 213, Rarity.COMMON, mage.cards.b.BoneSplinters.class));
+        cards.add(new SetCardInfo("Brightmare", 2, Rarity.COMMON, mage.cards.b.Brightmare.class));
         cards.add(new SetCardInfo("Brindle Shoat", 380, Rarity.UNCOMMON, mage.cards.b.BrindleShoat.class));
         cards.add(new SetCardInfo("Brushstrider", 381, Rarity.UNCOMMON, mage.cards.b.Brushstrider.class));
         cards.add(new SetCardInfo("Bubbling Cauldron", 460, Rarity.UNCOMMON, mage.cards.b.BubblingCauldron.class));

From 6bfe84ffd5d563cdac14fab78a71fc659012ffd1 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 18 Jun 2020 20:52:56 -0400
Subject: [PATCH 390/586] Implemented Archaeomender

---
 Mage.Sets/src/mage/cards/a/Archaeomender.java | 47 +++++++++++++++++++
 Mage.Sets/src/mage/sets/Jumpstart.java        |  1 +
 2 files changed, 48 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/a/Archaeomender.java

diff --git a/Mage.Sets/src/mage/cards/a/Archaeomender.java b/Mage.Sets/src/mage/cards/a/Archaeomender.java
new file mode 100644
index 0000000000..a96b186963
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/a/Archaeomender.java
@@ -0,0 +1,47 @@
+package mage.cards.a;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.filter.FilterCard;
+import mage.filter.common.FilterArtifactOrEnchantmentCard;
+import mage.target.common.TargetCardInYourGraveyard;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class Archaeomender extends CardImpl {
+
+    private static final FilterCard filter
+            = new FilterArtifactOrEnchantmentCard("artifact card from your graveyard");
+
+    public Archaeomender(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}");
+
+        this.subtype.add(SubType.HUMAN);
+        this.subtype.add(SubType.WIZARD);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(3);
+
+        // When Archaeomender enters the battlefield, return target artifact card from your graveyard to your hand.
+        Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect());
+        ability.addTarget(new TargetCardInYourGraveyard(filter));
+        this.addAbility(ability);
+    }
+
+    private Archaeomender(final Archaeomender card) {
+        super(card);
+    }
+
+    @Override
+    public Archaeomender copy() {
+        return new Archaeomender(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index 5566783192..56fe47f0e3 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -29,6 +29,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Ambassador Oak", 375, Rarity.COMMON, mage.cards.a.AmbassadorOak.class));
         cards.add(new SetCardInfo("Ancestral Statue", 458, Rarity.COMMON, mage.cards.a.AncestralStatue.class));
         cards.add(new SetCardInfo("Arcane Encyclopedia", 459, Rarity.UNCOMMON, mage.cards.a.ArcaneEncyclopedia.class));
+        cards.add(new SetCardInfo("Archaeomender", 9, Rarity.COMMON, mage.cards.a.Archaeomender.class));
         cards.add(new SetCardInfo("Ashmouth Hound", 290, Rarity.COMMON, mage.cards.a.AshmouthHound.class));
         cards.add(new SetCardInfo("Assassin's Strike", 200, Rarity.UNCOMMON, mage.cards.a.AssassinsStrike.class));
         cards.add(new SetCardInfo("Assault Formation", 378, Rarity.RARE, mage.cards.a.AssaultFormation.class));

From 3ef78ff783ad550b2fa57388fb1f8b4ebc192e9a Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 18 Jun 2020 21:02:38 -0400
Subject: [PATCH 391/586] Implemented Witch of the Moors

---
 .../src/mage/cards/w/WitchOfTheMoors.java     | 66 +++++++++++++++++++
 Mage.Sets/src/mage/sets/Jumpstart.java        |  1 +
 2 files changed, 67 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/w/WitchOfTheMoors.java

diff --git a/Mage.Sets/src/mage/cards/w/WitchOfTheMoors.java b/Mage.Sets/src/mage/cards/w/WitchOfTheMoors.java
new file mode 100644
index 0000000000..0659eaf221
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/w/WitchOfTheMoors.java
@@ -0,0 +1,66 @@
+package mage.cards.w;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.BeginningOfEndStepTriggeredAbility;
+import mage.abilities.condition.Condition;
+import mage.abilities.condition.common.YouGainedLifeCondition;
+import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
+import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect;
+import mage.abilities.effects.common.SacrificeOpponentsEffect;
+import mage.abilities.keyword.DeathtouchAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.ComparisonType;
+import mage.constants.SubType;
+import mage.constants.TargetController;
+import mage.filter.StaticFilters;
+import mage.target.common.TargetCardInYourGraveyard;
+import mage.watchers.common.PlayerGainedLifeWatcher;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class WitchOfTheMoors extends CardImpl {
+
+    private static final Condition condition = new YouGainedLifeCondition(ComparisonType.MORE_THAN, 0);
+
+    public WitchOfTheMoors(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}");
+
+        this.subtype.add(SubType.HUMAN);
+        this.subtype.add(SubType.WARLOCK);
+        this.power = new MageInt(4);
+        this.toughness = new MageInt(4);
+
+        // Deathtouch
+        this.addAbility(DeathtouchAbility.getInstance());
+
+        // At the beginning of your end step, if you gained life this turn, each opponent sacrifices a creature and you return up to one target creature card from your graveyard to your hand.
+        Ability ability = new ConditionalInterveningIfTriggeredAbility(
+                new BeginningOfEndStepTriggeredAbility(new SacrificeOpponentsEffect(
+                        StaticFilters.FILTER_PERMANENT_A_CREATURE
+                ), TargetController.YOU, false),
+                condition, "At the beginning of your end step, if you gained life this turn, " +
+                "each opponent sacrifices a creature and you return up to one target creature card " +
+                "from your graveyard to your hand"
+        );
+        ability.addEffect(new ReturnFromGraveyardToHandTargetEffect());
+        ability.addTarget(new TargetCardInYourGraveyard(
+                0, 1, StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD
+        ));
+        this.addAbility(ability, new PlayerGainedLifeWatcher());
+    }
+
+    private WitchOfTheMoors(final WitchOfTheMoors card) {
+        super(card);
+    }
+
+    @Override
+    public WitchOfTheMoors copy() {
+        return new WitchOfTheMoors(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index 56fe47f0e3..2cec2f0687 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -239,6 +239,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Wight of Precinct Six", 287, Rarity.COMMON, mage.cards.w.WightOfPrecinctSix.class));
         cards.add(new SetCardInfo("Wildheart Invoker", 444, Rarity.COMMON, mage.cards.w.WildheartInvoker.class));
         cards.add(new SetCardInfo("Winged Words", 196, Rarity.COMMON, mage.cards.w.WingedWords.class));
+        cards.add(new SetCardInfo("Witch of the Moors", 18, Rarity.RARE, mage.cards.w.WitchOfTheMoors.class));
         cards.add(new SetCardInfo("Wizard's Retort", 198, Rarity.UNCOMMON, mage.cards.w.WizardsRetort.class));
         cards.add(new SetCardInfo("Woodborn Behemoth", 446, Rarity.UNCOMMON, mage.cards.w.WoodbornBehemoth.class));
         cards.add(new SetCardInfo("Wren's Run Vanquisher", 447, Rarity.UNCOMMON, mage.cards.w.WrensRunVanquisher.class));

From 57ed834c146e421b50c6906e614a02bca6edf9cf Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 18 Jun 2020 21:10:40 -0400
Subject: [PATCH 392/586] Implemented Ormos, Archive Keeper

---
 .../src/mage/cards/o/OrmosArchiveKeeper.java  | 144 ++++++++++++++++++
 Mage.Sets/src/mage/sets/Jumpstart.java        |   1 +
 2 files changed, 145 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/o/OrmosArchiveKeeper.java

diff --git a/Mage.Sets/src/mage/cards/o/OrmosArchiveKeeper.java b/Mage.Sets/src/mage/cards/o/OrmosArchiveKeeper.java
new file mode 100644
index 0000000000..f8bb8d77de
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/o/OrmosArchiveKeeper.java
@@ -0,0 +1,144 @@
+package mage.cards.o;
+
+import mage.MageInt;
+import mage.MageObject;
+import mage.abilities.Ability;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.costs.common.DiscardTargetCost;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.effects.ReplacementEffectImpl;
+import mage.abilities.effects.common.DrawCardSourceControllerEffect;
+import mage.abilities.keyword.FlyingAbility;
+import mage.cards.Card;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.*;
+import mage.counters.CounterType;
+import mage.filter.FilterCard;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+import mage.game.permanent.Permanent;
+import mage.players.Player;
+import mage.target.common.TargetCardInHand;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class OrmosArchiveKeeper extends CardImpl {
+
+    public OrmosArchiveKeeper(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}{U}");
+
+        this.addSuperType(SuperType.LEGENDARY);
+        this.subtype.add(SubType.SPHINX);
+        this.power = new MageInt(5);
+        this.toughness = new MageInt(5);
+
+        // Flying
+        this.addAbility(FlyingAbility.getInstance());
+
+        // If you would draw a card while your library has no cards in it, instead put five +1/+1 counters on Ormos, Archive Keeper.
+        this.addAbility(new SimpleStaticAbility(new OrmosArchiveKeeperEffect()));
+
+        // {1}{U}{U}, Discard three cards with different names: Draw five cards.
+        Ability ability = new SimpleActivatedAbility(
+                new DrawCardSourceControllerEffect(5), new ManaCostsImpl("{1}{U}{U}")
+        );
+        ability.addCost(new DiscardTargetCost(new OrmosArchiveKeeperTarget()));
+        this.addAbility(ability);
+    }
+
+    private OrmosArchiveKeeper(final OrmosArchiveKeeper card) {
+        super(card);
+    }
+
+    @Override
+    public OrmosArchiveKeeper copy() {
+        return new OrmosArchiveKeeper(this);
+    }
+}
+
+class OrmosArchiveKeeperEffect extends ReplacementEffectImpl {
+
+    OrmosArchiveKeeperEffect() {
+        super(Duration.WhileOnBattlefield, Outcome.Benefit);
+        staticText = "If you would draw a card while your library has no cards in it, " +
+                "instead put five +1/+1 counters on {this}";
+    }
+
+    private OrmosArchiveKeeperEffect(final OrmosArchiveKeeperEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public OrmosArchiveKeeperEffect copy() {
+        return new OrmosArchiveKeeperEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        return true;
+    }
+
+    @Override
+    public boolean replaceEvent(GameEvent event, Ability source, Game game) {
+        Permanent permanent = game.getPermanent(source.getSourceId());
+        if (permanent == null) {
+            permanent.addCounters(CounterType.P1P1.createInstance(5), source, game);
+        }
+        return true;
+    }
+
+    @Override
+    public boolean checksEventType(GameEvent event, Game game) {
+        return event.getType() == GameEvent.EventType.DRAW_CARD;
+    }
+
+    @Override
+    public boolean applies(GameEvent event, Ability source, Game game) {
+        if (event.getPlayerId().equals(source.getControllerId())) {
+            Player player = game.getPlayer(event.getPlayerId());
+            if (player != null && !player.hasLost() && !player.getLibrary().hasCards()) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
+
+class OrmosArchiveKeeperTarget extends TargetCardInHand {
+
+    private static final FilterCard filter = new FilterCard("cards with different names");
+
+    OrmosArchiveKeeperTarget() {
+        super(3, filter);
+    }
+
+    private OrmosArchiveKeeperTarget(final OrmosArchiveKeeperTarget target) {
+        super(target);
+    }
+
+    @Override
+    public OrmosArchiveKeeperTarget copy() {
+        return new OrmosArchiveKeeperTarget(this);
+    }
+
+    @Override
+    public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
+        if (!super.canTarget(controllerId, id, source, game)) {
+            return false;
+        }
+        Card card = game.getCard(id);
+        if (card == null) {
+            return false;
+        }
+        return this.getTargets()
+                .stream()
+                .map(game::getCard)
+                .map(MageObject::getName)
+                .noneMatch(card.getName()::equals);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index 2cec2f0687..95455a6afe 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -156,6 +156,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Oneirophage", 162, Rarity.UNCOMMON, mage.cards.o.Oneirophage.class));
         cards.add(new SetCardInfo("Oracle of Mul Daya", 415, Rarity.RARE, mage.cards.o.OracleOfMulDaya.class));
         cards.add(new SetCardInfo("Orazca Frillback", 416, Rarity.COMMON, mage.cards.o.OrazcaFrillback.class));
+        cards.add(new SetCardInfo("Ormos, Archive Keeper", 13, Rarity.RARE, mage.cards.o.OrmosArchiveKeeper.class));
         cards.add(new SetCardInfo("Overgrown Battlement", 417, Rarity.UNCOMMON, mage.cards.o.OvergrownBattlement.class));
         cards.add(new SetCardInfo("Pacifism", 125, Rarity.COMMON, mage.cards.p.Pacifism.class));
         cards.add(new SetCardInfo("Peel from Reality", 163, Rarity.COMMON, mage.cards.p.PeelFromReality.class));

From 1695e6767da013d144ba50a85dc9819d1bd6226c Mon Sep 17 00:00:00 2001
From: John Hitchings <hitch17@gmail.com>
Date: Thu, 18 Jun 2020 23:45:02 -0700
Subject: [PATCH 393/586] add jumpstart swiss and elimiation tournament
 formats.

---
 .../client/dialog/NewTournamentDialog.java    |  1 +
 .../java/mage/view/TournamentTypeView.java    |  7 ++
 .../JumpstartEliminationTournament.java       | 44 ++++++++++
 .../JumpstartEliminationTournamentType.java   | 19 +++++
 .../tournament/JumpstartSwissTournament.java  | 44 ++++++++++
 .../JumpstartSwissTournamentType.java         | 18 ++++
 Mage.Server/config/config.xml                 |  2 +
 .../jumpstart/JumpstartPoolGenerator.java     | 84 +++++++++++++++++++
 .../java/mage/game/jumpstart/jumpstart.txt    | 15 ++++
 .../mage/game/tournament/LimitedOptions.java  | 12 ++-
 .../mage/game/tournament/TournamentImpl.java  | 24 +++++-
 .../mage/game/tournament/TournamentType.java  |  7 +-
 12 files changed, 272 insertions(+), 5 deletions(-)
 create mode 100644 Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartEliminationTournament.java
 create mode 100644 Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartEliminationTournamentType.java
 create mode 100644 Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartSwissTournament.java
 create mode 100644 Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartSwissTournamentType.java
 create mode 100644 Mage/src/main/java/mage/game/jumpstart/JumpstartPoolGenerator.java
 create mode 100644 Mage/src/main/java/mage/game/jumpstart/jumpstart.txt

diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java b/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java
index 648a13651a..2e4b980870 100644
--- a/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java
+++ b/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java
@@ -1203,6 +1203,7 @@ public class NewTournamentDialog extends MageDialog {
             tOptions.getLimitedOptions().setConstructionTime((Integer) this.spnConstructTime.getValue() * 60);
             tOptions.getLimitedOptions().setIsRandom(tournamentType.isRandom());
             tOptions.getLimitedOptions().setIsRichMan(tournamentType.isRichMan());
+            tOptions.getLimitedOptions().setIsJumpstart(tournamentType.isJumpstart());
             if (tournamentType.isCubeBooster()) {
                 tOptions.getLimitedOptions().setDraftCubeName(this.cbDraftCube.getSelectedItem().toString());
                 if (!(cubeFromDeckFilename.isEmpty())) {
diff --git a/Mage.Common/src/main/java/mage/view/TournamentTypeView.java b/Mage.Common/src/main/java/mage/view/TournamentTypeView.java
index 8bd4d8c3cc..22d7ecf9bc 100644
--- a/Mage.Common/src/main/java/mage/view/TournamentTypeView.java
+++ b/Mage.Common/src/main/java/mage/view/TournamentTypeView.java
@@ -22,6 +22,7 @@ public class TournamentTypeView implements Serializable {
     private final boolean elimination;
     private final boolean random;
     private final boolean richMan;
+    private final boolean jumpstart;
 
     public TournamentTypeView(TournamentType tournamentType) {
         this.name = tournamentType.getName();
@@ -34,6 +35,7 @@ public class TournamentTypeView implements Serializable {
         this.elimination = tournamentType.isElimination();
         this.random = tournamentType.isRandom();
         this.richMan = tournamentType.isRichMan();
+        this.jumpstart = tournamentType.isJumpstart();
     }
 
     @Override
@@ -80,4 +82,9 @@ public class TournamentTypeView implements Serializable {
     public boolean isRichMan() {
         return richMan;
     }
+
+    public boolean isJumpstart() {
+        return jumpstart;
+    }
+
 } 
diff --git a/Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartEliminationTournament.java b/Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartEliminationTournament.java
new file mode 100644
index 0000000000..d65634936a
--- /dev/null
+++ b/Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartEliminationTournament.java
@@ -0,0 +1,44 @@
+
+
+package mage.tournament;
+
+import mage.game.tournament.TournamentOptions;
+import mage.game.tournament.TournamentSingleElimination;
+
+public class JumpstartEliminationTournament extends TournamentSingleElimination {
+
+    protected enum TournamentStep {
+        START, OPEN_BOOSTERS, CONSTRUCT, COMPETE, WINNERS
+    }
+
+    protected TournamentStep currentStep;
+
+    public JumpstartEliminationTournament(TournamentOptions options) {
+        super(options);
+        currentStep = TournamentStep.START;
+    }
+
+    @Override
+    public void nextStep() {
+        switch (currentStep) {
+            case START:
+                currentStep = TournamentStep.OPEN_BOOSTERS;
+                openBoosters();
+                break;
+            case OPEN_BOOSTERS:
+                currentStep = TournamentStep.CONSTRUCT;
+                construct();
+                break;
+            case CONSTRUCT:
+                currentStep = TournamentStep.COMPETE;
+                runTournament();
+                break;
+            case COMPETE:
+                currentStep = TournamentStep.WINNERS;
+                winners();
+                end();
+                break;
+        }
+    }
+
+}
diff --git a/Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartEliminationTournamentType.java b/Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartEliminationTournamentType.java
new file mode 100644
index 0000000000..858588e441
--- /dev/null
+++ b/Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartEliminationTournamentType.java
@@ -0,0 +1,19 @@
+
+
+package mage.tournament;
+
+import mage.game.tournament.TournamentType;
+
+public class JumpstartEliminationTournamentType extends TournamentType {
+
+    public JumpstartEliminationTournamentType() {
+        this.name = "Jumpstart Elimination";
+        this.maxPlayers = 16;
+        this.minPlayers = 2;
+        this.numBoosters = 0;
+        this.isJumpstart = true;
+        this.elimination = true;
+        this.limited = true;
+    }
+
+}
diff --git a/Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartSwissTournament.java b/Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartSwissTournament.java
new file mode 100644
index 0000000000..f0e7f34bc9
--- /dev/null
+++ b/Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartSwissTournament.java
@@ -0,0 +1,44 @@
+
+
+package mage.tournament;
+
+import mage.game.tournament.TournamentOptions;
+import mage.game.tournament.TournamentSwiss;
+
+public class JumpstartSwissTournament extends TournamentSwiss {
+
+    protected enum TournamentStep {
+        START, OPEN_BOOSTERS, CONSTRUCT, COMPETE, WINNERS
+    }
+
+    protected TournamentStep currentStep;
+
+    public JumpstartSwissTournament(TournamentOptions options) {
+        super(options);
+        currentStep = TournamentStep.START;
+    }
+
+    @Override
+    public void nextStep() {
+        switch (currentStep) {
+            case START:
+                currentStep = TournamentStep.OPEN_BOOSTERS;
+                openBoosters();
+                break;
+            case OPEN_BOOSTERS:
+                currentStep = TournamentStep.CONSTRUCT;
+                construct();
+                break;
+            case CONSTRUCT:
+                currentStep = TournamentStep.COMPETE;
+                runTournament();
+                break;
+            case COMPETE:
+                currentStep = TournamentStep.WINNERS;
+                winners();
+                end();
+                break;
+        }
+    }
+
+}
diff --git a/Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartSwissTournamentType.java b/Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartSwissTournamentType.java
new file mode 100644
index 0000000000..bf5328ed52
--- /dev/null
+++ b/Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartSwissTournamentType.java
@@ -0,0 +1,18 @@
+
+
+package mage.tournament;
+
+import mage.game.tournament.TournamentType;
+
+public class JumpstartSwissTournamentType extends TournamentType {
+
+    public JumpstartSwissTournamentType() {
+        this.name = "Jumpstart Swiss";
+        this.maxPlayers = 16;
+        this.minPlayers = 2;
+        this.numBoosters = 0;
+        this.isJumpstart = true;
+        this.limited = true;
+    }
+
+}
diff --git a/Mage.Server/config/config.xml b/Mage.Server/config/config.xml
index 28ffc64abf..6ee2e09546 100644
--- a/Mage.Server/config/config.xml
+++ b/Mage.Server/config/config.xml
@@ -104,6 +104,8 @@
         <tournamentType name="Sealed Elimination (Cube)" jar="mage-tournament-sealed.jar" className="mage.tournament.SealedEliminationTournament" typeName="mage.tournament.SealedEliminationCubeTournamentType"/>
         <tournamentType name="Sealed Swiss" jar="mage-tournament-sealed.jar" className="mage.tournament.SealedSwissTournament" typeName="mage.tournament.SealedSwissTournamentType"/>
         <tournamentType name="Sealed Swiss (Cube)" jar="mage-tournament-sealed.jar" className="mage.tournament.SealedSwissTournament" typeName="mage.tournament.SealedSwissCubeTournamentType"/>
+        <tournamentType name="Jumpstart Elimination" jar="mage-tournament-sealed.jar" className="mage.tournament.JumpstartEliminationTournament" typeName="mage.tournament.JumpstartEliminationTournamentType"/>
+        <tournamentType name="Jumpstart Swiss" jar="mage-tournament-sealed.jar" className="mage.tournament.JumpstartSwissTournament" typeName="mage.tournament.JumpstartSwissTournamentType"/>
     </tournamentTypes>
     <draftCubes>
         <draftCube name="Adam Styborski's Pauper Cube" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.AdamStyborskisPauperCube"/>
diff --git a/Mage/src/main/java/mage/game/jumpstart/JumpstartPoolGenerator.java b/Mage/src/main/java/mage/game/jumpstart/JumpstartPoolGenerator.java
new file mode 100644
index 0000000000..476f2b7337
--- /dev/null
+++ b/Mage/src/main/java/mage/game/jumpstart/JumpstartPoolGenerator.java
@@ -0,0 +1,84 @@
+package mage.game.jumpstart;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import com.google.common.base.Charsets;
+import com.google.common.io.CharSource;
+import com.google.common.io.Files;
+import com.google.common.io.Resources;
+
+import mage.cards.Card;
+import mage.cards.decks.Deck;
+import mage.cards.decks.DeckCardInfo;
+import mage.cards.decks.DeckCardLists;
+import mage.game.GameException;
+
+public class JumpstartPoolGenerator {
+
+  private static final String RESOURCE_NAME = "mage/game/jumpstart/jumpstart.txt";
+  private static final List<JumpstartPack> JUMPSTART_PACKS;
+
+  static {
+    try {
+      CharSource source = Resources.asCharSource(Resources.getResource(RESOURCE_NAME), Charsets.UTF_8);
+      List<JumpstartPack> packs = new ArrayList<>();
+      JumpstartPack pack = new JumpstartPack();
+      for (String line : source.readLines()) {
+        if (line.isEmpty()) {
+          if (!pack.isEmpty()) {
+            packs.add(pack);
+            pack = new JumpstartPack();
+          }
+        } else if (line.startsWith("#")) {
+          // skip comment
+        } else {
+          String[] ls = line.split(" ", 3);
+          pack.add(new DeckCardInfo(ls[2], ls[1], ls[0]));
+        }
+      }
+      JUMPSTART_PACKS = Collections.unmodifiableList(packs);
+    } catch (IOException e) {
+      throw new UncheckedIOException(e);
+    }
+  }
+
+  public static Set<Card> generatePool() {
+    try {
+      DeckCardLists list = new DeckCardLists();
+      SecureRandom random = new SecureRandom();
+      for (int i = 0; i < 2; i++) {
+        int index = random.nextInt(JUMPSTART_PACKS.size());
+        list.getCards().addAll(JUMPSTART_PACKS.get(index).getCards());
+      }
+      return Deck.load(list).getCards();
+    } catch (GameException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  public static class JumpstartPack {
+
+    private final List<DeckCardInfo> cards = new ArrayList<>();
+
+    public void add(DeckCardInfo card) {
+      cards.add(card);
+    }
+
+    public boolean isEmpty() {
+      return cards.isEmpty();
+    }
+
+    public List<DeckCardInfo> getCards() {
+      return Collections.unmodifiableList(cards);
+    }
+
+  }
+
+}
diff --git a/Mage/src/main/java/mage/game/jumpstart/jumpstart.txt b/Mage/src/main/java/mage/game/jumpstart/jumpstart.txt
new file mode 100644
index 0000000000..cd0f7fad66
--- /dev/null
+++ b/Mage/src/main/java/mage/game/jumpstart/jumpstart.txt
@@ -0,0 +1,15 @@
+# setCode cardNum cardName
+IKO 105 Whisper Squad
+IKO 105 Whisper Squad
+IKO 105 Whisper Squad
+IKO 105 Whisper Squad
+
+IKO 113 Drannith Stinger
+IKO 113 Drannith Stinger
+IKO 113 Drannith Stinger
+IKO 113 Drannith Stinger
+
+IKO 149 Essence Symbiote
+IKO 149 Essence Symbiote
+IKO 149 Essence Symbiote
+IKO 149 Essence Symbiote
diff --git a/Mage/src/main/java/mage/game/tournament/LimitedOptions.java b/Mage/src/main/java/mage/game/tournament/LimitedOptions.java
index 0151beed80..9628b5c33a 100644
--- a/Mage/src/main/java/mage/game/tournament/LimitedOptions.java
+++ b/Mage/src/main/java/mage/game/tournament/LimitedOptions.java
@@ -19,7 +19,8 @@ public class LimitedOptions implements Serializable {
     protected int numberBoosters;
     protected boolean isRandom;
     protected boolean isRichMan;
-    protected Deck cubeFromDeck = null;
+    protected Deck cubeFromDeck;
+    protected boolean isJumpstart;
 
     public List<String> getSetCodes() {
         return sets;
@@ -80,4 +81,13 @@ public class LimitedOptions implements Serializable {
     public void setIsRichMan(boolean isRichMan) {
         this.isRichMan = isRichMan;
     }
+
+    public void setIsJumpstart(boolean isJumpstart) {
+        this.isJumpstart = isJumpstart;
+    }
+
+    public boolean getIsJumpstart() {
+        return this.isJumpstart;
+    }
+
 }
diff --git a/Mage/src/main/java/mage/game/tournament/TournamentImpl.java b/Mage/src/main/java/mage/game/tournament/TournamentImpl.java
index 016ca73dd6..cd3729b844 100644
--- a/Mage/src/main/java/mage/game/tournament/TournamentImpl.java
+++ b/Mage/src/main/java/mage/game/tournament/TournamentImpl.java
@@ -1,15 +1,32 @@
 
 package mage.game.tournament;
 
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
 import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.apache.log4j.Logger;
+
 import mage.cards.ExpansionSet;
 import mage.cards.decks.Deck;
 import mage.constants.TournamentPlayerState;
 import mage.game.draft.Draft;
 import mage.game.draft.DraftCube;
-import mage.game.events.*;
+import mage.game.events.Listener;
+import mage.game.events.PlayerQueryEvent;
+import mage.game.events.PlayerQueryEventSource;
+import mage.game.events.TableEvent;
 import mage.game.events.TableEvent.EventType;
+import mage.game.events.TableEventSource;
+import mage.game.jumpstart.JumpstartPoolGenerator;
 import mage.game.match.Match;
 import mage.game.match.MatchPlayer;
 import mage.game.result.ResultProtos.MatchPlayerProto;
@@ -20,7 +37,6 @@ import mage.game.result.ResultProtos.TourneyRoundProto;
 import mage.players.Player;
 import mage.players.PlayerType;
 import mage.util.RandomUtil;
-import org.apache.log4j.Logger;
 
 /**
  *
@@ -394,6 +410,8 @@ public abstract class TournamentImpl implements Tournament {
                 for (int i = 0; i < options.getLimitedOptions().getNumberBoosters(); i++) {
                     player.getDeck().getSideboard().addAll(cube.createBooster());
                 }
+            } else if (options.getLimitedOptions().getIsJumpstart()) {
+                player.getDeck().getSideboard().addAll(JumpstartPoolGenerator.generatePool());
             } else {
                 for (ExpansionSet set : sets) {
                     player.getDeck().getSideboard().addAll(set.createBooster());
diff --git a/Mage/src/main/java/mage/game/tournament/TournamentType.java b/Mage/src/main/java/mage/game/tournament/TournamentType.java
index 5b227ed3d1..f11bd7e38e 100644
--- a/Mage/src/main/java/mage/game/tournament/TournamentType.java
+++ b/Mage/src/main/java/mage/game/tournament/TournamentType.java
@@ -18,7 +18,8 @@ public class TournamentType implements Serializable {
     protected boolean limited;      // or construced
     protected boolean elimination;  // or Swiss
     protected boolean isRandom;
-    protected boolean isRichMan = false;    // or Rich Man Draft
+    protected boolean isRichMan;    // or Rich Man Draft
+    protected boolean isJumpstart;
 
     protected TournamentType() {
     }
@@ -68,4 +69,8 @@ public class TournamentType implements Serializable {
         return this.isRichMan;
     }
 
+    public boolean isJumpstart() {
+        return this.isJumpstart;
+    }
+
 } 

From 1bd98d64357cbc64cda208940a493b087601f371 Mon Sep 17 00:00:00 2001
From: John Hitchings <hitch17@gmail.com>
Date: Fri, 19 Jun 2020 00:04:09 -0700
Subject: [PATCH 394/586] move jumpstart file to resources

---
 .../jumpstart/JumpstartPoolGenerator.java     |  4 +-
 .../java/mage/game/jumpstart/jumpstart.txt    | 15 ------
 .../main/resources/jumpstart/jumpstart.txt    | 50 +++++++++++++++++++
 3 files changed, 51 insertions(+), 18 deletions(-)
 delete mode 100644 Mage/src/main/java/mage/game/jumpstart/jumpstart.txt
 create mode 100644 Mage/src/main/resources/jumpstart/jumpstart.txt

diff --git a/Mage/src/main/java/mage/game/jumpstart/JumpstartPoolGenerator.java b/Mage/src/main/java/mage/game/jumpstart/JumpstartPoolGenerator.java
index 476f2b7337..b9425d2c3a 100644
--- a/Mage/src/main/java/mage/game/jumpstart/JumpstartPoolGenerator.java
+++ b/Mage/src/main/java/mage/game/jumpstart/JumpstartPoolGenerator.java
@@ -1,6 +1,5 @@
 package mage.game.jumpstart;
 
-import java.io.File;
 import java.io.IOException;
 import java.io.UncheckedIOException;
 import java.security.SecureRandom;
@@ -11,7 +10,6 @@ import java.util.Set;
 
 import com.google.common.base.Charsets;
 import com.google.common.io.CharSource;
-import com.google.common.io.Files;
 import com.google.common.io.Resources;
 
 import mage.cards.Card;
@@ -22,7 +20,7 @@ import mage.game.GameException;
 
 public class JumpstartPoolGenerator {
 
-  private static final String RESOURCE_NAME = "mage/game/jumpstart/jumpstart.txt";
+  private static final String RESOURCE_NAME = "jumpstart/jumpstart.txt";
   private static final List<JumpstartPack> JUMPSTART_PACKS;
 
   static {
diff --git a/Mage/src/main/java/mage/game/jumpstart/jumpstart.txt b/Mage/src/main/java/mage/game/jumpstart/jumpstart.txt
deleted file mode 100644
index cd0f7fad66..0000000000
--- a/Mage/src/main/java/mage/game/jumpstart/jumpstart.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-# setCode cardNum cardName
-IKO 105 Whisper Squad
-IKO 105 Whisper Squad
-IKO 105 Whisper Squad
-IKO 105 Whisper Squad
-
-IKO 113 Drannith Stinger
-IKO 113 Drannith Stinger
-IKO 113 Drannith Stinger
-IKO 113 Drannith Stinger
-
-IKO 149 Essence Symbiote
-IKO 149 Essence Symbiote
-IKO 149 Essence Symbiote
-IKO 149 Essence Symbiote
diff --git a/Mage/src/main/resources/jumpstart/jumpstart.txt b/Mage/src/main/resources/jumpstart/jumpstart.txt
new file mode 100644
index 0000000000..f030253772
--- /dev/null
+++ b/Mage/src/main/resources/jumpstart/jumpstart.txt
@@ -0,0 +1,50 @@
+# setCode cardNum cardName
+# Red Cards
+ELD 137 Rimrock Knight
+ELD 137 Rimrock Knight
+ELD 137 Rimrock Knight
+ELD 137 Rimrock Knight
+ELD 291 Bonecrusher Giant
+ELD 291 Bonecrusher Giant
+ELD 291 Bonecrusher Giant
+ELD 291 Bonecrusher Giant
+ELD 120 Embercleave
+ELD 120 Embercleave
+ELD 120 Embercleave
+ELD 120 Embercleave
+
+# Green Cards
+GRN 141 Pelt Collector
+GRN 141 Pelt Collector
+GRN 141 Pelt Collector
+GRN 141 Pelt Collector
+WAR 171 Paradise Druid
+WAR 171 Paradise Druid
+WAR 171 Paradise Druid
+WAR 171 Paradise Druid
+ELD 165 Lovestruck Beast
+ELD 165 Lovestruck Beast
+ELD 165 Lovestruck Beast
+ELD 165 Lovestruck Beast
+ELD 171 Questing Beast
+ELD 171 Questing Beast
+ELD 171 Questing Beast
+ELD 171 Questing Beast
+
+# Black Cards
+THB 123 Woe Strider
+THB 123 Woe Strider
+THB 123 Woe Strider
+THB 123 Woe Strider
+ELD 81 Cauldron Familiar
+ELD 81 Cauldron Familiar
+ELD 81 Cauldron Familiar
+ELD 81 Cauldron Familiar
+ELD 237 Witch's Oven
+ELD 237 Witch's Oven
+ELD 237 Witch's Oven
+ELD 237 Witch's Oven
+IKO 220 Fiend Artisan
+IKO 220 Fiend Artisan
+IKO 220 Fiend Artisan
+IKO 220 Fiend Artisan
\ No newline at end of file

From fc5d766ebdf9a4d780a87f5cda046b2d3096d045 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Fri, 19 Jun 2020 12:43:26 +0400
Subject: [PATCH 395/586] * AI: improved exile from graveyard cost (now AI can
 use it with more efficiency)

---
 .../src/main/java/mage/player/ai/ComputerPlayer.java        | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java
index a71a8432fb..bdf8fd13b4 100644
--- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java
+++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java
@@ -396,12 +396,16 @@ public class ComputerPlayer extends PlayerImpl implements Player {
                 }
             }
 
-            while ((outcome.isGood() ? target.getTargets().size() < target.getMaxNumberOfTargets() : !target.isChosen())
+            // exile cost workaround: exile is bad, but exile from graveyard in most cases is good (more exiled -- more good things you get, e.g. delve's pay)
+            boolean isRealGood = outcome.isGood() || outcome == Outcome.Exile;
+            while ((isRealGood ? target.getTargets().size() < target.getMaxNumberOfTargets() : !target.isChosen())
                     && !cards.isEmpty()) {
                 Card pick = pickTarget(abilityControllerId, cards, outcome, target, null, game);
                 if (pick != null) {
                     target.addTarget(pick.getId(), null, game);
                     cards.remove(pick);
+                } else {
+                    break;
                 }
             }
 

From f6f5aa04730db0e7284ce5af63463951b1c87d5f Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Fri, 19 Jun 2020 10:54:11 +0200
Subject: [PATCH 396/586] * Fixed broken rarity sorting in card grid (fixes
 #6508).

---
 .../java/mage/client/util/CardViewRarityComparator.java   | 8 +-------
 1 file changed, 1 insertion(+), 7 deletions(-)

diff --git a/Mage.Client/src/main/java/mage/client/util/CardViewRarityComparator.java b/Mage.Client/src/main/java/mage/client/util/CardViewRarityComparator.java
index 82f0edeb50..77bd9ff23f 100644
--- a/Mage.Client/src/main/java/mage/client/util/CardViewRarityComparator.java
+++ b/Mage.Client/src/main/java/mage/client/util/CardViewRarityComparator.java
@@ -15,16 +15,10 @@ public class CardViewRarityComparator implements Comparator<CardView> {
         Rarity r1 = o1.getRarity();
         Rarity r2 = o2.getRarity();
 
-        int val = Integer.compare(
+        return Integer.compare(
                 r1 == null ? 0 : r1.getSorting(),
                 r2 == null ? 0 : r2.getSorting()
         );
-
-        if (val == 0) {
-            return o1.getName().compareTo(o2.getName());
-        } else {
-            return val;
-        }
     }
 
 }
\ No newline at end of file

From bdaf6454def5e832e1db0102e220d4051091eb83 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Fri, 19 Jun 2020 12:54:42 +0400
Subject: [PATCH 397/586] Added commander's zone info in leave/keep message

---
 Mage/src/main/java/mage/game/GameImpl.java | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java
index f32b53881b..a7cdb25fdd 100644
--- a/Mage/src/main/java/mage/game/GameImpl.java
+++ b/Mage/src/main/java/mage/game/GameImpl.java
@@ -1875,7 +1875,11 @@ public abstract class GameImpl implements Game, Serializable {
                     .forEach(commanders::add);
             commanders.removeIf(card -> state.checkCommanderShouldStay(card, this));
             for (Card card : commanders) {
-                if (player.chooseUse(Outcome.Benefit, "Move " + card.getIdName() + " to the command zone or leave it in its current zone?", "You can only make this choice once", "Move to command", "Leave in current zone", null, this)) {
+                Zone currentZone = this.getState().getZone(card.getId());
+                String currentZoneInfo = (currentZone == null ? "(error)" : "(" + currentZone.name() + ")");
+                if (player.chooseUse(Outcome.Benefit, "Move " + card.getIdName()
+                                + " to the command zone or leave it in current zone " + currentZoneInfo + "?", "You can only make this choice once per object",
+                        "Move to command", "Leave in current zone " + currentZoneInfo, null, this)) {
                     toMove.add(card);
                 } else {
                     state.setCommanderShouldStay(card, this);

From c2e7b02e13d4f4a39abf773f351db0f36285ee9a Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Fri, 19 Jun 2020 13:09:45 +0400
Subject: [PATCH 398/586] Reworked and improved special mana payment abilities
 (convoke, delve, assist, improvise): * now it can be used to calc and find
 available mana and playable abilities; * now tests and AI can use that
 abilities; * now it follows mtg's rules and restrictions for mana activation
 order (rule 601.2f, see #768);

---
 .../src/mage/player/human/HumanPlayer.java    |  89 ++++++----
 .../asthough/PlayFromNonHandZoneTest.java     | 115 +++++++-----
 .../java/mage/abilities/SpecialAction.java    |  50 ++++--
 .../costs/mana/ActivationManaAbilityStep.java |  25 +++
 .../mana/AlternateManaPaymentAbility.java     |  24 ++-
 .../effects/ContinuousEffectImpl.java         |   6 +-
 .../mana/ActivatedManaAbilityImpl.java        |  20 ++-
 Mage/src/main/java/mage/game/stack/Spell.java |  23 ++-
 .../main/java/mage/players/PlayerImpl.java    | 166 ++++++++++--------
 9 files changed, 341 insertions(+), 177 deletions(-)
 create mode 100644 Mage/src/main/java/mage/abilities/costs/mana/ActivationManaAbilityStep.java

diff --git a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java
index feb685d60c..e38c162e8d 100644
--- a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java
+++ b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java
@@ -10,6 +10,7 @@ import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.RequirementEffect;
 import mage.abilities.hint.HintUtils;
 import mage.abilities.mana.ActivatedManaAbilityImpl;
+import mage.abilities.mana.ManaAbility;
 import mage.cards.Card;
 import mage.cards.Cards;
 import mage.cards.decks.Deck;
@@ -61,6 +62,8 @@ import static mage.constants.PlayerAction.TRIGGER_AUTO_ORDER_RESET_ALL;
  */
 public class HumanPlayer extends PlayerImpl {
 
+    private static final boolean ALLOW_USERS_TO_PUT_NON_PLAYABLE_SPELLS_ON_STACK_WORKAROUND = false; // warning, see workaround's info on usage
+
     private transient Boolean responseOpenedForAnswer = false; // can't get response until prepared target (e.g. until send all fire events to all players)
     private final transient PlayerResponse response = new PlayerResponse();
 
@@ -1039,7 +1042,7 @@ public class HumanPlayer extends PlayerImpl {
 
             if (response.getString() != null
                     && response.getString().equals("special")) {
-                specialAction(game);
+                activateSpecialAction(game, null);
             } else if (response.getUUID() != null) {
                 boolean result = false;
                 MageObject object = game.getObject(response.getUUID());
@@ -1057,6 +1060,24 @@ public class HumanPlayer extends PlayerImpl {
                         }
                         if (actingPlayer != null) {
                             useableAbilities = actingPlayer.getPlayableActivatedAbilities(object, zone, game);
+
+                            // GUI: workaround to enable users to put spells on stack without real available mana
+                            // (without highlighting, like it was in old versions before June 2020)
+                            // Reason: some gain ability adds cost modification and other things to spells on stack only,
+                            // e.g. xmage can't find playable ability before put that spell on stack (wtf example: Chief Engineer,
+                            // see ConvokeTest)
+                            // TODO: it's a BAD workaround  -- users can't see that card/ability is broken and will not report to us, AI can't play that ability too
+                            // Enable it on massive broken cards/abilities only or for manual tests
+                            if (ALLOW_USERS_TO_PUT_NON_PLAYABLE_SPELLS_ON_STACK_WORKAROUND) {
+                                if (object instanceof Card) {
+                                    for (Ability ability : ((Card) object).getAbilities(game)) {
+                                        if (ability instanceof SpellAbility && ((SpellAbility) ability).canActivate(actingPlayer.getId(), game).canActivate()
+                                                || ability instanceof PlayLandAbility) {
+                                            useableAbilities.putIfAbsent(ability.getId(), (ActivatedAbility) ability);
+                                        }
+                                    }
+                                }
+                            }
                         }
 
                         if (object instanceof Card
@@ -1221,7 +1242,7 @@ public class HumanPlayer extends PlayerImpl {
             } else if (response.getString() != null
                     && response.getString().equals("special")) {
                 if (unpaid instanceof ManaCostsImpl) {
-                    specialManaAction(unpaid, game);
+                    activateSpecialAction(game, unpaid);
                 }
             } else if (response.getManaType() != null) {
                 // this mana type can be paid once from pool
@@ -1336,14 +1357,26 @@ public class HumanPlayer extends PlayerImpl {
         if (object == null) {
             return;
         }
-        if (AbilityType.SPELL.equals(abilityToCast.getAbilityType())) {
+
+        // GUI: for user's information only - check if mana abilities allows to use here (getUseableManaAbilities already filter it)
+        // Reason: when you use special mana ability then normal mana abilities will be restricted to pay. Users
+        // can't see lands as playable and must know the reason (if they click on land then they get that message)
+        if (abilityToCast.getAbilityType() == AbilityType.SPELL) {
             Spell spell = game.getStack().getSpell(abilityToCast.getSourceId());
-            if (spell != null && !spell.isResolving()
-                    && spell.isDoneActivatingManaAbilities()) {
-                game.informPlayer(this, "You can no longer use activated mana abilities to pay for the current spell. Cancel and recast the spell and activate mana abilities first.");
-                return;
+            boolean haveManaAbilities = object.getAbilities().stream().anyMatch(a -> a instanceof ManaAbility);
+            if (spell != null && !spell.isResolving() && haveManaAbilities) {
+                switch (spell.getCurrentActivatingManaAbilitiesStep()) {
+                    // if you used special mana ability like convoke then normal mana abilities will be restricted to use, see Convoke for details
+                    case BEFORE:
+                    case NORMAL:
+                        break;
+                    case AFTER:
+                        game.informPlayer(this, "You can no longer use activated mana abilities to pay for the current spell (special mana pay already used). Cancel and recast the spell to activate mana abilities first.");
+                        return;
+                }
             }
         }
+
         Zone zone = game.getState().getZone(object.getId());
         if (zone != null) {
             LinkedHashMap<UUID, ActivatedManaAbilityImpl> useableAbilities = getUseableManaAbilities(object, zone, game);
@@ -1844,7 +1877,13 @@ public class HumanPlayer extends PlayerImpl {
         draft.firePickCardEvent(playerId);
     }
 
-    protected void specialAction(Game game) {
+    /**
+     * Activate special action (normal or mana)
+     *
+     * @param game
+     * @param unpaidForManaAction - set unpaid for mana actions like convoke
+     */
+    protected void activateSpecialAction(Game game, ManaCost unpaidForManaAction) {
         if (gameInCheckPlayableState(game)) {
             return;
         }
@@ -1853,7 +1892,7 @@ public class HumanPlayer extends PlayerImpl {
             return;
         }
 
-        Map<UUID, SpecialAction> specialActions = game.getState().getSpecialActions().getControlledBy(playerId, false);
+        Map<UUID, SpecialAction> specialActions = game.getState().getSpecialActions().getControlledBy(playerId, unpaidForManaAction != null);
         if (!specialActions.isEmpty()) {
 
             updateGameStatePriority("specialAction", game);
@@ -1863,39 +1902,13 @@ public class HumanPlayer extends PlayerImpl {
             }
             waitForResponse(game);
 
-            if (response.getUUID() != null) {
-                if (specialActions.containsKey(response.getUUID())) {
-                    activateAbility(specialActions.get(response.getUUID()), game);
-                }
-            }
-        }
-    }
-
-    protected void specialManaAction(ManaCost unpaid, Game game) {
-        if (gameInCheckPlayableState(game)) {
-            return;
-        }
-
-        if (!canRespond()) {
-            return;
-        }
-
-        Map<UUID, SpecialAction> specialActions = game.getState().getSpecialActions().getControlledBy(playerId, true);
-        if (!specialActions.isEmpty()) {
-            updateGameStatePriority("specialAction", game);
-            prepareForResponse(game);
-            if (!isExecutingMacro()) {
-                game.fireGetChoiceEvent(playerId, name, null, new ArrayList<>(specialActions.values()));
-            }
-            waitForResponse(game);
-
             if (response.getUUID() != null) {
                 if (specialActions.containsKey(response.getUUID())) {
                     SpecialAction specialAction = specialActions.get(response.getUUID());
-                    if (specialAction != null) {
-                        specialAction.setUnpaidMana(unpaid);
-                        activateAbility(specialActions.get(response.getUUID()), game);
+                    if (unpaidForManaAction != null) {
+                        specialAction.setUnpaidMana(unpaidForManaAction);
                     }
+                    activateAbility(specialAction, game);
                 }
             }
         }
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/asthough/PlayFromNonHandZoneTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/asthough/PlayFromNonHandZoneTest.java
index 1c0480fc1b..1e145efc24 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/asthough/PlayFromNonHandZoneTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/asthough/PlayFromNonHandZoneTest.java
@@ -273,7 +273,7 @@ public class PlayFromNonHandZoneTest extends CardTestPlayerBase {
      * the discard was required. In the first log, when it was cast after
      * Angelic Purge the discard was not required.
      */
-    
+
     @Test
     public void castFromExileButWithAdditionalCostTest() {
         // Ninjutsu {2}{U}{B}
@@ -282,7 +282,7 @@ public class PlayFromNonHandZoneTest extends CardTestPlayerBase {
         addCard(Zone.BATTLEFIELD, playerB, "Fallen Shinobi", 1); // Creature 5/4
         addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion");
         addCard(Zone.HAND, playerB, "Pillarfield Ox");
-        
+
         addCard(Zone.LIBRARY, playerB, "Pillarfield Ox"); // Card to draw on turn 2
         // As an additional cost to cast Tormenting Voice, discard a card.
         // Draw two cards.        
@@ -290,14 +290,14 @@ public class PlayFromNonHandZoneTest extends CardTestPlayerBase {
         // As an additional cost to cast this spell, sacrifice a creature.
         // Flying, Trample        
         addCard(Zone.LIBRARY, playerA, "Demon of Catastrophes"); // Creature {2}{B}{B}  6/6
-  
+
         skipInitShuffling();
 
         attack(2, playerB, "Fallen Shinobi");
-        
+
         castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Tormenting Voice");
         setChoice(playerB, "Pillarfield Ox"); // Discord for Tormenting Voice
-        
+
         castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Demon of Catastrophes");
         setChoice(playerB, "Silvercoat Lion"); // Sacrifice for Demon
 
@@ -309,20 +309,20 @@ public class PlayFromNonHandZoneTest extends CardTestPlayerBase {
 
         assertLife(playerA, 15);
         assertPermanentCount(playerB, "Fallen Shinobi", 1);
-        
-        assertGraveyardCount(playerA,  "Tormenting Voice", 1);
-        assertGraveyardCount(playerB,  "Pillarfield Ox", 1);  // Discarded for Tormenting Voice
-        
-        
+
+        assertGraveyardCount(playerA, "Tormenting Voice", 1);
+        assertGraveyardCount(playerB, "Pillarfield Ox", 1);  // Discarded for Tormenting Voice
+
+
         assertPermanentCount(playerB, "Demon of Catastrophes", 1);
-        assertGraveyardCount(playerB,  "Silvercoat Lion", 1);  // sacrificed for Demon
-        
+        assertGraveyardCount(playerB, "Silvercoat Lion", 1);  // sacrificed for Demon
+
         assertHandCount(playerB, "Pillarfield Ox", 1);
         assertHandCount(playerB, 3); // 2 from Tormenting Voice + 1 from Turn 2
         assertExileCount(playerA, 0); // Both exiled cards are cast
-    }    
-    
-    
+    }
+
+
     @Test
     public void castFromExileButWithAdditionalCost2Test() {
         // Ninjutsu {2}{U}{B}
@@ -331,7 +331,7 @@ public class PlayFromNonHandZoneTest extends CardTestPlayerBase {
         addCard(Zone.BATTLEFIELD, playerB, "Fallen Shinobi", 1); // Creature 5/4
         addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion");
         addCard(Zone.HAND, playerB, "Pillarfield Ox");
-        
+
         addCard(Zone.BATTLEFIELD, playerA, "Amulet of Kroog"); // Just to exile for Angelic Purge
 
         // As an additional cost to cast Tormenting Voice, discard a card.
@@ -341,18 +341,18 @@ public class PlayFromNonHandZoneTest extends CardTestPlayerBase {
         // As an additional cost to cast Angelic Purge, sacrifice a permanent.
         // Exile target artifact, creature, or enchantment.      
         addCard(Zone.LIBRARY, playerA, "Angelic Purge"); // Sorcery {2}{W}
-  
+
         skipInitShuffling();
 
         attack(2, playerB, "Fallen Shinobi");
-        
+
         castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Angelic Purge");
         setChoice(playerB, "Silvercoat Lion"); // Sacrifice for Purge
         addTarget(playerB, "Amulet of Kroog"); // Exile with Purge
-        
+
         castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Tormenting Voice");
         setChoice(playerB, "Pillarfield Ox"); // Discord for Tormenting Voice
-        
+
         setStopAt(2, PhaseStep.END_TURN);
 
         setStrictChooseMode(true);
@@ -361,27 +361,27 @@ public class PlayFromNonHandZoneTest extends CardTestPlayerBase {
 
         assertLife(playerA, 15);
         assertPermanentCount(playerB, "Fallen Shinobi", 1);
-        
-        assertGraveyardCount(playerA, "Angelic Purge", 1);
-        assertGraveyardCount(playerB,  "Silvercoat Lion", 1);  // sacrificed for Purge
 
-        assertGraveyardCount(playerA,  "Tormenting Voice", 1);
-        assertGraveyardCount(playerB,  "Pillarfield Ox", 1);  // Discarded for Tormenting Voice
-                     
+        assertGraveyardCount(playerA, "Angelic Purge", 1);
+        assertGraveyardCount(playerB, "Silvercoat Lion", 1);  // sacrificed for Purge
+
+        assertGraveyardCount(playerA, "Tormenting Voice", 1);
+        assertGraveyardCount(playerB, "Pillarfield Ox", 1);  // Discarded for Tormenting Voice
+
         assertHandCount(playerB, 3); // 2 from Tormenting Voice + 1 from Turn 2 draw
-        
+
         assertExileCount(playerA, 1); // Both exiled cards are cast
         assertExileCount(playerA, "Amulet of Kroog", 1); // Exiled with Purge
-    }     
-    
-     @Test
-    public void castAdventureWithFallenShinobiTest() {     
+    }
+
+    @Test
+    public void castAdventureWithFallenShinobiTest() {
         // Ninjutsu {2}{U}{B}
         // Whenever Fallen Shinobi deals combat damage to a player, that player exiles the top two cards 
         // of their library. Until end of turn, you may play those cards without paying their mana cost.
         addCard(Zone.BATTLEFIELD, playerB, "Fallen Shinobi", 1); // Creature 5/4
         addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion");
-        
+
         addCard(Zone.BATTLEFIELD, playerA, "Amulet of Kroog"); // Just to exile for Angelic Purge
 
         /* Curious Pair {1}{G}
@@ -391,23 +391,23 @@ public class PlayFromNonHandZoneTest extends CardTestPlayerBase {
          * Treats to Share {G}
          * Sorcery — Adventure
          * Create a Food token.
-         */        
-        addCard(Zone.LIBRARY, playerA, "Curious Pair");      
+         */
+        addCard(Zone.LIBRARY, playerA, "Curious Pair");
 
         // As an additional cost to cast Angelic Purge, sacrifice a permanent.
         // Exile target artifact, creature, or enchantment.      
         addCard(Zone.LIBRARY, playerA, "Angelic Purge"); // Sorcery {2}{W}
-  
+
         skipInitShuffling();
 
         attack(2, playerB, "Fallen Shinobi");
-        
+
         castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Angelic Purge");
         setChoice(playerB, "Silvercoat Lion"); // Sacrifice for Purge
         addTarget(playerB, "Amulet of Kroog"); // Exile with Purge
-        
+
         castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Treats to Share");
-        
+
         setStopAt(2, PhaseStep.END_TURN);
 
         setStrictChooseMode(true);
@@ -416,17 +416,46 @@ public class PlayFromNonHandZoneTest extends CardTestPlayerBase {
 
         assertLife(playerA, 15);
         assertPermanentCount(playerB, "Fallen Shinobi", 1);
-        
+
         assertGraveyardCount(playerA, "Angelic Purge", 1);
-        assertGraveyardCount(playerB,  "Silvercoat Lion", 1);  // sacrificed for Purge
+        assertGraveyardCount(playerB, "Silvercoat Lion", 1);  // sacrificed for Purge
 
         assertPermanentCount(playerB, "Food", 1);
         assertExileCount(playerA, "Curious Pair", 1);
-                     
+
         assertHandCount(playerB, 1); // 1 from Turn 2 draw
-        
+
         assertExileCount(playerA, 2); // Both exiled cards are cast 
         assertExileCount(playerA, "Amulet of Kroog", 1); // Exiled with Purge        
     }
-    
+
+    @Test
+    public void test_ActivateFromOpponentCreature() {
+        // Players can’t search libraries. Any player may pay {2} for that player to ignore this effect until end of turn.
+        addCard(Zone.BATTLEFIELD, playerB, "Leonin Arbiter", 1);
+        //
+        // {3}{U}{U}
+        // Search target opponent’s library for an artifact card and put that card onto the battlefield under your control. Then that player shuffles their library.
+        addCard(Zone.HAND, playerA, "Acquire", 2);
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 5 * 2 + 2);
+        addCard(Zone.LIBRARY, playerB, "Alpha Myr", 1);
+
+        // first cast -- can't search library
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Acquire", playerB);
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+
+        // second cast -- unlock library and search
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}: Any player may");
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+        //
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Acquire", playerB);
+        addTarget(playerA, "Alpha Myr");
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerA, "Alpha Myr", 1);
+    }
 }
diff --git a/Mage/src/main/java/mage/abilities/SpecialAction.java b/Mage/src/main/java/mage/abilities/SpecialAction.java
index b4cc07c731..fd0a85092f 100644
--- a/Mage/src/main/java/mage/abilities/SpecialAction.java
+++ b/Mage/src/main/java/mage/abilities/SpecialAction.java
@@ -1,18 +1,22 @@
-
-
 package mage.abilities;
 
+import mage.abilities.costs.mana.AlternateManaPaymentAbility;
 import mage.abilities.costs.mana.ManaCost;
+import mage.abilities.mana.ManaOptions;
 import mage.constants.AbilityType;
 import mage.constants.Zone;
+import mage.game.Game;
+import mage.game.stack.Spell;
+import mage.game.stack.StackObject;
+
+import java.util.UUID;
 
 /**
- *
- * @author BetaSteward_at_googlemail.com
+ * @author BetaSteward_at_googlemail.com, JayDi85
  */
 public abstract class SpecialAction extends ActivatedAbilityImpl {
 
-    private boolean manaAction;
+    private final AlternateManaPaymentAbility manaAbility; // mana actions generates on every pay cycle, no need to copy it
     protected ManaCost unpaidMana;
 
     public SpecialAction() {
@@ -20,22 +24,23 @@ public abstract class SpecialAction extends ActivatedAbilityImpl {
     }
 
     public SpecialAction(Zone zone) {
-        this(zone, false);
+        this(zone, null);
     }
-    public SpecialAction(Zone zone, boolean manaAction) {
+
+    public SpecialAction(Zone zone, AlternateManaPaymentAbility manaAbility) {
         super(AbilityType.SPECIAL_ACTION, zone);
         this.usesStack = false;
-        this.manaAction = manaAction;
+        this.manaAbility = manaAbility;
     }
 
     public SpecialAction(final SpecialAction action) {
         super(action);
-        this.manaAction = action.manaAction;
         this.unpaidMana = action.unpaidMana;
+        this.manaAbility = action.manaAbility;
     }
 
     public boolean isManaAction() {
-        return manaAction;
+        return manaAbility != null;
     }
 
     public void setUnpaidMana(ManaCost manaCost) {
@@ -45,4 +50,29 @@ public abstract class SpecialAction extends ActivatedAbilityImpl {
     public ManaCost getUnpaidMana() {
         return unpaidMana;
     }
+
+    public ManaOptions getManaOptions(Ability source, Game game, ManaCost unpaid) {
+        if (manaAbility != null) {
+            return manaAbility.getManaOptions(source, game, unpaid);
+        }
+        return null;
+    }
+
+    @Override
+    public ActivationStatus canActivate(UUID playerId, Game game) {
+        if (isManaAction()) {
+            // limit play mana abilities by steps
+            int currentStepOrder = 0;
+            if (!game.getStack().isEmpty()) {
+                StackObject stackObject = game.getStack().getFirst();
+                if (stackObject instanceof Spell) {
+                    currentStepOrder = ((Spell) stackObject).getCurrentActivatingManaAbilitiesStep().getStepOrder();
+                }
+            }
+            if (currentStepOrder > manaAbility.useOnActivationManaAbilityStep().getStepOrder()) {
+                return ActivationStatus.getFalse();
+            }
+        }
+        return super.canActivate(playerId, game);
+    }
 }
diff --git a/Mage/src/main/java/mage/abilities/costs/mana/ActivationManaAbilityStep.java b/Mage/src/main/java/mage/abilities/costs/mana/ActivationManaAbilityStep.java
new file mode 100644
index 0000000000..27a89c4766
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/costs/mana/ActivationManaAbilityStep.java
@@ -0,0 +1,25 @@
+package mage.abilities.costs.mana;
+
+/**
+ * Some special AlternateManaPaymentAbility must be restricted to pay before or after mana abilities.
+ * Game logic: if you use special mana ability then normal mana abilities must be restricted and vice versa,
+ * see Convoke for more info and rules
+ *
+ * @author JayDi85
+ */
+
+public enum ActivationManaAbilityStep {
+    BEFORE(0), // assist
+    NORMAL(1), // all activated mana abilities
+    AFTER(2); // convoke, delve, improvise
+
+    private final int stepOrder;
+
+    ActivationManaAbilityStep(int stepOrder) {
+        this.stepOrder = stepOrder;
+    }
+
+    public int getStepOrder() {
+        return stepOrder;
+    }
+}
diff --git a/Mage/src/main/java/mage/abilities/costs/mana/AlternateManaPaymentAbility.java b/Mage/src/main/java/mage/abilities/costs/mana/AlternateManaPaymentAbility.java
index d9df07ba03..e3769cbcec 100644
--- a/Mage/src/main/java/mage/abilities/costs/mana/AlternateManaPaymentAbility.java
+++ b/Mage/src/main/java/mage/abilities/costs/mana/AlternateManaPaymentAbility.java
@@ -1,18 +1,17 @@
-
 package mage.abilities.costs.mana;
 
 import mage.abilities.Ability;
+import mage.abilities.mana.ManaOptions;
 import mage.game.Game;
 
 /**
  * Interface for abilities that allow the player to pay mana costs of a spell in alternate ways.
  * For the payment SpecialActions are used.
- *
+ * <p>
  * Example of such an alternate payment ability: {@link mage.abilities.keyword.DelveAbility}
  *
- * @author LevelX2
+ * @author LevelX2, JayDi85
  */
-@FunctionalInterface
 public interface AlternateManaPaymentAbility {
     /**
      * Adds the special action to the state, that allows the user to do the alternate mana payment
@@ -22,4 +21,21 @@ public interface AlternateManaPaymentAbility {
      * @param unpaid unapaid mana costs of the spell
      */
     void addSpecialAction(Ability source, Game game, ManaCost unpaid);
+
+    /**
+     * All possible mana payments that can make that ability (uses to find playable abilities)
+     *
+     * @param source
+     * @param game
+     * @param unpaid
+     * @return
+     */
+    ManaOptions getManaOptions(Ability source, Game game, ManaCost unpaid);
+
+    /**
+     * Mana payment step where you can use it
+     *
+     * @return
+     */
+    ActivationManaAbilityStep useOnActivationManaAbilityStep();
 }
\ No newline at end of file
diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousEffectImpl.java b/Mage/src/main/java/mage/abilities/effects/ContinuousEffectImpl.java
index e2f8e2f159..37671eed41 100644
--- a/Mage/src/main/java/mage/abilities/effects/ContinuousEffectImpl.java
+++ b/Mage/src/main/java/mage/abilities/effects/ContinuousEffectImpl.java
@@ -1,10 +1,10 @@
 package mage.abilities.effects;
 
-import java.util.*;
 import mage.MageObjectReference;
 import mage.abilities.Ability;
 import mage.abilities.CompoundAbility;
 import mage.abilities.MageSingleton;
+import mage.abilities.costs.mana.ActivationManaAbilityStep;
 import mage.abilities.dynamicvalue.DynamicValue;
 import mage.abilities.dynamicvalue.common.DomainValue;
 import mage.abilities.dynamicvalue.common.SignInversionDynamicValue;
@@ -20,6 +20,8 @@ import mage.game.stack.StackObject;
 import mage.players.Player;
 import mage.target.targetpointer.TargetPointer;
 
+import java.util.*;
+
 /**
  * @author BetaSteward_at_googlemail.com, JayDi85
  */
@@ -416,7 +418,7 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
             StackObject stackObject = game.getStack().getFirst();
             return !(stackObject instanceof Spell)
                     || !Zone.LIBRARY.equals(((Spell) stackObject).getFromZone())
-                    || ((Spell) stackObject).isDoneActivatingManaAbilities();
+                    || ((Spell) stackObject).getCurrentActivatingManaAbilitiesStep() == ActivationManaAbilityStep.AFTER; // mana payment finished
         }
         return true;
     }
diff --git a/Mage/src/main/java/mage/abilities/mana/ActivatedManaAbilityImpl.java b/Mage/src/main/java/mage/abilities/mana/ActivatedManaAbilityImpl.java
index 5ebda8934b..85eae1ea4a 100644
--- a/Mage/src/main/java/mage/abilities/mana/ActivatedManaAbilityImpl.java
+++ b/Mage/src/main/java/mage/abilities/mana/ActivatedManaAbilityImpl.java
@@ -10,6 +10,8 @@ import mage.constants.AsThoughEffectType;
 import mage.constants.TimingRule;
 import mage.constants.Zone;
 import mage.game.Game;
+import mage.game.stack.Spell;
+import mage.game.stack.StackObject;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -54,10 +56,24 @@ public abstract class ActivatedManaAbilityImpl extends ActivatedAbilityImpl impl
                 && null == game.getContinuousEffects().asThough(sourceId, AsThoughEffectType.ACTIVATE_AS_INSTANT, this, controllerId, game)) {
             return ActivationStatus.getFalse();
         }
-        // check if player is in the process of playing spell costs and they are no longer allowed to use activated mana abilities (e.g. because they started to use improvise)
+
+        // check if player is in the process of playing spell costs and they are no longer allowed to use
+        // activated mana abilities (e.g. because they started to use improvise or convoke)
+        if (!game.getStack().isEmpty()) {
+            StackObject stackObject = game.getStack().getFirst();
+            if (stackObject instanceof Spell) {
+                switch (((Spell) stackObject).getCurrentActivatingManaAbilitiesStep()) {
+                    case BEFORE:
+                    case NORMAL:
+                        break;
+                    case AFTER:
+                        return ActivationStatus.getFalse();
+                }
+            }
+        }
+
         //20091005 - 605.3a
         return new ActivationStatus(costs.canPay(this, sourceId, controllerId, game), null);
-
     }
 
     /**
diff --git a/Mage/src/main/java/mage/game/stack/Spell.java b/Mage/src/main/java/mage/game/stack/Spell.java
index a9b5dd2b93..11a2562aa7 100644
--- a/Mage/src/main/java/mage/game/stack/Spell.java
+++ b/Mage/src/main/java/mage/game/stack/Spell.java
@@ -1,6 +1,5 @@
 package mage.game.stack;
 
-import java.util.*;
 import mage.MageInt;
 import mage.MageObject;
 import mage.Mana;
@@ -11,6 +10,7 @@ import mage.abilities.Mode;
 import mage.abilities.SpellAbility;
 import mage.abilities.costs.AlternativeSourceCosts;
 import mage.abilities.costs.Cost;
+import mage.abilities.costs.mana.ActivationManaAbilityStep;
 import mage.abilities.costs.mana.ManaCost;
 import mage.abilities.costs.mana.ManaCosts;
 import mage.abilities.keyword.BestowAbility;
@@ -32,6 +32,11 @@ import mage.players.Player;
 import mage.util.GameLog;
 import mage.util.SubTypeList;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+
 /**
  * @author BetaSteward_at_googlemail.com
  */
@@ -62,7 +67,7 @@ public class Spell extends StackObjImpl implements Card {
     private boolean resolving = false;
     private UUID commandedBy = null; // for Word of Command
 
-    private boolean doneActivatingManaAbilities; // if this is true, the player is no longer allowed to pay the spell costs with activating of mana abilies
+    private ActivationManaAbilityStep currentActivatingManaAbilitiesStep = ActivationManaAbilityStep.BEFORE;
 
     public Spell(Card card, SpellAbility ability, UUID controllerId, Zone fromZone) {
         this.card = card;
@@ -118,12 +123,12 @@ public class Spell extends StackObjImpl implements Card {
         this.resolving = spell.resolving;
         this.commandedBy = spell.commandedBy;
 
-        this.doneActivatingManaAbilities = spell.doneActivatingManaAbilities;
+        this.currentActivatingManaAbilitiesStep = spell.currentActivatingManaAbilitiesStep;
         this.targetChanged = spell.targetChanged;
     }
 
     public boolean activate(Game game, boolean noMana) {
-        setDoneActivatingManaAbilities(false); // used for e.g. improvise
+        setCurrentActivatingManaAbilitiesStep(ActivationManaAbilityStep.BEFORE); // mana payment step started, can use any mana abilities, see AlternateManaPaymentAbility
 
         if (!ability.activate(game, noMana)) {
             return false;
@@ -147,7 +152,7 @@ public class Spell extends StackObjImpl implements Card {
                 return false;
             }
         }
-        setDoneActivatingManaAbilities(true); // can be activated again maybe during the resolution of the spell (e.g. Metallic Rebuke)
+        setCurrentActivatingManaAbilitiesStep(ActivationManaAbilityStep.NORMAL);
         return true;
     }
 
@@ -401,12 +406,12 @@ public class Spell extends StackObjImpl implements Card {
         }
     }
 
-    public boolean isDoneActivatingManaAbilities() {
-        return doneActivatingManaAbilities;
+    public ActivationManaAbilityStep getCurrentActivatingManaAbilitiesStep() {
+        return this.currentActivatingManaAbilitiesStep;
     }
 
-    public void setDoneActivatingManaAbilities(boolean doneActivatingManaAbilities) {
-        this.doneActivatingManaAbilities = doneActivatingManaAbilities;
+    public void setCurrentActivatingManaAbilitiesStep(ActivationManaAbilityStep currentActivatingManaAbilitiesStep) {
+        this.currentActivatingManaAbilitiesStep = currentActivatingManaAbilitiesStep;
     }
 
     @Override
diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java
index 742f456bb4..bbc47b1193 100644
--- a/Mage/src/main/java/mage/players/PlayerImpl.java
+++ b/Mage/src/main/java/mage/players/PlayerImpl.java
@@ -1,9 +1,6 @@
 package mage.players;
 
 import com.google.common.collect.ImmutableMap;
-import java.io.Serializable;
-import java.util.*;
-import java.util.Map.Entry;
 import mage.ConditionalMana;
 import mage.MageObject;
 import mage.MageObjectReference;
@@ -15,6 +12,7 @@ import mage.abilities.common.PlayLandAsCommanderAbility;
 import mage.abilities.common.WhileSearchingPlayFromLibraryAbility;
 import mage.abilities.common.delayed.AtTheEndOfTurnStepPostDelayedTriggeredAbility;
 import mage.abilities.costs.*;
+import mage.abilities.costs.mana.AlternateManaPaymentAbility;
 import mage.abilities.costs.mana.ManaCost;
 import mage.abilities.costs.mana.ManaCosts;
 import mage.abilities.costs.mana.ManaCostsImpl;
@@ -68,6 +66,10 @@ import mage.util.GameLog;
 import mage.util.RandomUtil;
 import org.apache.log4j.Logger;
 
+import java.io.Serializable;
+import java.util.*;
+import java.util.Map.Entry;
+
 public abstract class PlayerImpl implements Player, Serializable {
 
     private static final Logger logger = Logger.getLogger(PlayerImpl.class);
@@ -611,9 +613,9 @@ public abstract class PlayerImpl implements Player, Serializable {
                     && this.hasOpponent(sourceControllerId, game)
                     && game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, null, sourceControllerId, game) == null
                     && abilities.stream()
-                            .filter(HexproofBaseAbility.class::isInstance)
-                            .map(HexproofBaseAbility.class::cast)
-                            .anyMatch(ability -> ability.checkObject(source, game))) {
+                    .filter(HexproofBaseAbility.class::isInstance)
+                    .map(HexproofBaseAbility.class::cast)
+                    .anyMatch(ability -> ability.checkObject(source, game))) {
                 return false;
             }
 
@@ -653,7 +655,7 @@ public abstract class PlayerImpl implements Player, Serializable {
                 game.informPlayers(getLogName() + " discards down to "
                         + this.maxHandSize
                         + (this.maxHandSize == 1
-                                ? " hand card" : " hand cards"));
+                        ? " hand card" : " hand cards"));
             }
             discard(hand.size() - this.maxHandSize, false, null, game);
         }
@@ -802,7 +804,7 @@ public abstract class PlayerImpl implements Player, Serializable {
         }
         GameEvent gameEvent = GameEvent.getEvent(GameEvent.EventType.DISCARD_CARD,
                 card.getId(), source == null
-                ? null : source.getSourceId(), playerId);
+                        ? null : source.getSourceId(), playerId);
         gameEvent.setFlag(source != null); // event from effect or from cost (source == null)
         if (game.replaceEvent(gameEvent, source)) {
             return false;
@@ -1811,9 +1813,9 @@ public abstract class PlayerImpl implements Player, Serializable {
     }
 
     private List<Permanent> getPermanentsThatCanBeUntapped(Game game,
-            List<Permanent> canBeUntapped,
-            RestrictionUntapNotMoreThanEffect handledEffect,
-            Map<Entry<RestrictionUntapNotMoreThanEffect, Set<Ability>>, Integer> notMoreThanEffectsUsage) {
+                                                           List<Permanent> canBeUntapped,
+                                                           RestrictionUntapNotMoreThanEffect handledEffect,
+                                                           Map<Entry<RestrictionUntapNotMoreThanEffect, Set<Ability>>, Integer> notMoreThanEffectsUsage) {
         List<Permanent> leftForUntap = new ArrayList<>();
         // select permanents that can still be untapped
         for (Permanent permanent : canBeUntapped) {
@@ -2522,7 +2524,7 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean searchLibrary(TargetCardInLibrary target, Ability source, Game game, UUID targetPlayerId,
-            boolean triggerEvents) {
+                                 boolean triggerEvents) {
         //20091005 - 701.14c
         Library searchedLibrary = null;
         String searchInfo = null;
@@ -2724,7 +2726,7 @@ public abstract class PlayerImpl implements Player, Serializable {
     /**
      * @param game
      * @param appliedEffects
-     * @param numSides Number of sides the dice has
+     * @param numSides       Number of sides the dice has
      * @return the number that the player rolled
      */
     @Override
@@ -2761,16 +2763,16 @@ public abstract class PlayerImpl implements Player, Serializable {
     /**
      * @param game
      * @param appliedEffects
-     * @param numberChaosSides The number of chaos sides the planar die
-     * currently has (normally 1 but can be 5)
+     * @param numberChaosSides  The number of chaos sides the planar die
+     *                          currently has (normally 1 but can be 5)
      * @param numberPlanarSides The number of chaos sides the planar die
-     * currently has (normally 1)
+     *                          currently has (normally 1)
      * @return the outcome that the player rolled. Either ChaosRoll, PlanarRoll
      * or NilRoll
      */
     @Override
     public PlanarDieRoll rollPlanarDie(Game game, List<UUID> appliedEffects, int numberChaosSides,
-            int numberPlanarSides) {
+                                       int numberPlanarSides) {
         int result = RandomUtil.nextInt(9) + 1;
         PlanarDieRoll roll = PlanarDieRoll.NIL_ROLL;
         if (numberChaosSides + numberPlanarSides > 9) {
@@ -2927,7 +2929,7 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     /**
      * @param ability
-     * @param available if null, it won't be checked if enough mana is available
+     * @param available    if null, it won't be checked if enough mana is available
      * @param sourceObject
      * @param game
      * @return
@@ -3076,7 +3078,6 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     protected ActivatedAbility findActivatedAbilityFromPlayable(Card card, ManaOptions manaAvailable, Ability ability, Game game) {
         // replace alternative abilities by real play abilities (e.g. morph/facedown static ability by play land)
-
         if (ability instanceof ActivatedManaAbilityImpl) {
             // mana ability
             if (((ActivatedManaAbilityImpl) ability).canActivate(this.getId(), game).canActivate()) {
@@ -3085,8 +3086,11 @@ public abstract class PlayerImpl implements Player, Serializable {
         } else if (ability instanceof AlternativeSourceCosts) {
             // alternative cost must be replaced by real play ability
             return findActivatedAbilityFromAlternativeSourceCost(card, manaAvailable, ability, game);
+        } else if (ability instanceof AlternateManaPaymentAbility) {
+            // alternative mana pay like convoke (tap creature to pay)
+            return findActivatedAbilityFromAlternateManaPaymentAbility(card, manaAvailable, (AlternateManaPaymentAbility) ability, game);
         } else if (ability instanceof ActivatedAbility) {
-            // activated ability
+            // all other activated ability
             if (canPlay((ActivatedAbility) ability, manaAvailable, card, game)) {
                 return (ActivatedAbility) ability;
             }
@@ -3119,6 +3123,20 @@ public abstract class PlayerImpl implements Player, Serializable {
         return null;
     }
 
+    protected ActivatedAbility findActivatedAbilityFromAlternateManaPaymentAbility(Card card, ManaOptions manaAvailable, AlternateManaPaymentAbility ability, Game game) {
+        // alternative mana payment allows to pay mana for spell ability
+        SpellAbility spellAbility = card.getSpellAbility();
+        if (spellAbility != null) {
+            ManaOptions manaSpecial = ability.getManaOptions(spellAbility, game, spellAbility.getManaCostsToPay());
+            ManaOptions manaFull = manaAvailable.copy();
+            manaFull.addMana(manaSpecial);
+            if (canPlay(spellAbility, manaFull, card, game)) {
+                return spellAbility;
+            }
+        }
+        return null;
+    }
+
     protected boolean canLandPlayAlternateSourceCostsAbility(Card sourceObject, ManaOptions available, Ability ability, Game game) {
         if (sourceObject != null && !(sourceObject instanceof Permanent)) {
             Ability sourceAbility = sourceObject.getAbilities().stream()
@@ -3221,37 +3239,45 @@ public abstract class PlayerImpl implements Player, Serializable {
 
             boolean canActivateAsHandZone = permittingObject != null
                     || (fromZone == Zone.GRAVEYARD && canPlayCardsFromGraveyard());
+            boolean possibleToPlay = false;
 
-            // as affected controller
-            UUID savedControllerId = ability.getControllerId();
-            ability.setControllerId(this.getId());
-            try {
-                boolean possibleToPlay = false;
+            // spell/hand abilities (play from all zones)
+            // need permitingObject or canPlayCardsFromGraveyard
+            if (canActivateAsHandZone
+                    && ability.getZone().match(Zone.HAND)
+                    && (isPlaySpell || isPlayLand)) {
+                possibleToPlay = true;
+            }
 
-                // spell/hand abilities (play from all zones)
-                // need permitingObject or canPlayCardsFromGraveyard
-                if (canActivateAsHandZone
-                        && ability.getZone().match(Zone.HAND)
-                        && (isPlaySpell || isPlayLand)) {
-                    possibleToPlay = true;
+            // zone's abilities (play from specific zone)
+            // no need in permitingObject
+            if (fromZone != Zone.ALL && ability.getZone().match(fromZone)) {
+                possibleToPlay = true;
+            }
+
+            if (!possibleToPlay) {
+                continue;
+            }
+
+            // direct mode (with original controller)
+            ActivatedAbility playAbility = findActivatedAbilityFromPlayable(card, availableMana, ability, game);
+            if (playAbility != null && !output.contains(playAbility)) {
+                output.add(playAbility);
+                continue;
+            }
+
+            // from non hand mode (with affected controller)
+            if (canActivateAsHandZone) {
+                UUID savedControllerId = ability.getControllerId();
+                ability.setControllerId(this.getId());
+                try {
+                    playAbility = findActivatedAbilityFromPlayable(card, availableMana, ability, game);
+                    if (playAbility != null && !output.contains(playAbility)) {
+                        output.add(playAbility);
+                    }
+                } finally {
+                    ability.setControllerId(savedControllerId);
                 }
-
-                // zone's abilities (play from specific zone)
-                // no need in permitingObject
-                if (fromZone != Zone.ALL && ability.getZone().match(fromZone)) {
-                    possibleToPlay = true;
-                }
-
-                if (!possibleToPlay) {
-                    continue;
-                }
-
-                ActivatedAbility playAbility = findActivatedAbilityFromPlayable(card, availableMana, ability, game);
-                if (playAbility != null && !output.contains(playAbility)) {
-                    output.add(playAbility);
-                }
-            } finally {
-                ability.setControllerId(savedControllerId);
             }
         }
     }
@@ -3270,8 +3296,10 @@ public abstract class PlayerImpl implements Player, Serializable {
         boolean previousState = game.inCheckPlayableState();
         game.setCheckPlayableState(true);
         try {
+            // basic mana
             ManaOptions availableMana = getManaAvailable(game);
             availableMana.addMana(manaPool.getMana());
+            // conditional mana
             for (ConditionalMana conditionalMana : manaPool.getConditionalMana()) {
                 availableMana.addMana(conditionalMana);
             }
@@ -3364,7 +3392,7 @@ public abstract class PlayerImpl implements Player, Serializable {
 
             // activated abilities from battlefield objects
             if (fromAll || fromZone == Zone.BATTLEFIELD) {
-                for (Permanent permanent : game.getBattlefield().getAllActivePermanents(playerId)) {
+                for (Permanent permanent : game.getBattlefield().getAllActivePermanents()) {
                     boolean canUseActivated = permanent.canUseActivatedAbilities(game);
                     List<ActivatedAbility> battlePlayable = new ArrayList<>();
                     getPlayableFromCardAll(game, Zone.BATTLEFIELD, permanent, availableMana, battlePlayable);
@@ -3624,7 +3652,7 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean canPaySacrificeCost(Permanent permanent, UUID sourceId,
-            UUID controllerId, Game game
+                                       UUID controllerId, Game game
     ) {
         return sacrificeCostFilter == null || !sacrificeCostFilter.match(permanent, sourceId, controllerId, game);
     }
@@ -3777,8 +3805,8 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCards(Card card, Zone toZone,
-            Ability source, Game game,
-            boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
+                             Ability source, Game game,
+                             boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
     ) {
         Set<Card> cardList = new HashSet<>();
         if (card != null) {
@@ -3789,22 +3817,22 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCards(Cards cards, Zone toZone,
-            Ability source, Game game
+                             Ability source, Game game
     ) {
         return moveCards(cards.getCards(game), toZone, source, game);
     }
 
     @Override
     public boolean moveCards(Set<Card> cards, Zone toZone,
-            Ability source, Game game
+                             Ability source, Game game
     ) {
         return moveCards(cards, toZone, source, game, false, false, false, null);
     }
 
     @Override
     public boolean moveCards(Set<Card> cards, Zone toZone,
-            Ability source, Game game,
-            boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
+                             Ability source, Game game,
+                             boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
     ) {
         if (cards.isEmpty()) {
             return true;
@@ -3906,8 +3934,8 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCardsToExile(Card card, Ability source,
-            Game game, boolean withName, UUID exileId,
-            String exileZoneName
+                                    Game game, boolean withName, UUID exileId,
+                                    String exileZoneName
     ) {
         Set<Card> cards = new HashSet<>();
         cards.add(card);
@@ -3916,8 +3944,8 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCardsToExile(Set<Card> cards, Ability source,
-            Game game, boolean withName, UUID exileId,
-            String exileZoneName
+                                    Game game, boolean withName, UUID exileId,
+                                    String exileZoneName
     ) {
         if (cards.isEmpty()) {
             return true;
@@ -3933,14 +3961,14 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCardToHandWithInfo(Card card, UUID sourceId,
-            Game game
+                                          Game game
     ) {
         return this.moveCardToHandWithInfo(card, sourceId, game, true);
     }
 
     @Override
     public boolean moveCardToHandWithInfo(Card card, UUID sourceId,
-            Game game, boolean withName
+                                          Game game, boolean withName
     ) {
         boolean result = false;
         Zone fromZone = game.getState().getZone(card.getId());
@@ -3965,7 +3993,7 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public Set<Card> moveCardsToGraveyardWithInfo(Set<Card> allCards, Ability source,
-            Game game, Zone fromZone
+                                                  Game game, Zone fromZone
     ) {
         UUID sourceId = source == null ? null : source.getSourceId();
         Set<Card> movedCards = new LinkedHashSet<>();
@@ -3973,7 +4001,7 @@ public abstract class PlayerImpl implements Player, Serializable {
             // identify cards from one owner
             Cards cards = new CardsImpl();
             UUID ownerId = null;
-            for (Iterator<Card> it = allCards.iterator(); it.hasNext();) {
+            for (Iterator<Card> it = allCards.iterator(); it.hasNext(); ) {
                 Card card = it.next();
                 if (cards.isEmpty()) {
                     ownerId = card.getOwnerId();
@@ -4036,7 +4064,7 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCardToGraveyardWithInfo(Card card, UUID sourceId,
-            Game game, Zone fromZone
+                                               Game game, Zone fromZone
     ) {
         if (card == null) {
             return false;
@@ -4065,8 +4093,8 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCardToLibraryWithInfo(Card card, UUID sourceId,
-            Game game, Zone fromZone,
-            boolean toTop, boolean withName
+                                             Game game, Zone fromZone,
+                                             boolean toTop, boolean withName
     ) {
         if (card == null) {
             return false;
@@ -4131,7 +4159,7 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCardToExileWithInfo(Card card, UUID exileId, String exileName, UUID sourceId,
-            Game game, Zone fromZone, boolean withName) {
+                                           Game game, Zone fromZone, boolean withName) {
         if (card == null) {
             return false;
         }
@@ -4154,7 +4182,7 @@ public abstract class PlayerImpl implements Player, Serializable {
                 game.informPlayers(this.getLogName() + " moves " + (withName ? card.getLogName()
                         + (card.isCopy() ? " (Copy)" : "") : "a card face down") + ' '
                         + (fromZone != null ? "from " + fromZone.toString().toLowerCase(Locale.ENGLISH)
-                                + ' ' : "") + "to the exile zone");
+                        + ' ' : "") + "to the exile zone");
 
             }
             result = true;

From 10cf884923abe283db82b8fcbf91fa4d428a96d9 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Fri, 19 Jun 2020 13:16:26 +0400
Subject: [PATCH 399/586] AI and test framework improved: * Now AI can see and
 use special mana payments like convoke, delve, improvise pays; * Now devs can
 test special mana payments (disable auto-payment and use choices for mana
 pool and special pays); * Fixed broken TargetDiscard in tests; * Fixed broken
 same named targets in tests;

---
 .../java/mage/player/ai/ComputerPlayer.java   | 24 ++++++
 .../java/org/mage/test/player/TestPlayer.java | 83 ++++++++++++++-----
 2 files changed, 88 insertions(+), 19 deletions(-)

diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java
index bdf8fd13b4..e097b62393 100644
--- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java
+++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java
@@ -1533,10 +1533,34 @@ public class ComputerPlayer extends PlayerImpl implements Player {
                 }
             }
         }
+
         // pay phyrexian life costs
         if (cost instanceof PhyrexianManaCost) {
             return cost.pay(null, game, null, playerId, false, null) || permittingObject != null;
         }
+
+        // pay special mana like convoke cost (tap for pay)
+        // GUI: user see "special" button while pay spell's cost
+        // TODO: AI can't prioritize special mana types to pay, e.g. it will use first available
+        SpecialAction specialAction = game.getState().getSpecialActions().getControlledBy(this.getId(), true)
+                .values().stream().findFirst().orElse(null);
+        ManaOptions specialMana = specialAction == null ? null : specialAction.getManaOptions(ability, game, unpaid);
+        if (specialMana != null) {
+            for (Mana netMana : specialMana) {
+                if (cost.testPay(netMana) || permittingObject != null) {
+                    if (netMana instanceof ConditionalMana && !((ConditionalMana) netMana).apply(ability, game, getId(), cost)) {
+                        continue;
+                    }
+                    specialAction.setUnpaidMana(unpaid);
+                    if (activateAbility(specialAction, game)) {
+                        return true;
+                    }
+                    // only one time try to pay
+                    break;
+                }
+            }
+        }
+
         return false;
     }
 
diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
index d83cb859ff..c937ce70d6 100644
--- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
+++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
@@ -1795,7 +1795,7 @@ public class TestPlayer implements Player {
 
         assertAliasSupportInChoices(true);
         if (!choices.isEmpty()) {
-            List<String> usedChoices = new ArrayList<>();
+            List<Integer> usedChoices = new ArrayList<>();
             List<UUID> usedTargets = new ArrayList<>();
 
             Ability source = null;
@@ -1883,7 +1883,8 @@ public class TestPlayer implements Player {
                 boolean targetCompleted = false;
 
                 CheckAllChoices:
-                for (String choiceRecord : choices) {
+                for (int choiceIndex = 0; choiceIndex < choices.size(); choiceIndex++) {
+                    String choiceRecord = choices.get(choiceIndex);
                     if (targetCompleted) {
                         break CheckAllChoices;
                     }
@@ -1923,14 +1924,19 @@ public class TestPlayer implements Player {
                     }
 
                     if (targetFound) {
-                        usedChoices.add(choiceRecord);
+                        usedChoices.add(choiceIndex);
                     }
                 }
 
                 // apply only on ALL targets or revert
                 if (usedChoices.size() > 0) {
                     if (target.isChosen()) {
-                        choices.removeAll(usedChoices);
+                        // remove all used choices
+                        for (int i = choices.size(); i >= 0; i--) {
+                            if (usedChoices.contains(i)) {
+                                choices.remove(i);
+                            }
+                        }
                         return true;
                     } else {
                         Assert.fail("Not full targets list.");
@@ -2091,7 +2097,7 @@ public class TestPlayer implements Player {
                                     if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly)) {
                                         target.addTarget(permanent.getId(), source, game);
                                         targetFound = true;
-                                        break; // return to for (String targetName
+                                        break; // return to next targetName
                                     }
                                 }
                             }
@@ -2105,18 +2111,19 @@ public class TestPlayer implements Player {
             }
 
             // card in hand
-            if (target.getOriginalTarget() instanceof TargetCardInHand) {
+            if (target.getOriginalTarget() instanceof TargetCardInHand
+                    || target.getOriginalTarget() instanceof TargetDiscard) {
                 for (String targetDefinition : targets) {
                     checkTargetDefinitionMarksSupport(target, targetDefinition, "^");
                     String[] targetList = targetDefinition.split("\\^");
                     boolean targetFound = false;
                     for (String targetName : targetList) {
-                        for (Card card : computerPlayer.getHand().getCards(((TargetCardInHand) target.getOriginalTarget()).getFilter(), game)) {
+                        for (Card card : computerPlayer.getHand().getCards(((TargetCard) target.getOriginalTarget()).getFilter(), game)) {
                             if (hasObjectTargetNameOrAlias(card, targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) { // TODO: remove set code search?
                                 if (target.canTarget(abilityControllerId, card.getId(), source, game) && !target.getTargets().contains(card.getId())) {
                                     target.addTarget(card.getId(), source, game);
                                     targetFound = true;
-                                    break; // return to for (String targetName
+                                    break; // return to next targetName
                                 }
                             }
                         }
@@ -2141,7 +2148,7 @@ public class TestPlayer implements Player {
                                 if (target.canTarget(abilityControllerId, card.getId(), source, game) && !target.getTargets().contains(card.getId())) {
                                     target.addTarget(card.getId(), source, game);
                                     targetFound = true;
-                                    break; // return to for (String targetName
+                                    break; // return to next targetName
                                 }
                             }
                         }
@@ -2166,7 +2173,7 @@ public class TestPlayer implements Player {
                                 if (targetFull.canTarget(abilityControllerId, card.getId(), source, game) && !targetFull.getTargets().contains(card.getId())) {
                                     targetFull.add(card.getId(), game);
                                     targetFound = true;
-                                    break; // return to for (String targetName
+                                    break; // return to next targetName
                                 }
                             }
                         }
@@ -2216,7 +2223,7 @@ public class TestPlayer implements Player {
                                     if (target.canTarget(abilityControllerId, card.getId(), source, game) && !target.getTargets().contains(card.getId())) {
                                         target.addTarget(card.getId(), source, game);
                                         targetFound = true;
-                                        break IterateGraveyards;  // return to for (String targetName
+                                        break IterateGraveyards;  // return to next targetName
                                     }
                                 }
                             }
@@ -2243,7 +2250,7 @@ public class TestPlayer implements Player {
                                 if (target.canTarget(abilityControllerId, stackObject.getId(), source, game) && !target.getTargets().contains(stackObject.getId())) {
                                     target.addTarget(stackObject.getId(), source, game);
                                     targetFound = true;
-                                    break; // return to for (String targetName
+                                    break; // return to next targetName
                                 }
                             }
                         }
@@ -3620,38 +3627,76 @@ public class TestPlayer implements Player {
         groupsForTargetHandling = null;
 
         if (!computerPlayer.getManaPool().isAutoPayment()) {
-            // manual pay by mana clicks/commands
             if (!choices.isEmpty()) {
-                String needColor = choices.get(0);
-                switch (needColor) {
+                // manual pay by mana clicks/commands
+                String choice = choices.get(0);
+                boolean choiceUsed = false;
+                boolean choiceRemoved = false;
+                switch (choice) {
                     case "White":
                         Assert.assertTrue("pool must have white mana", computerPlayer.getManaPool().getWhite() > 0);
                         computerPlayer.getManaPool().unlockManaType(ManaType.WHITE);
+                        choiceUsed = true;
                         break;
                     case "Blue":
                         Assert.assertTrue("pool must have blue mana", computerPlayer.getManaPool().getBlue() > 0);
                         computerPlayer.getManaPool().unlockManaType(ManaType.BLUE);
+                        choiceUsed = true;
                         break;
                     case "Black":
                         Assert.assertTrue("pool must have black mana", computerPlayer.getManaPool().getBlack() > 0);
                         computerPlayer.getManaPool().unlockManaType(ManaType.BLACK);
+                        choiceUsed = true;
                         break;
                     case "Red":
                         Assert.assertTrue("pool must have red mana", computerPlayer.getManaPool().getRed() > 0);
                         computerPlayer.getManaPool().unlockManaType(ManaType.RED);
+                        choiceUsed = true;
                         break;
                     case "Green":
                         Assert.assertTrue("pool must have green mana", computerPlayer.getManaPool().getGreen() > 0);
                         computerPlayer.getManaPool().unlockManaType(ManaType.GREEN);
+                        choiceUsed = true;
+                        break;
+                    case "Colorless":
+                        Assert.assertTrue("pool must have colorless mana", computerPlayer.getManaPool().getColorless() > 0);
+                        computerPlayer.getManaPool().unlockManaType(ManaType.COLORLESS);
+                        choiceUsed = true;
                         break;
                     default:
-                        Assert.fail("Unknown choice command for mana unlock: " + needColor);
+                        // go to special block
+                        //Assert.fail("Unknown choice command for mana unlock: " + needColor);
                         break;
                 }
-                choices.remove(0);
-                return true;
+
+                // manual pay by special actions like convoke
+                if (!choiceUsed) {
+                    Map<UUID, SpecialAction> specialActions = game.getState().getSpecialActions().getControlledBy(this.getId(), true);
+                    for (SpecialAction specialAction : specialActions.values()) {
+                        if (specialAction.getRule(true).startsWith(choice)) {
+                            if (specialAction.canActivate(this.getId(), game).canActivate()) {
+                                choices.remove(0);
+                                choiceRemoved = true;
+                                specialAction.setUnpaidMana(unpaid);
+                                if (activateAbility(specialAction, game)) {
+                                    choiceUsed = true;
+                                }
+                            }
+                        }
+                    }
+                }
+
+                if (choiceUsed) {
+                    if (!choiceRemoved) {
+                        choices.remove(0);
+                    }
+                    return true;
+                } else {
+                    Assert.fail("Can't use choice in play mana: " + choice);
+                }
             }
-            Assert.fail(this.getName() + " disabled mana auto-payment, but no choices found for color unlock in pool for unpaid cost: " + unpaid.getText());
+
+            Assert.fail(this.getName() + " disabled mana auto-payment, but no choices found for color unlock in pool or special action for unpaid cost: " + unpaid.getText());
         }
 
         return computerPlayer.playMana(ability, unpaid, promptText, game);

From 708b4e872aef110f2578f43a3808ad891d0d5c1d Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Fri, 19 Jun 2020 13:19:27 +0400
Subject: [PATCH 400/586] Reworked Convoke ability (#768, #6636)

---
 .../cards/abilities/keywords/ConvokeTest.java | 335 +++++++++++++-----
 .../abilities/keyword/ConvokeAbility.java     |  94 +++--
 2 files changed, 311 insertions(+), 118 deletions(-)

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ConvokeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ConvokeTest.java
index b96e9efabc..d492972341 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ConvokeTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ConvokeTest.java
@@ -1,145 +1,298 @@
-
-
 package org.mage.test.cards.abilities.keywords;
 
 import mage.constants.PhaseStep;
 import mage.constants.Zone;
-import mage.filter.common.FilterLandPermanent;
-import mage.game.permanent.Permanent;
-import org.junit.Assert;
 import org.junit.Ignore;
 import org.junit.Test;
-import org.mage.test.serverside.base.CardTestPlayerBase;
+import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps;
 
 /**
- *
- * @author LevelX2
+ * @author JayDi85
  */
 
-public class ConvokeTest extends CardTestPlayerBase {
-
-    /*
-    Test are set to Ignore because the new way to handle this alternate mana payment methods 
-    are not supported yet from AI and getPlayable logic.
-    */
+public class ConvokeTest extends CardTestPlayerBaseWithAIHelps {
 
     @Test
-    @Ignore
-    public void testConvokeTwoCreatures() {
-        /**
-         * Ephemeral Shields   {1}{W}
-         * Instant
-         * Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for or one mana of that creature's color.)
-         * Target creature gains indestructible until end of turn. (Damage and effects that say "destroy" don't destroy it.)
-         */
+    public void test_Playable_NoMana_NoConvoke() {
+        // {2}{R}{R}
+        // Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature’s color.)
+        // Stoke the Flames deals 4 damage to any target.
+        addCard(Zone.HAND, playerA, "Stoke the Flames", 1);
 
-        addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); // must be added because getPlayable does not take Convoke into account
-        
+        checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Stoke the Flames", false);
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+    }
+
+    @Test
+    public void test_Playable_Mana_NoConvoke() {
+        // {2}{R}{R}
+        // Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature’s color.)
+        // Stoke the Flames deals 4 damage to any target.
+        addCard(Zone.HAND, playerA, "Stoke the Flames", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4);
+
+        checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Stoke the Flames", true);
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+    }
+
+    @Test
+    public void test_Playable_NoMana_Convoke() {
+        // {2}{R}{R}
+        // Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature’s color.)
+        // Stoke the Flames deals 4 damage to any target.
+        addCard(Zone.HAND, playerA, "Stoke the Flames", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Goblin Racketeer", 4); // convoke pay
+
+        checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Stoke the Flames", true);
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+    }
+
+    @Test
+    public void test_Playable_Mana_Convoke() {
+        // {2}{R}{R}
+        // Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature’s color.)
+        // Stoke the Flames deals 4 damage to any target.
+        addCard(Zone.HAND, playerA, "Stoke the Flames", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
+        addCard(Zone.BATTLEFIELD, playerA, "Goblin Racketeer", 2); // convoke pay
+
+        checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Stoke the Flames", true);
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+    }
+
+    @Test
+    public void test_Playable_ManaPartly_ConvokePartly() {
+        // {2}{R}{R}
+        // Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature’s color.)
+        // Stoke the Flames deals 4 damage to any target.
+        addCard(Zone.HAND, playerA, "Stoke the Flames", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2 - 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Goblin Racketeer", 2 - 1); // convoke pay
+
+        checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Stoke the Flames", false);
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+    }
+
+    @Test
+    public void test_PlayConvoke_Manual() {
+        // {2}{R}{R}
+        // Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature’s color.)
+        // Stoke the Flames deals 4 damage to any target.
+        addCard(Zone.HAND, playerA, "Stoke the Flames", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
+        addCard(Zone.BATTLEFIELD, playerA, "Goblin Racketeer", 2); // convoke pay
+
+        // use special action to pay (need disabled auto-payment and prepared mana pool)
+        disableManaAutoPayment(playerA);
+        activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}", 2);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Stoke the Flames", playerB);
+        setChoice(playerA, "Red"); // pay 1
+        setChoice(playerA, "Red"); // pay 2
+        setChoice(playerA, "Convoke");
+        addTarget(playerA, "Goblin Racketeer"); // pay 3 as convoke
+        setChoice(playerA, "Convoke");
+        addTarget(playerA, "Goblin Racketeer"); // pay 4 as convoke
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertLife(playerB, 20 - 4);
+    }
+
+    @Test
+    public void test_PlayConvoke_AI_AutoPay() {
+        // {2}{R}{R}
+        // Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature’s color.)
+        // Stoke the Flames deals 4 damage to any target.
+        addCard(Zone.HAND, playerA, "Stoke the Flames", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
+        addCard(Zone.BATTLEFIELD, playerA, "Goblin Racketeer", 2); // convoke pay
+
+        // AI must use special actions to pay as convoke
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Stoke the Flames", playerB);
+
+        //setStrictChooseMode(true); AI must choose targets
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertLife(playerB, 20 - 4);
+    }
+
+    @Test
+    public void test_PlayConvoke_AI_AutoPayAsConvoke() {
+        // {2}{R}{R}
+        // Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature’s color.)
+        // Stoke the Flames deals 4 damage to any target.
+        addCard(Zone.HAND, playerA, "Stoke the Flames", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
+        addCard(Zone.BATTLEFIELD, playerA, "Goblin Racketeer", 2); // convoke pay
+
+        // AI must use special actions to pay as convoke
+        // Current version uses special mana pay as last, after no normal mana available (it can be changed in the future, see playManaHandling)
+        // e.g. it must tap lands 2 times and convoke 2 times
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Stoke the Flames", playerB);
+        addTarget(playerA, "Goblin Racketeer"); // pay 1 as convoke
+        addTarget(playerA, "Goblin Racketeer"); // pay 2 as convoke
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertLife(playerB, 20 - 4);
+    }
+
+    @Test
+    public void test_PlayConvoke_AI_FullPlay() {
+        // {2}{R}{R}
+        // Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature’s color.)
+        // Stoke the Flames deals 4 damage to any target.
+        addCard(Zone.HAND, playerA, "Stoke the Flames", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
+        addCard(Zone.BATTLEFIELD, playerA, "Goblin Racketeer", 2); // convoke pay
+
+        // AI must use special actions to pay as convoke and play card
+        aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA);
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertLife(playerB, 20 - 4);
+    }
+
+    @Test
+    public void test_Other_ConvokeTwoCreatures() {
+        // {1}{W}
+        // Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for or one mana of that creature's color.)
+        // Target creature gains indestructible until end of turn. (Damage and effects that say "destroy" don't destroy it.)
+        addCard(Zone.HAND, playerA, "Ephemeral Shields");
         addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1);
         addCard(Zone.BATTLEFIELD, playerA, "Oreskos Swiftclaw", 1);
 
-        addCard(Zone.HAND, playerA, "Ephemeral Shields");
-
-        addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1);
-        addCard(Zone.HAND, playerB, "Lightning Bolt");
-
-
+        // AI automaticly use convoke to pay
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ephemeral Shields", "Silvercoat Lion");
-        setChoice(playerA, "Yes");
-        addTarget(playerA, "Silvercoat Lion^Oreskos Swiftclaw");
+        addTarget(playerA, "Silvercoat Lion"); // pay 1 as convoke
+        addTarget(playerA, "Oreskos Swiftclaw"); // pay 2 as convoke
 
-        castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Lightning Bolt", "Silvercoat Lion");
+        setStrictChooseMode(true);
         setStopAt(1, PhaseStep.END_TURN);
         execute();
+        assertAllCommandsUsed();
 
         assertLife(playerA, 20);
         assertLife(playerB, 20);
 
-        assertGraveyardCount(playerB, "Lightning Bolt", 1);
-
         assertGraveyardCount(playerA, "Ephemeral Shields", 1);
-        assertPermanentCount(playerA, "Silvercoat Lion", 1); // was indestructible
+        assertPermanentCount(playerA, "Silvercoat Lion", 1);
         assertPermanentCount(playerA, "Oreskos Swiftclaw", 1);
-
-        for (Permanent permanent: currentGame.getBattlefield().getAllActivePermanents(new FilterLandPermanent(), playerA.getId(), currentGame)) {
-            Assert.assertTrue(permanent.getName() + " may not be tapped", !permanent.isTapped());
-        }
     }
 
 
     @Test
-    @Ignore
-    public void testConvokeTwoCreaturesOneWithProtection() {
-        /**
-         * Ephemeral Shields   {1}{W}
-         * Instant
-         * Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for or one mana of that creature's color.)
-         * Target creature gains indestructible until end of turn. (Damage and effects that say "destroy" don't destroy it.)
-         */
-
-        addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); // must be added because getPlayable does not take Convoke into account
-
+    public void test_Other_ConvokeProtection() {
+        // {1}{W}
+        // Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for or one mana of that creature's color.)
+        // Target creature gains indestructible until end of turn. (Damage and effects that say "destroy" don't destroy it.)
+        addCard(Zone.HAND, playerA, "Ephemeral Shields");
         addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1);
+        // Protection from white
         addCard(Zone.BATTLEFIELD, playerA, "Black Knight", 1);
 
-        addCard(Zone.HAND, playerA, "Ephemeral Shields");
-
-        addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1);
-        addCard(Zone.HAND, playerB, "Lightning Bolt");
-
-
+        // convoke must be able to target card with protection (it's no target)
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ephemeral Shields", "Silvercoat Lion");
-        setChoice(playerA, "Yes");
-        addTarget(playerA, "Silvercoat Lion^Black Knight");
+        addTarget(playerA, "Silvercoat Lion");
+        addTarget(playerA, "Black Knight");
 
-        castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Lightning Bolt", "Silvercoat Lion");
+        setStrictChooseMode(true);
         setStopAt(1, PhaseStep.END_TURN);
         execute();
+        assertAllCommandsUsed();
 
         assertLife(playerA, 20);
         assertLife(playerB, 20);
-
-        assertGraveyardCount(playerB, "Lightning Bolt", 1);
-
-        assertGraveyardCount(playerA, "Ephemeral Shields", 1);
-        assertPermanentCount(playerA, "Silvercoat Lion", 1); // was indestructible
-        assertPermanentCount(playerA, "Black Knight", 1);
-        assertTapped("Silvercoat Lion", true);
-        assertTapped("Black Knight", true);
-
-        for (Permanent permanent: currentGame.getBattlefield().getAllActivePermanents(new FilterLandPermanent(), playerA.getId(), currentGame)) {
-            Assert.assertTrue(permanent.getName() + " may not be tapped", !permanent.isTapped());
-        }
     }
 
     @Test
     @Ignore
-    public void testConvokeFromChiefEngineer() {
-        /**
-         * Chief Engineer   {1}{U}
-         * Creature - Vedalken, Artificer
-         * Artifact spells you cast have convoke.
-         */
-
-        // THIS TEST IS NOT FINISHED
-        addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); 
-        addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); // creatures to use for convoek
+    // TODO: fix gain ability for spells to apply in all zones instead stack only or change getPlayable to look ahead and simulate spell on stack (wtf)
+    public void test_Other_ConvokeAsGains() {
+        // {1}{U}
+        // Artifact spells you cast have convoke.
         addCard(Zone.BATTLEFIELD, playerA, "Chief Engineer", 1);
-        
-        addCard(Zone.HAND, playerA, "ARTIFACT TO CAST", 1);
+        //
+        // {2}
+        addCard(Zone.HAND, playerA, "Alpha Myr", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 2);
 
-
-        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "ARTIFACT TO CAST");
-        setChoice(playerA, "Yes");
+        // Chief Engineer gives convoke to Alpha Myr and xmage must see it as playable before put real spell to stack
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Alpha Myr");
+        addTarget(playerA, "Silvercoat Lion");
         addTarget(playerA, "Silvercoat Lion");
 
+        setStrictChooseMode(true);
         setStopAt(1, PhaseStep.END_TURN);
         execute();
+        assertAllCommandsUsed();
 
-        assertLife(playerA, 20);
-        assertLife(playerB, 20);
-
-
+        assertPermanentCount(playerA, "Alpha Myr", 1);
     }
 
+    @Test
+    @Ignore
+    // I don't know how to test it by framework - manual test works fine for HumanPlayer
+    // (he get warning message and can't activate mana abilities after convoke)
+    public void test_Other_CantUseConvokeBeforeManaAbilities() {
+        // https://github.com/magefree/mage/issues/768
+
+        // {6}
+        // Convoke
+        addCard(Zone.HAND, playerA, "Will-Forged Golem", 1);
+        //
+        // {2}{G}
+        // Create two 1/1 colorless Eldrazi Scion creature tokens. They have “Sacrifice this creature: Add {C}.”
+        addCard(Zone.HAND, playerA, "Call the Scions", 2);
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 3 * 2);
+
+        // prepare scions
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Call the Scions");
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Call the Scions");
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
+        checkPermanentCount("scions", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Eldrazi Scion", 4);
+
+        // test case 1 - playable abilities must not show it as playable (not work, cause we don't known real payment order before payment)
+        //checkPlayableAbility("can't use convoke", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Will-Forged Golem", false);
+
+        // test case 2 - it's in playable list, but mana abilities can't be activated after convoke pay
+        //castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Will-Forged Golem");
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+    }
 }
\ No newline at end of file
diff --git a/Mage/src/main/java/mage/abilities/keyword/ConvokeAbility.java b/Mage/src/main/java/mage/abilities/keyword/ConvokeAbility.java
index 19a50c3e32..3aa290bba5 100644
--- a/Mage/src/main/java/mage/abilities/keyword/ConvokeAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/ConvokeAbility.java
@@ -1,17 +1,17 @@
 package mage.abilities.keyword;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-import java.util.UUID;
 import mage.Mana;
 import mage.ObjectColor;
 import mage.abilities.Ability;
 import mage.abilities.SpecialAction;
 import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.costs.mana.ActivationManaAbilityStep;
 import mage.abilities.costs.mana.AlternateManaPaymentAbility;
 import mage.abilities.costs.mana.ManaCost;
+import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
 import mage.abilities.effects.OneShotEffect;
+import mage.abilities.hint.ValueHint;
+import mage.abilities.mana.ManaOptions;
 import mage.choices.Choice;
 import mage.choices.ChoiceColor;
 import mage.constants.AbilityType;
@@ -19,62 +19,67 @@ import mage.constants.ManaType;
 import mage.constants.Outcome;
 import mage.constants.Zone;
 import mage.filter.common.FilterControlledCreaturePermanent;
-import mage.filter.common.FilterCreaturePermanent;
 import mage.filter.predicate.Predicates;
 import mage.filter.predicate.mageobject.ColorPredicate;
 import mage.filter.predicate.permanent.TappedPredicate;
 import mage.game.Game;
 import mage.game.events.GameEvent;
 import mage.game.permanent.Permanent;
+import mage.game.stack.Spell;
 import mage.players.ManaPool;
 import mage.players.Player;
 import mage.target.Target;
 import mage.target.common.TargetControlledCreaturePermanent;
 
-/*
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.UUID;
+
+/**
  * 502.46. Convoke
- *
+ * <p>
  * 502.46a Convoke is a static ability that functions while the spell is on the stack. "Convoke"
  * means "As an additional cost to play this spell, you may tap any number of untapped creatures
  * you control. Each creature tapped this way reduces the cost to play this spell by {1} or by
  * one mana of any of that creature's colors." Using the convoke ability follows the rules for
  * paying additional costs in rules 409.1b and 4091f-h.
- *
+ * <p>
  * Example: You play Guardian of Vitu-Ghazi, a spell with convoke that costs {3}{G}{W}. You announce
  * that you're going to tap an artifact creature, a red creature, and a green-and-white creature to
  * help pay for it. The artifact creature and the red creature each reduce the spell's cost by {1}.
  * You choose whether the green-white creature reduces the spell's cost by {1}, {G}, or {W}. Then
  * the creatures become tapped as you pay Guardian of Vitu-Ghazi's cost.
- *
+ * <p>
  * 502.46b Convoke can't reduce the cost to play a spell to less than 0.
- *
+ * <p>
  * 502.46c Multiple instances of convoke on the same spell are redundant.
- *
+ * <p>
  * You can tap only untapped creatures you control to reduce the cost of a spell with convoke
  * that you play.
- *
+ * <p>
  * While playing a spell with convoke, if you control a creature that taps to produce mana, you
  * can either tap it for mana or tap it to reduce the cost of the spell, but not both.
- *
+ * <p>
  * If you tap a multicolored creature to reduce the cost of a spell with convoke, you reduce
  * the cost by {1} or by one mana of your choice of any of that creature's colors.
- *
+ * <p>
  * Convoke doesn't change a spell's mana cost or converted mana cost.
  *
- *
- * @author LevelX2
+ * @author LevelX2, JayDi85
  */
 public class ConvokeAbility extends SimpleStaticAbility implements AlternateManaPaymentAbility {
 
-    private static final FilterCreaturePermanent filterUntapped = new FilterCreaturePermanent();
+    private static final FilterControlledCreaturePermanent filterUntapped = new FilterControlledCreaturePermanent();
 
     static {
         filterUntapped.add(Predicates.not(TappedPredicate.instance));
     }
 
     public ConvokeAbility() {
-        super(Zone.STACK, null);
+        super(Zone.ALL, null); // all AlternateManaPaymentAbility must use ALL zone to calculate playable abilities
         this.setRuleAtTheTop(true);
+        this.addHint(new ValueHint("Untapped creatures you control", new PermanentsOnBattlefieldCount(filterUntapped)));
     }
 
     public ConvokeAbility(final ConvokeAbility ability) {
@@ -86,14 +91,25 @@ public class ConvokeAbility extends SimpleStaticAbility implements AlternateMana
         return new ConvokeAbility(this);
     }
 
+    @Override
+    public String getRule() {
+        return "Convoke <i>(Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature's color.)</i>";
+    }
+
+    @Override
+    public ActivationManaAbilityStep useOnActivationManaAbilityStep() {
+        return ActivationManaAbilityStep.AFTER;
+    }
+
     @Override
     public void addSpecialAction(Ability source, Game game, ManaCost unpaid) {
         Player controller = game.getPlayer(source.getControllerId());
         if (controller != null && game.getBattlefield().contains(filterUntapped, controller.getId(), 1, game)) {
             if (source.getAbilityType() == AbilityType.SPELL) {
-                SpecialAction specialAction = new ConvokeSpecialAction(unpaid);
+                SpecialAction specialAction = new ConvokeSpecialAction(unpaid, this);
                 specialAction.setControllerId(source.getControllerId());
                 specialAction.setSourceId(source.getSourceId());
+
                 // create filter for possible creatures to tap
                 FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent();
                 filter.add(Predicates.not(TappedPredicate.instance));
@@ -117,7 +133,7 @@ public class ConvokeAbility extends SimpleStaticAbility implements AlternateMana
                     filter.add(Predicates.or(colorPredicates));
                 }
                 Target target = new TargetControlledCreaturePermanent(1, 1, filter, true);
-                target.setTargetName("creature to convoke");
+                target.setTargetName("tap creature card as convoke's pay");
                 specialAction.addTarget(target);
                 if (specialAction.canActivate(source.getControllerId(), game).canActivate()) {
                     game.getState().getSpecialActions().add(specialAction);
@@ -127,15 +143,36 @@ public class ConvokeAbility extends SimpleStaticAbility implements AlternateMana
     }
 
     @Override
-    public String getRule() {
-        return "Convoke <i>(Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature's color.)</i>";
+    public ManaOptions getManaOptions(Ability source, Game game, ManaCost unpaid) {
+        ManaOptions options = new ManaOptions();
+        FilterControlledCreaturePermanent filterBasic = new FilterControlledCreaturePermanent();
+
+        // each creature can give {1} or color mana
+        game.getBattlefield().getActivePermanents(filterBasic, source.getControllerId(), source.getSourceId(), game)
+                .stream()
+                .filter(permanent -> !permanent.isTapped())
+                .forEach(permanent -> {
+                    ManaOptions permMana = new ManaOptions();
+                    permMana.add(Mana.GenericMana(1));
+                    for (ObjectColor color : permanent.getColor(game).getColors()) {
+                        if (color.isBlack()) permMana.add(Mana.BlackMana(1));
+                        if (color.isBlue()) permMana.add(Mana.BlueMana(1));
+                        if (color.isGreen()) permMana.add(Mana.GreenMana(1));
+                        if (color.isRed()) permMana.add(Mana.RedMana(1));
+                        if (color.isWhite()) permMana.add(Mana.WhiteMana(1));
+                    }
+                    options.addMana(permMana);
+                });
+
+        options.removeDuplicated();
+        return options;
     }
 }
 
 class ConvokeSpecialAction extends SpecialAction {
 
-    public ConvokeSpecialAction(ManaCost unpaid) {
-        super(Zone.ALL, true);
+    public ConvokeSpecialAction(ManaCost unpaid, AlternateManaPaymentAbility manaAbility) {
+        super(Zone.ALL, manaAbility);
         setRuleVisible(false);
         this.addEffect(new ConvokeEffect(unpaid));
     }
@@ -157,7 +194,7 @@ class ConvokeEffect extends OneShotEffect {
     public ConvokeEffect(ManaCost unpaid) {
         super(Outcome.Benefit);
         this.unpaid = unpaid;
-        this.staticText = "Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {C} or one mana of that creature's color.)";
+        this.staticText = "Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature's color.)";
     }
 
     public ConvokeEffect(final ConvokeEffect effect) {
@@ -173,7 +210,8 @@ class ConvokeEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player controller = game.getPlayer(source.getControllerId());
-        if (controller != null) {
+        Spell spell = game.getStack().getSpell(source.getSourceId());
+        if (controller != null && spell != null) {
             for (UUID creatureId : this.getTargetPointer().getTargets(game, source)) {
                 Permanent perm = game.getPermanent(creatureId);
                 if (perm == null) {
@@ -225,8 +263,10 @@ class ConvokeEffect extends OneShotEffect {
                     }
                     game.fireEvent(GameEvent.getEvent(GameEvent.EventType.CONVOKED, perm.getId(), source.getSourceId(), source.getControllerId()));
                     game.informPlayers("Convoke: " + controller.getLogName() + " taps " + perm.getLogName() + " to pay one " + manaName + " mana");
-                }
 
+                    // can't use mana abilities after that (convoke cost must be payed after mana abilities only)
+                    spell.setCurrentActivatingManaAbilitiesStep(ActivationManaAbilityStep.AFTER);
+                }
             }
             return true;
         }

From fb1e284960d3f3b5b056025507e018901b2f43d1 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Fri, 19 Jun 2020 13:20:32 +0400
Subject: [PATCH 401/586] Reworked Assist ability (#768)

---
 .../cards/abilities/keywords/AssistTest.java  | 138 ++++++++++++++++++
 .../mage/abilities/keyword/AssistAbility.java |  97 ++++++++++--
 2 files changed, 221 insertions(+), 14 deletions(-)
 create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/AssistTest.java

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/AssistTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/AssistTest.java
new file mode 100644
index 0000000000..7210f12e69
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/AssistTest.java
@@ -0,0 +1,138 @@
+package org.mage.test.cards.abilities.keywords;
+
+import mage.constants.PhaseStep;
+import mage.constants.Zone;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps;
+
+/**
+ * @author JayDi85
+ */
+
+public class AssistTest extends CardTestPlayerBaseWithAIHelps {
+
+    @Test
+    public void test_Playable_NoMana_NoAssist() {
+        // {7}{G}
+        // Assist (Another player can pay up to {7} of this spell's cost.)
+        addCard(Zone.HAND, playerA, "Charging Binox", 1);
+
+        checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Charging Binox", false);
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+    }
+
+    @Test
+    public void test_Playable_Mana_NoAssist() {
+        // {7}{G}
+        // Assist (Another player can pay up to {7} of this spell's cost.)
+        addCard(Zone.HAND, playerA, "Charging Binox", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 8);
+
+        checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Charging Binox", true);
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+    }
+
+    @Test
+    public void test_Playable_NoMana_Assist() {
+        // {7}{G}
+        // Assist (Another player can pay up to {7} of this spell's cost.)
+        addCard(Zone.HAND, playerA, "Charging Binox", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 6);
+        addCard(Zone.BATTLEFIELD, playerB, "Forest", 2); // assist pay
+
+        checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Charging Binox", true);
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+    }
+
+    @Test
+    public void test_Playable_Mana_Assist() {
+        // {7}{G}
+        // Assist (Another player can pay up to {7} of this spell's cost.)
+        addCard(Zone.HAND, playerA, "Charging Binox", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 8);
+        addCard(Zone.BATTLEFIELD, playerB, "Forest", 2); // assist pay
+
+        checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Charging Binox", true);
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+    }
+
+    @Test
+    public void test_Playable_ManaPartly_AssistPartly() {
+        // {7}{G}
+        // Assist (Another player can pay up to {7} of this spell's cost.)
+        addCard(Zone.HAND, playerA, "Charging Binox", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 2);
+        addCard(Zone.BATTLEFIELD, playerB, "Forest", 2); // assist pay
+
+        checkPlayableAbility("all", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Charging Binox", false);
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+    }
+
+    @Test
+    public void test_PlayAssist_Manual() {
+        // {7}{G}
+        // Assist (Another player can pay up to {7} of this spell's cost.)
+        addCard(Zone.HAND, playerA, "Charging Binox", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 8 - 2);
+        addCard(Zone.BATTLEFIELD, playerB, "Forest", 2); // assist pay
+
+        // disabled auto-payment and prepare mana pool to control payment
+        disableManaAutoPayment(playerA);
+        activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {G}", 6);
+        // cast and assist
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Charging Binox");
+        setChoice(playerA, "Green", 6); // normal pay x6
+        setChoice(playerA, "Assist"); // activate assist
+        addTarget(playerA, playerB); // player to assist
+        setChoice(playerB, "X=2"); // can pay (auto-pay from B to A's mana pool as colorless x2)
+        setChoice(playerA, "Colorless", 2 - 1); // assist pay x2, but 1 mana was unlocked in assist code (wtf)
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerA, "Charging Binox", 1);
+    }
+
+    @Test
+    public void test_PlayAssist_AI_MustIgnoreAssist() {
+        // {7}{G}
+        // Assist (Another player can pay up to {7} of this spell's cost.)
+        addCard(Zone.HAND, playerA, "Charging Binox", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 8 - 2);
+        addCard(Zone.BATTLEFIELD, playerB, "Forest", 2); // assist pay
+
+        // AI must ignore assist
+        checkPlayableAbility("playable", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Charging Binox", true);
+        aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA);
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertHandCount(playerA, "Charging Binox", 1);
+        assertTappedCount("Forest", false, 8); // no mana used
+    }
+}
\ No newline at end of file
diff --git a/Mage/src/main/java/mage/abilities/keyword/AssistAbility.java b/Mage/src/main/java/mage/abilities/keyword/AssistAbility.java
index 7da5d3a447..ae4e837194 100644
--- a/Mage/src/main/java/mage/abilities/keyword/AssistAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/AssistAbility.java
@@ -5,21 +5,37 @@ import mage.abilities.Ability;
 import mage.abilities.SpecialAction;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.costs.Cost;
+import mage.abilities.costs.mana.ActivationManaAbilityStep;
 import mage.abilities.costs.mana.AlternateManaPaymentAbility;
 import mage.abilities.costs.mana.ManaCost;
 import mage.abilities.effects.OneShotEffect;
+import mage.abilities.mana.ManaOptions;
 import mage.constants.*;
 import mage.filter.FilterPlayer;
 import mage.game.Game;
+import mage.game.stack.Spell;
 import mage.players.ManaPool;
 import mage.players.Player;
 import mage.target.Target;
 import mage.target.TargetPlayer;
 import mage.util.ManaUtil;
 
-/*
- * @author emerald000
+import java.util.UUID;
+
+/**
+ * 702.131. Assist
+ * <p>
+ * 702.131a Assist is a static ability that modifies the rules of paying for the spell with assist (see rules 601.2g-h).
+ * If the total cost to cast a spell with assist includes a generic mana component, before you activate mana
+ * abilities while casting it, you may choose another player. That player has a chance to activate mana abilities.
+ * Once that player chooses not to activate any more mana abilities, you have a chance to activate mana abilities.
+ * Before you begin to pay the total cost of the spell, the player you chose may pay for any amount of the generic
+ * mana in the spell’s total cost.
+ *
+ * @author emerald000, JayDi85
  */
+
+
 public class AssistAbility extends SimpleStaticAbility implements AlternateManaPaymentAbility {
 
     private static final FilterPlayer filter = new FilterPlayer("another player");
@@ -29,7 +45,7 @@ public class AssistAbility extends SimpleStaticAbility implements AlternateManaP
     }
 
     public AssistAbility() {
-        super(Zone.STACK, null);
+        super(Zone.ALL, null);
         this.setRuleAtTheTop(true);
     }
 
@@ -42,14 +58,24 @@ public class AssistAbility extends SimpleStaticAbility implements AlternateManaP
         return new AssistAbility(this);
     }
 
+    @Override
+    public String getRule() {
+        return "Assist <i>(Another player can help pay the generic mana of this spell's cost.)</i>";
+    }
+
+    @Override
+    public ActivationManaAbilityStep useOnActivationManaAbilityStep() {
+        return ActivationManaAbilityStep.BEFORE;
+    }
+
     @Override
     public void addSpecialAction(Ability source, Game game, ManaCost unpaid) {
         Player controller = game.getPlayer(source.getControllerId());
         if (controller != null
                 && source.getAbilityType() == AbilityType.SPELL
                 && unpaid.getMana().getGeneric() >= 1
-                && game.getState().getValue(source.getSourceId().toString() + game.getState().getZoneChangeCounter(source.getSourceId())) == null) {
-            SpecialAction specialAction = new AssistSpecialAction(unpaid);
+                && game.getState().getValue(source.getSourceId().toString() + game.getState().getZoneChangeCounter(source.getSourceId()) + "_assisted") == null) {
+            SpecialAction specialAction = new AssistSpecialAction(unpaid, this);
             specialAction.setControllerId(source.getControllerId());
             specialAction.setSourceId(source.getSourceId());
             Target target = new TargetPlayer(1, 1, true, filter);
@@ -61,15 +87,47 @@ public class AssistAbility extends SimpleStaticAbility implements AlternateManaP
     }
 
     @Override
-    public String getRule() {
-        return "Assist <i>(Another player can help pay the generic mana of this spell's cost.)</i>";
+    public ManaOptions getManaOptions(Ability source, Game game, ManaCost unpaid) {
+        ManaOptions options = new ManaOptions();
+        if (unpaid.getMana().getGeneric() == 0) {
+            // nothing to pay
+            return options;
+        }
+
+        // AI can't use assist (can't ask another player to help), maybe in teammode it can be enabled, but tests must works all the time
+        Player controller = game.getPlayer(source.getControllerId());
+        if (controller != null && !controller.isTestMode() && !controller.isHuman()) {
+            return options;
+        }
+
+        // search opponents who can help with generic pay
+        int opponentCanPayMax = 0;
+        for (UUID opponentId : game.getOpponents(source.getControllerId())) {
+            Player opponent = game.getPlayer(opponentId);
+            if (opponent != null) {
+                // basic and pool, but no coditional mana
+                ManaOptions availableMana = opponent.getManaAvailable(game);
+                availableMana.addMana(opponent.getManaPool().getMana());
+                for (Mana mana : availableMana) {
+                    if (mana.count() > 0) {
+                        opponentCanPayMax = Math.max(opponentCanPayMax, mana.count());
+                    }
+                }
+            }
+        }
+
+        if (opponentCanPayMax > 0) {
+            options.addMana(Mana.GenericMana(Math.min(unpaid.getMana().getGeneric(), opponentCanPayMax)));
+        }
+
+        return options;
     }
 }
 
 class AssistSpecialAction extends SpecialAction {
 
-    AssistSpecialAction(ManaCost unpaid) {
-        super(Zone.ALL, true);
+    AssistSpecialAction(ManaCost unpaid, AlternateManaPaymentAbility manaAbility) {
+        super(Zone.ALL, manaAbility);
         setRuleVisible(false);
         this.addEffect(new AssistEffect(unpaid));
     }
@@ -108,17 +166,28 @@ class AssistEffect extends OneShotEffect {
     public boolean apply(Game game, Ability source) {
         Player controller = game.getPlayer(source.getControllerId());
         Player targetPlayer = game.getPlayer(this.getTargetPointer().getFirst(game, source));
-        if (controller != null && targetPlayer != null) {
-            int amountToPay = targetPlayer.announceXMana(0, unpaid.getMana().getGeneric(), "How much mana to pay?", game, source);
+        Spell spell = game.getStack().getSpell(source.getSourceId());
+        if (controller != null && spell != null && targetPlayer != null) {
+            // AI can't assist other players, maybe for teammates only (but tests must work as normal)
+            int amountToPay = 0;
+            if (targetPlayer.isHuman() || targetPlayer.isTestMode()) {
+                amountToPay = targetPlayer.announceXMana(0, unpaid.getMana().getGeneric(),
+                        "How much mana to pay as assist for " + controller.getName() + "?", game, source);
+            }
+
             if (amountToPay > 0) {
                 Cost cost = ManaUtil.createManaCost(amountToPay, false);
                 if (cost.pay(source, game, source.getSourceId(), targetPlayer.getId(), false)) {
                     ManaPool manaPool = controller.getManaPool();
                     manaPool.addMana(Mana.ColorlessMana(amountToPay), game, source);
-                    manaPool.unlockManaType(ManaType.COLORLESS);
-                    game.informPlayers(targetPlayer.getLogName() + " paid {" + amountToPay + "}.");
-                    game.getState().setValue(source.getSourceId().toString() + game.getState().getZoneChangeCounter(source.getSourceId()), true);
+                    manaPool.unlockManaType(ManaType.COLORLESS); // it's unlock mana for one use/click, but it can gives more
+                    game.informPlayers(targetPlayer.getLogName() + " paid {" + amountToPay + "} for " + controller.getLogName());
+                    game.getState().setValue(source.getSourceId().toString() + game.getState().getZoneChangeCounter(source.getSourceId()) + "_assisted", true);
                 }
+
+                // assist must be used before activating mana abilities, so no need to switch step after usage
+                // (mana and other special abilities can be used after assist)
+                //spell.setCurrentActivatingManaAbilitiesStep(ActivationManaAbilityStep.NORMAL);
             }
             return true;
         }

From 56a5fd58519be465ee5871bc8450c2277fd9669f Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Fri, 19 Jun 2020 13:21:03 +0400
Subject: [PATCH 402/586] Reworked Delve ability (#768)

---
 .../cards/abilities/keywords/DelveTest.java   | 84 ++++++++++++++++++
 .../mage/abilities/keyword/DelveAbility.java  | 86 +++++++++++++------
 2 files changed, 143 insertions(+), 27 deletions(-)
 create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DelveTest.java

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DelveTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DelveTest.java
new file mode 100644
index 0000000000..fe9a453382
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DelveTest.java
@@ -0,0 +1,84 @@
+package org.mage.test.cards.abilities.keywords;
+
+import mage.constants.PhaseStep;
+import mage.constants.Zone;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps;
+
+/**
+ * @author JayDi85
+ */
+
+public class DelveTest extends CardTestPlayerBaseWithAIHelps {
+
+    // no simple playable tests for delve, it's same as ConvokeTest
+
+    @Test
+    public void test_PlayDelve_Manual() {
+        // {4}{U}{U} creature
+        // Delve (Each card you exile from your graveyard while casting this spell pays for {1}.)
+        addCard(Zone.HAND, playerA, "Ethereal Forager", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
+        addCard(Zone.GRAVEYARD, playerA, "Balduvian Bears", 4); // delve pay
+
+        // use special action to pay (need disabled auto-payment and prepared mana pool)
+        disableManaAutoPayment(playerA);
+        activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}", 2);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ethereal Forager");
+        setChoice(playerA, "Blue"); // pay 1
+        setChoice(playerA, "Blue"); // pay 2
+        // delve can be payed in test only by one card
+        setChoice(playerA, "Exile cards");
+        setChoice(playerA, "Balduvian Bears"); // pay 3 as delve
+        setChoice(playerA, "Exile cards");
+        setChoice(playerA, "Balduvian Bears"); // pay 4 as delve
+        setChoice(playerA, "Exile cards");
+        setChoice(playerA, "Balduvian Bears"); // pay 5 as delve
+        setChoice(playerA, "Exile cards");
+        setChoice(playerA, "Balduvian Bears"); // pay 6 as delve
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerA, "Ethereal Forager", 1);
+    }
+
+    @Test
+    public void test_PlayDelve_AI_AutoPay() {
+        // {4}{U}{U} creature
+        // Delve (Each card you exile from your graveyard while casting this spell pays for {1}.)
+        addCard(Zone.HAND, playerA, "Ethereal Forager", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
+        addCard(Zone.GRAVEYARD, playerA, "Balduvian Bears", 4); // delve pay
+
+        // AI must use special actions to pay as delve
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ethereal Forager");
+
+        //setStrictChooseMode(true); AI must choose targets
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerA, "Ethereal Forager", 1);
+    }
+
+    @Test
+    public void test_PlayDelve_AI_FullPlay() {
+        // {4}{U}{U} creature
+        // Delve (Each card you exile from your graveyard while casting this spell pays for {1}.)
+        addCard(Zone.HAND, playerA, "Ethereal Forager", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
+        addCard(Zone.GRAVEYARD, playerA, "Balduvian Bears", 4); // delve pay
+
+        aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA);
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerA, "Ethereal Forager", 1);
+    }
+}
\ No newline at end of file
diff --git a/Mage/src/main/java/mage/abilities/keyword/DelveAbility.java b/Mage/src/main/java/mage/abilities/keyword/DelveAbility.java
index 2855262cdb..6f8002ed78 100644
--- a/Mage/src/main/java/mage/abilities/keyword/DelveAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/DelveAbility.java
@@ -5,9 +5,14 @@ import mage.abilities.Ability;
 import mage.abilities.SpecialAction;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.costs.common.ExileFromGraveCost;
+import mage.abilities.costs.mana.ActivationManaAbilityStep;
 import mage.abilities.costs.mana.AlternateManaPaymentAbility;
 import mage.abilities.costs.mana.ManaCost;
+import mage.abilities.dynamicvalue.DynamicValue;
+import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount;
 import mage.abilities.effects.OneShotEffect;
+import mage.abilities.hint.ValueHint;
+import mage.abilities.mana.ManaOptions;
 import mage.cards.Card;
 import mage.cards.Cards;
 import mage.cards.CardsImpl;
@@ -17,6 +22,7 @@ import mage.constants.Outcome;
 import mage.constants.Zone;
 import mage.filter.FilterCard;
 import mage.game.Game;
+import mage.game.stack.Spell;
 import mage.players.ManaPool;
 import mage.players.Player;
 import mage.target.common.TargetCardInYourGraveyard;
@@ -25,35 +31,42 @@ import mage.util.CardUtil;
 import java.util.List;
 
 /**
- * 702.65. Delve 702.65a Delve is a static ability that functions while the
- * spell with delve is on the stack. “Delve” means “For each generic mana in
- * this spell's total cost, you may exile a card from your graveyard rather than
- * pay that mana.” The delve ability isn't an additional or alternative cost and
- * applies only after the total cost of the spell with delve is determined.
- * 702.65b Multiple instances of delve on the same spell are redundant.
+ * 702.65. Delve
  * <p>
- * The rules for delve have changed slightly since it was last in an expansion.
- * Previously, delve reduced the cost to cast a spell. Under the current rules,
- * you exile cards from your graveyard at the same time you pay the spell's
- * cost. Exiling a card this way is simply another way to pay that cost. * Delve
- * doesn't change a spell's mana cost or converted mana cost. For example, Dead
- * Drop's converted mana cost is 10 even if you exiled three cards to cast it. *
- * You can't exile cards to pay for the colored mana requirements of a spell
- * with delve. * You can't exile more cards than the generic mana requirement of
- * a spell with delve. For example, you can't exile more than nine cards from
- * your graveyard to cast Dead Drop. * Because delve isn't an alternative cost,
- * it can be used in conjunction with alternative costs.
+ * 702.65a Delve is a static ability that functions while the spell with delve is on the stack. “Delve” means “For
+ * each generic mana in this spell’s total cost, you may exile a card from your graveyard rather than pay that mana.”
+ * <p>
+ * 702.65b The delve ability isn’t an additional or alternative cost and applies only after the total cost of the spell
+ * with delve is determined.
+ * <p>
+ * 702.65c Multiple instances of delve on the same spell are redundant.
+ * <p>
+ * The rules for delve have changed slightly since it was last in an expansion. Previously, delve reduced the cost
+ * to cast a spell. Under the current rules, you exile cards from your graveyard at the same time you pay the spell’s
+ * cost. Exiling a card this way is simply another way to pay that cost. (This is similar to the change made to
+ * convoke for the Magic 2015 Core Set.)
+ * <p>
+ * You can’t exile cards to pay for the colored mana requirements of a spell with delve.
+ * <p>
+ * You can’t exile more cards than the generic mana requirement of a spell with delve. For example, you can’t exile more
+ * than nine cards from your graveyard to cast Dead Drop.
+ * <p>
+ * Because delve isn’t an alternative cost, it can be used in conjunction with alternative costs.
  *
- * @author LevelX2
+ * @author LevelX2, JayDi85
  * <p>
  * TODO: Change card exiling to a way to pay mana costs, now it's maybe not
  * possible to pay costs from effects that increase the mana costs.
+ * If it real bug then possible fix: choose cards on apply like convoke and improvise, not as cost
  */
 public class DelveAbility extends SimpleStaticAbility implements AlternateManaPaymentAbility {
 
+    private static final DynamicValue cardsInGraveyard = new CardsInControllerGraveyardCount();
+
     public DelveAbility() {
-        super(Zone.STACK, null);
+        super(Zone.ALL, null);
         this.setRuleAtTheTop(true);
+        this.addHint(new ValueHint("Cards in your graveyard", cardsInGraveyard));
     }
 
     public DelveAbility(final DelveAbility ability) {
@@ -70,12 +83,17 @@ public class DelveAbility extends SimpleStaticAbility implements AlternateManaPa
         return "Delve <i>(Each card you exile from your graveyard while casting this spell pays for {1}.)</i>";
     }
 
+    @Override
+    public ActivationManaAbilityStep useOnActivationManaAbilityStep() {
+        return ActivationManaAbilityStep.AFTER;
+    }
+
     @Override
     public void addSpecialAction(Ability source, Game game, ManaCost unpaid) {
         Player controller = game.getPlayer(source.getControllerId());
         if (controller != null && !controller.getGraveyard().isEmpty()) {
-            if (unpaid.getMana().getGeneric() > 0 && source.getAbilityType() == AbilityType.SPELL) {
-                SpecialAction specialAction = new DelveSpecialAction();
+            if (source.getAbilityType() == AbilityType.SPELL && unpaid.getMana().getGeneric() > 0) {
+                SpecialAction specialAction = new DelveSpecialAction(this);
                 specialAction.setControllerId(source.getControllerId());
                 specialAction.setSourceId(source.getSourceId());
                 int unpaidAmount = unpaid.getMana().getGeneric();
@@ -84,19 +102,30 @@ public class DelveAbility extends SimpleStaticAbility implements AlternateManaPa
                 }
                 specialAction.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(
                         0, Math.min(controller.getGraveyard().size(), unpaidAmount),
-                        new FilterCard("cards to exile for delve's pay from your graveyard"), true)));
+                        new FilterCard("cards from your graveyard"), true)));
                 if (specialAction.canActivate(source.getControllerId(), game).canActivate()) {
                     game.getState().getSpecialActions().add(specialAction);
                 }
             }
         }
     }
+
+    @Override
+    public ManaOptions getManaOptions(Ability source, Game game, ManaCost unpaid) {
+        ManaOptions options = new ManaOptions();
+        Player controller = game.getPlayer(source.getControllerId());
+        int graveCount = cardsInGraveyard.calculate(game, source, null);
+        if (controller != null && graveCount > 0) {
+            options.addMana(Mana.GenericMana(Math.min(unpaid.getMana().getGeneric(), graveCount)));
+        }
+        return options;
+    }
 }
 
 class DelveSpecialAction extends SpecialAction {
 
-    public DelveSpecialAction() {
-        super(Zone.ALL, true);
+    public DelveSpecialAction(AlternateManaPaymentAbility manaAbility) {
+        super(Zone.ALL, manaAbility);
         this.addEffect(new DelveEffect());
     }
 
@@ -129,9 +158,9 @@ class DelveEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player controller = game.getPlayer(source.getControllerId());
-        if (controller != null) {
+        Spell spell = game.getStack().getSpell(source.getSourceId());
+        if (controller != null && spell != null) {
             ExileFromGraveCost exileFromGraveCost = (ExileFromGraveCost) source.getCosts().get(0);
-
             List<Card> exiledCards = exileFromGraveCost.getExiledCards();
             if (!exiledCards.isEmpty()) {
                 Cards toDelve = new CardsImpl();
@@ -139,7 +168,7 @@ class DelveEffect extends OneShotEffect {
                     toDelve.add(card);
                 }
                 ManaPool manaPool = controller.getManaPool();
-                manaPool.addMana(new Mana(0, 0, 0, 0, 0, 0, 0, toDelve.size()), game, source);
+                manaPool.addMana(Mana.ColorlessMana(toDelve.size()), game, source);
                 manaPool.unlockManaType(ManaType.COLORLESS);
                 String keyString = CardUtil.getCardZoneString("delvedCards", source.getSourceId(), game);
                 @SuppressWarnings("unchecked")
@@ -149,6 +178,9 @@ class DelveEffect extends OneShotEffect {
                 } else {
                     delvedCards.addAll(toDelve);
                 }
+
+                // can't use mana abilities after that (delve cost must be payed after mana abilities only)
+                spell.setCurrentActivatingManaAbilitiesStep(ActivationManaAbilityStep.AFTER);
             }
             return true;
         }

From ce3f9e80d5a3f55f02ee4d8aeea2264d71ac9acb Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Fri, 19 Jun 2020 13:22:05 +0400
Subject: [PATCH 403/586] Reworked Improvise ability (#768)

---
 .../abilities/keywords/ImproviseTest.java     | 79 +++++++++++++++++++
 .../abilities/keyword/ImproviseAbility.java   | 74 ++++++++++++-----
 2 files changed, 132 insertions(+), 21 deletions(-)
 create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ImproviseTest.java

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ImproviseTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ImproviseTest.java
new file mode 100644
index 0000000000..e0712c2c41
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ImproviseTest.java
@@ -0,0 +1,79 @@
+package org.mage.test.cards.abilities.keywords;
+
+import mage.constants.PhaseStep;
+import mage.constants.Zone;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps;
+
+/**
+ * @author JayDi85
+ */
+
+public class ImproviseTest extends CardTestPlayerBaseWithAIHelps {
+
+    // no simple playable tests for improvise, it's same as ConvokeTest
+
+    @Test
+    public void test_PlayImprovise_Manual() {
+        // {5}{U} creature
+        // Improvise (Your artifacts can help cast this spell. Each artifact you tap after you’re done activating mana abilities pays for {1}.)
+        addCard(Zone.HAND, playerA, "Bastion Inventor", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 4);
+        addCard(Zone.BATTLEFIELD, playerA, "Alpha Myr", 2); // improvise pay
+
+        // use special action to pay (need disabled auto-payment and prepared mana pool)
+        disableManaAutoPayment(playerA);
+        activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}", 4);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bastion Inventor");
+        setChoice(playerA, "Blue", 4); // pay 1-4
+        // improvise pay by one card
+        setChoice(playerA, "Improvise");
+        addTarget(playerA, "Alpha Myr"); // pay 5 as improvise
+        setChoice(playerA, "Improvise");
+        addTarget(playerA, "Alpha Myr"); // pay 6 as improvise
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerA, "Bastion Inventor", 1);
+    }
+
+    @Test
+    public void test_PlayImprovise_AI_AutoPay() {
+        // {5}{U} creature
+        // Improvise (Your artifacts can help cast this spell. Each artifact you tap after you’re done activating mana abilities pays for {1}.)
+        addCard(Zone.HAND, playerA, "Bastion Inventor", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 4);
+        addCard(Zone.BATTLEFIELD, playerA, "Alpha Myr", 2); // improvise pay
+
+        // AI must use special actions to pay as delve
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bastion Inventor");
+
+        //setStrictChooseMode(true); AI must choose targets
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerA, "Bastion Inventor", 1);
+    }
+
+    @Test
+    public void test_PlayImprovise_AI_FullPlay() {
+        // {5}{U} creature
+        // Improvise (Your artifacts can help cast this spell. Each artifact you tap after you’re done activating mana abilities pays for {1}.)
+        addCard(Zone.HAND, playerA, "Bastion Inventor", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 4);
+        addCard(Zone.BATTLEFIELD, playerA, "Alpha Myr", 2); // improvise pay
+
+        aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA);
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerA, "Bastion Inventor", 1);
+    }
+}
\ No newline at end of file
diff --git a/Mage/src/main/java/mage/abilities/keyword/ImproviseAbility.java b/Mage/src/main/java/mage/abilities/keyword/ImproviseAbility.java
index 7418145817..5d7d15a4e3 100644
--- a/Mage/src/main/java/mage/abilities/keyword/ImproviseAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/ImproviseAbility.java
@@ -1,23 +1,21 @@
-/*
- * To change this license header, choose License Headers in Project Properties.
- * To change this template file, choose Tools | Templates
- * and open the template in the editor.
- */
 package mage.abilities.keyword;
 
-import java.util.UUID;
 import mage.Mana;
 import mage.abilities.Ability;
 import mage.abilities.SpecialAction;
 import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.costs.mana.ActivationManaAbilityStep;
 import mage.abilities.costs.mana.AlternateManaPaymentAbility;
 import mage.abilities.costs.mana.ManaCost;
+import mage.abilities.dynamicvalue.DynamicValue;
+import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
 import mage.abilities.effects.OneShotEffect;
+import mage.abilities.hint.ValueHint;
+import mage.abilities.mana.ManaOptions;
 import mage.constants.AbilityType;
 import mage.constants.ManaType;
 import mage.constants.Outcome;
 import mage.constants.Zone;
-import mage.filter.common.FilterArtifactPermanent;
 import mage.filter.common.FilterControlledArtifactPermanent;
 import mage.filter.predicate.Predicates;
 import mage.filter.predicate.permanent.TappedPredicate;
@@ -29,20 +27,36 @@ import mage.players.Player;
 import mage.target.Target;
 import mage.target.common.TargetControlledPermanent;
 
+import java.util.UUID;
+
 /**
- * @author LevelX2
+ * 702.125. Improvise
+ * <p>
+ * 702.125a Improvise is a static ability that functions while the spell with improvise is on the stack. “Improvise”
+ * means “For each generic mana in this spell’s total cost, you may tap an untapped artifact you control rather
+ * than pay that mana.”
+ * <p>
+ * 702.125b The improvise ability isn’t an additional or alternative cost and applies only after the total cost of
+ * the spell with improvise is determined.
+ * <p>
+ * 702.125c Multiple instances of improvise on the same spell are redundant.
+ *
+ * @author LevelX2, JayDi85
  */
 public class ImproviseAbility extends SimpleStaticAbility implements AlternateManaPaymentAbility {
 
-    private static final FilterArtifactPermanent filterUntapped = new FilterArtifactPermanent();
+    private static final FilterControlledArtifactPermanent filterUntapped = new FilterControlledArtifactPermanent("untapped artifact you control");
 
     static {
         filterUntapped.add(Predicates.not(TappedPredicate.instance));
     }
 
+    private static final DynamicValue untappedCount = new PermanentsOnBattlefieldCount(filterUntapped);
+
     public ImproviseAbility() {
-        super(Zone.STACK, null);
+        super(Zone.ALL, null);
         this.setRuleAtTheTop(true);
+        this.addHint(new ValueHint("Untapped artifacts you control", untappedCount));
     }
 
     public ImproviseAbility(final ImproviseAbility ability) {
@@ -54,19 +68,29 @@ public class ImproviseAbility extends SimpleStaticAbility implements AlternateMa
         return new ImproviseAbility(this);
     }
 
+
+    @Override
+    public String getRule() {
+        return "Improvise <i>(Your artifacts can help cast this spell. Each artifact you tap after you're done activating mana abilities pays for {1}.)</i>";
+    }
+
+    @Override
+    public ActivationManaAbilityStep useOnActivationManaAbilityStep() {
+        return ActivationManaAbilityStep.AFTER;
+    }
+
     @Override
     public void addSpecialAction(Ability source, Game game, ManaCost unpaid) {
         Player controller = game.getPlayer(source.getControllerId());
-        if (controller != null && game.getBattlefield().contains(filterUntapped, controller.getId(), 1, game)) {
+        int canPayCount = untappedCount.calculate(game, source, null);
+        if (controller != null && canPayCount > 0) {
             if (source.getAbilityType() == AbilityType.SPELL && unpaid.getMana().getGeneric() > 0) {
-                SpecialAction specialAction = new ImproviseSpecialAction(unpaid);
+                SpecialAction specialAction = new ImproviseSpecialAction(unpaid, this);
                 specialAction.setControllerId(source.getControllerId());
                 specialAction.setSourceId(source.getSourceId());
                 // create filter for possible artifacts to tap
-                FilterControlledArtifactPermanent filter = new FilterControlledArtifactPermanent();
-                filter.add(Predicates.not(TappedPredicate.instance));
-                Target target = new TargetControlledPermanent(1, unpaid.getMana().getGeneric(), filter, true);
-                target.setTargetName("artifact to Improvise");
+                Target target = new TargetControlledPermanent(1, unpaid.getMana().getGeneric(), filterUntapped, true);
+                target.setTargetName("artifact to tap as Improvise's pay");
                 specialAction.addTarget(target);
                 if (specialAction.canActivate(source.getControllerId(), game).canActivate()) {
                     game.getState().getSpecialActions().add(specialAction);
@@ -76,15 +100,21 @@ public class ImproviseAbility extends SimpleStaticAbility implements AlternateMa
     }
 
     @Override
-    public String getRule() {
-        return "Improvise <i>(Your artifacts can help cast this spell. Each artifact you tap after you're done activating mana abilities pays for {1}.)</i>";
+    public ManaOptions getManaOptions(Ability source, Game game, ManaCost unpaid) {
+        ManaOptions options = new ManaOptions();
+        Player controller = game.getPlayer(source.getControllerId());
+        int canPayCount = untappedCount.calculate(game, source, null);
+        if (controller != null && canPayCount > 0) {
+            options.addMana(Mana.GenericMana(Math.min(unpaid.getMana().getGeneric(), canPayCount)));
+        }
+        return options;
     }
 }
 
 class ImproviseSpecialAction extends SpecialAction {
 
-    public ImproviseSpecialAction(ManaCost unpaid) {
-        super(Zone.ALL, true);
+    public ImproviseSpecialAction(ManaCost unpaid, AlternateManaPaymentAbility manaAbility) {
+        super(Zone.ALL, manaAbility);
         setRuleVisible(false);
         this.addEffect(new ImproviseEffect(unpaid));
     }
@@ -136,7 +166,9 @@ class ImproviseEffect extends OneShotEffect {
                     if (!game.isSimulation()) {
                         game.informPlayers("Improvise: " + controller.getLogName() + " taps " + perm.getLogName() + " to pay {1}");
                     }
-                    spell.setDoneActivatingManaAbilities(true);
+
+                    // can't use mana abilities after that (improvise cost must be payed after mana abilities only)
+                    spell.setCurrentActivatingManaAbilitiesStep(ActivationManaAbilityStep.AFTER);
                 }
 
             }

From 5fdf9607aca1bbfa854631b88edc91d2949728d4 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Fri, 19 Jun 2020 13:23:18 +0400
Subject: [PATCH 404/586] Added test for #5121

---
 .../cards/continuous/CommandersCastTest.java  | 54 +++++++++++++++++--
 1 file changed, 50 insertions(+), 4 deletions(-)

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/CommandersCastTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/CommandersCastTest.java
index b217f12dda..c8ea023d90 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/CommandersCastTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/CommandersCastTest.java
@@ -17,7 +17,6 @@ public class CommandersCastTest extends CardTestCommander4Players {
         addCard(Zone.COMMAND, playerA, "Balduvian Bears", 1); // {1}{G}, 2/2, commander
         addCard(Zone.BATTLEFIELD, playerA, "Forest", 2);
 
-        // showCommand("commanders", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears");
 
         setStopAt(1, PhaseStep.END_TURN);
@@ -70,9 +69,7 @@ public class CommandersCastTest extends CardTestCommander4Players {
     public void test_PlayAsLandOneTime() {
         addCard(Zone.COMMAND, playerA, "Academy Ruins", 1);
 
-        // showAvaileableAbilities("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
         playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Academy Ruins");
-        //castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Academy Ruins");
 
         setStopAt(1, PhaseStep.END_TURN);
         setStrictChooseMode(true);
@@ -258,7 +255,6 @@ public class CommandersCastTest extends CardTestCommander4Players {
         addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 2);
 
         // cast overload
-        // showAvaileableAbilities("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Weapon Surge with overload");
         setChoice(playerA, "Yes"); // move to command zone
         checkAbility("after", 1, PhaseStep.BEGIN_COMBAT, playerA, "Balduvian Bears", FirstStrikeAbility.class, true);
@@ -268,4 +264,54 @@ public class CommandersCastTest extends CardTestCommander4Players {
         execute();
         assertAllCommandsUsed();
     }
+
+    @Test
+    public void test_ExileWithDelvePayAndReturn() {
+        // https://github.com/magefree/mage/issues/5121
+        // Exiling your commander from your graveyard should give you the option to put it in command zone
+        // We were playing in a restarted-by-Karn game (if that mattered), and a player who exiled their
+        // commander from graveyard via Delve was not given the opportunity to place it in the command zone.
+        // Instead, it went directly to the exiled zone.
+
+        // disable auto-payment for delve test
+        disableManaAutoPayment(playerA);
+
+        // commander
+        addCard(Zone.COMMAND, playerA, "Balduvian Bears", 1); // {1}{G}, 2/2, commander
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 2);
+        //
+        addCard(Zone.HAND, playerA, "Lightning Bolt", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
+        //
+        // {4}{U}{U} creature
+        // Delve (Each card you exile from your graveyard while casting this spell pays for {1}.)
+        addCard(Zone.HAND, playerA, "Ethereal Forager", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 5); // one from delve
+
+        // prepare commander and put it to graveyard
+        activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {G}", 2);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears");
+        setChoice(playerA, "Green", 2); // pay
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+        //
+        activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Balduvian Bears");
+        setChoice(playerA, "Red"); // pay
+        setChoice(playerA, "No"); // leave in graveyard
+
+        // use commander as delve pay
+        activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}", 5);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ethereal Forager");
+        setChoice(playerA, "Blue", 5); // pay normal
+        setChoice(playerA, "Exile cards"); // pay delve
+        setChoice(playerA, "Balduvian Bears");
+        setChoice(playerA, "Yes"); // move to command zone
+
+        setStopAt(1, PhaseStep.END_TURN);
+        setStrictChooseMode(true);
+        execute();
+        assertAllCommandsUsed();
+
+        assertCommandZoneCount(playerA, "Balduvian Bears", 1);
+    }
 }

From d069bbd544b4c0ca2dc9d82bac0be0778e141adb Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Fri, 19 Jun 2020 13:24:52 +0400
Subject: [PATCH 405/586] Code cleanup and test fixes

---
 .../src/mage/cards/d/DigThroughTime.java      |  10 +-
 .../src/mage/cards/i/IllusionistsBracers.java |  23 ++-
 .../src/mage/cards/s/StokeTheFlames.java      |  10 +-
 .../cards/copy/CleverImpersonatorTest.java    |  18 +-
 .../triggers/damage/SyrCarahTheBoldTest.java  |  31 ++--
 .../serverside/base/MageTestPlayerBase.java   |  11 +-
 .../base/impl/CardTestPlayerAPIImpl.java      | 171 +++++++++---------
 Mage/src/main/java/mage/game/GameImpl.java    |  25 +--
 Mage/src/main/java/mage/util/ManaUtil.java    |   7 +-
 9 files changed, 160 insertions(+), 146 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/d/DigThroughTime.java b/Mage.Sets/src/mage/cards/d/DigThroughTime.java
index fbc8168103..5a652e5ceb 100644
--- a/Mage.Sets/src/mage/cards/d/DigThroughTime.java
+++ b/Mage.Sets/src/mage/cards/d/DigThroughTime.java
@@ -1,7 +1,5 @@
-
 package mage.cards.d;
 
-import java.util.UUID;
 import mage.abilities.dynamicvalue.common.StaticValue;
 import mage.abilities.effects.common.LookLibraryAndPickControllerEffect;
 import mage.abilities.keyword.DelveAbility;
@@ -11,19 +9,19 @@ import mage.constants.CardType;
 import mage.constants.Zone;
 import mage.filter.FilterCard;
 
+import java.util.UUID;
+
 /**
- *
  * @author emerald000
  */
 public final class DigThroughTime extends CardImpl {
 
     public DigThroughTime(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{6}{U}{U}");
-
+        super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{6}{U}{U}");
 
         // Delve
         this.addAbility(new DelveAbility());
-        
+
         // Look at the top seven cards of your library. Put two of them into your hand and the rest on the bottom of your library in any order.
         this.getSpellAbility().addEffect(new LookLibraryAndPickControllerEffect(StaticValue.get(7), false, StaticValue.get(2), new FilterCard(), Zone.LIBRARY, false, false));
     }
diff --git a/Mage.Sets/src/mage/cards/i/IllusionistsBracers.java b/Mage.Sets/src/mage/cards/i/IllusionistsBracers.java
index 257ca55ff2..6b98993ed8 100644
--- a/Mage.Sets/src/mage/cards/i/IllusionistsBracers.java
+++ b/Mage.Sets/src/mage/cards/i/IllusionistsBracers.java
@@ -1,7 +1,5 @@
-
 package mage.cards.i;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.TriggeredAbilityImpl;
 import mage.abilities.costs.mana.GenericManaCost;
@@ -12,8 +10,8 @@ import mage.abilities.mana.ActivatedManaAbilityImpl;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.SubType;
 import mage.constants.Outcome;
+import mage.constants.SubType;
 import mage.constants.Zone;
 import mage.game.Game;
 import mage.game.events.GameEvent;
@@ -22,18 +20,19 @@ import mage.game.permanent.Permanent;
 import mage.game.stack.StackAbility;
 import mage.players.Player;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class IllusionistsBracers extends CardImpl {
 
     public IllusionistsBracers(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{2}");
+        super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}");
         this.subtype.add(SubType.EQUIPMENT);
 
         // Whenever an ability of equipped creature is activated, if it isn't a mana ability, copy that ability. You may choose new targets for the copy.
-        this.addAbility(new AbilityActivatedTriggeredAbility());
+        this.addAbility(new IllusionistsBracersTriggeredAbility());
 
         // Equip 3
         this.addAbility(new EquipAbility(Outcome.BoostCreature, new GenericManaCost(3)));
@@ -49,19 +48,19 @@ public final class IllusionistsBracers extends CardImpl {
     }
 }
 
-class AbilityActivatedTriggeredAbility extends TriggeredAbilityImpl {
+class IllusionistsBracersTriggeredAbility extends TriggeredAbilityImpl {
 
-    AbilityActivatedTriggeredAbility() {
+    IllusionistsBracersTriggeredAbility() {
         super(Zone.BATTLEFIELD, new CopyActivatedAbilityEffect());
     }
 
-    AbilityActivatedTriggeredAbility(final AbilityActivatedTriggeredAbility ability) {
+    IllusionistsBracersTriggeredAbility(final IllusionistsBracersTriggeredAbility ability) {
         super(ability);
     }
 
     @Override
-    public AbilityActivatedTriggeredAbility copy() {
-        return new AbilityActivatedTriggeredAbility(this);
+    public IllusionistsBracersTriggeredAbility copy() {
+        return new IllusionistsBracersTriggeredAbility(this);
     }
 
     @Override
@@ -72,7 +71,7 @@ class AbilityActivatedTriggeredAbility extends TriggeredAbilityImpl {
     @Override
     public boolean checkTrigger(GameEvent event, Game game) {
         Permanent equipment = game.getPermanent(this.getSourceId());
-        if (equipment != null  && equipment.isAttachedTo(event.getSourceId())) {
+        if (equipment != null && equipment.isAttachedTo(event.getSourceId())) {
             StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId());
             if (!(stackAbility.getStackAbility() instanceof ActivatedManaAbilityImpl)) {
                 Effect effect = this.getEffects().get(0);
diff --git a/Mage.Sets/src/mage/cards/s/StokeTheFlames.java b/Mage.Sets/src/mage/cards/s/StokeTheFlames.java
index 39cd33c84c..bffdfd33b8 100644
--- a/Mage.Sets/src/mage/cards/s/StokeTheFlames.java
+++ b/Mage.Sets/src/mage/cards/s/StokeTheFlames.java
@@ -1,7 +1,5 @@
-
 package mage.cards.s;
 
-import java.util.UUID;
 import mage.abilities.effects.common.DamageTargetEffect;
 import mage.abilities.keyword.ConvokeAbility;
 import mage.cards.CardImpl;
@@ -9,19 +7,19 @@ import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.target.common.TargetAnyTarget;
 
+import java.util.UUID;
+
 /**
- *
  * @author Quercitron
  */
 public final class StokeTheFlames extends CardImpl {
 
     public StokeTheFlames(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{R}{R}");
-
+        super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R}{R}");
 
         // Convoke
         this.addAbility(new ConvokeAbility());
-        
+
         // Stoke the Flames deals 4 damage to any target.
         this.getSpellAbility().addEffect(new DamageTargetEffect(4));
         this.getSpellAbility().addTarget(new TargetAnyTarget());
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/CleverImpersonatorTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/CleverImpersonatorTest.java
index 1cb16170ab..e6b6eb16ef 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/CleverImpersonatorTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/CleverImpersonatorTest.java
@@ -1,4 +1,3 @@
-
 package org.mage.test.cards.copy;
 
 import mage.constants.CardType;
@@ -9,7 +8,6 @@ import org.junit.Test;
 import org.mage.test.serverside.base.CardTestPlayerBase;
 
 /**
- *
  * @author LevelX2
  */
 public class CleverImpersonatorTest extends CardTestPlayerBase {
@@ -54,22 +52,29 @@ public class CleverImpersonatorTest extends CardTestPlayerBase {
      */
     @Test
     public void testCopyPlaneswalker() {
-        addCard(Zone.BATTLEFIELD, playerA, "Island", 4);
-
         // You may have Clever Impersonator enter the battlefield as a copy of any nonland permanent on the battlefield.
         addCard(Zone.HAND, playerA, "Clever Impersonator", 1); // {2}{U}{U}
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 4);
 
         // +2: Each player discards a card.
         // -X: Return target nonlegendary creature with converted mana cost X from your graveyard to the battlefield.
         // -8: You get an emblem with "Whenever a creature dies, return it to the battlefield under your control at the beginning of the next end step.";
         addCard(Zone.BATTLEFIELD, playerB, "Liliana, Defiant Necromancer", 1);
+        //
+        addCard(Zone.HAND, playerA, "Balduvian Bears", 1);
+        addCard(Zone.HAND, playerB, "Balduvian Bears", 1);
 
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Clever Impersonator");
-        setChoice(playerA, "Liliana, Defiant Necromancer");
+        setChoice(playerA, "Yes"); // make copy
+        setChoice(playerA, "Liliana, Defiant Necromancer"); // copy target
         activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+2: Each player discards a card");
+        addTarget(playerA, "Balduvian Bears"); // discard
+        addTarget(playerB, "Balduvian Bears"); // discard
 
+        setStrictChooseMode(true);
         setStopAt(1, PhaseStep.BEGIN_COMBAT);
         execute();
+        assertAllCommandsUsed();
 
         assertHandCount(playerA, "Clever Impersonator", 0);
         assertCounterCount(playerB, "Liliana, Defiant Necromancer", CounterType.LOYALTY, 3);  // 3
@@ -157,8 +162,7 @@ public class CleverImpersonatorTest extends CardTestPlayerBase {
      * Reported bug: could not use Clever Impersonator to copy Dawn's Reflection
      */
     @Test
-    public void dawnsReflectionCopiedByImpersonator()
-    {
+    public void dawnsReflectionCopiedByImpersonator() {
         String impersonator = "Clever Impersonator";
         String dReflection = "Dawn's Reflection";
 
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/damage/SyrCarahTheBoldTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/damage/SyrCarahTheBoldTest.java
index a8d5eb50e5..5962c91cc2 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/damage/SyrCarahTheBoldTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/damage/SyrCarahTheBoldTest.java
@@ -55,32 +55,31 @@ public class SyrCarahTheBoldTest extends CardTestPlayerBase {
     public void test_DamageWithCopyAbility() {
         removeAllCardsFromLibrary(playerA);
         removeAllCardsFromHand(playerA);
+
         // When Syr Carah, the Bold or an instant or sorcery spell you control deals damage to a player, exile the top card of your library. You may play that card this turn.
         // {T}: Syr Carah deals 1 damage to any target.
         addCard(Zone.BATTLEFIELD, playerA, "Syr Carah, the Bold", 1);
-        //
-        addCard(Zone.LIBRARY, playerA, "Swamp", 5);
-        //
-        // {T}: Embermage Goblin deals 1 damage to any target.
-        addCard(Zone.BATTLEFIELD, playerB, "Embermage Goblin", 1);
+        addCard(Zone.LIBRARY, playerA, "Balduvian Bears", 2);
         //
         // Whenever an ability of equipped creature is activated, if it isn't a mana ability, copy that ability. You may choose new targets for the copy.
         // Equip 3
-        addCard(Zone.BATTLEFIELD, playerB, "Illusionist's Bracers", 1);
-        addCard(Zone.BATTLEFIELD, playerB, "Island", 3);
+        addCard(Zone.BATTLEFIELD, playerA, "Illusionist's Bracers", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 3);
 
-        // equip to copy abilities
-        // showAvaileableAbilities("abils", 2, PhaseStep.PRECOMBAT_MAIN, playerB);
-        activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Equip {3}", "Embermage Goblin");
-        setChoice(playerB, "No"); // no new target
+        // prepare equip
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip {3}", "Syr Carah, the Bold");
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+        checkExileCount("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears", 0);
 
-        // 3 - 2x damage (copy), but no trigger
-        // java.lang.ClassCastException: mage.game.stack.StackAbility cannot be cast to mage.game.stack.Spell
-        activateAbility(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "{T}: {source} deals", playerA);
-        checkLife("damage 3", 2, PhaseStep.END_TURN, playerA, 20 - 1 - 1);
+        // activate damage - 2x damage with copy
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: {source} deals", playerB);
+        setChoice(playerA, "No"); // no new target for copy
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+        checkLife("damage 2", 1, PhaseStep.PRECOMBAT_MAIN, playerB, 20 - 1 - 1);
+        checkExileCount("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears", 2);
 
         setStrictChooseMode(true);
-        setStopAt(3, PhaseStep.END_TURN);
+        setStopAt(1, PhaseStep.END_TURN);
         execute();
         assertAllCommandsUsed();
     }
diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java
index 5b5b758af4..4efe7837f2 100644
--- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java
+++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java
@@ -351,6 +351,13 @@ public abstract class MageTestPlayerBase {
         return new TestPlayer(new TestComputerPlayer(name, rangeOfInfluence));
     }
 
+    /**
+     * Raise error on any miss choices/targets setup in tests (if AI try to make decision itself instead of user defined actions)
+     * If you want to disable mana auto-payment (e.g. to simulate user clicks on mana pool or special mana) then call
+     * disableManaAutoPayment()
+     *
+     * @param enable
+     */
     protected void setStrictChooseMode(boolean enable) {
         if (playerA != null) playerA.setChooseStrictMode(enable);
         if (playerB != null) playerB.setChooseStrictMode(enable);
@@ -403,8 +410,8 @@ public abstract class MageTestPlayerBase {
 // custom card with global abilities list to init (can contains abilities per card name)
 class CustomTestCard extends CardImpl {
 
-    static private Map<String, Abilities<Ability>> abilitiesList = new HashMap<>(); // card name -> abilities
-    static private Map<String, SpellAbility> spellAbilitiesList = new HashMap<>(); // card name -> spell ability
+    static private final Map<String, Abilities<Ability>> abilitiesList = new HashMap<>(); // card name -> abilities
+    static private final Map<String, SpellAbility> spellAbilitiesList = new HashMap<>(); // card name -> spell ability
 
     static void addCustomAbility(String cardName, SpellAbility spellAbility, Ability ability) {
         if (!abilitiesList.containsKey(cardName)) {
diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
index 57a4c26ac6..b23d039a1c 100644
--- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
+++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
@@ -1,13 +1,5 @@
 package org.mage.test.serverside.base.impl;
 
-import java.io.FileNotFoundException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.UUID;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.stream.Collectors;
 import mage.MageObject;
 import mage.Mana;
 import mage.ObjectColor;
@@ -43,6 +35,15 @@ import org.mage.test.player.TestPlayer;
 import org.mage.test.serverside.base.CardTestAPI;
 import org.mage.test.serverside.base.MageTestPlayerBase;
 
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.UUID;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
 /**
  * API for test initialization and asserting the test results.
  *
@@ -273,7 +274,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
             }
         }
         Assert.assertFalse("Wrong stop command on " + this.stopOnTurn + " / " + this.stopAtStep + " (" + this.stopAtStep.getIndex() + ")"
-                + " (found actions after stop on " + maxTurn + " / " + maxPhase + ")",
+                        + " (found actions after stop on " + maxTurn + " / " + maxPhase + ")",
                 (maxTurn > this.stopOnTurn) || (maxTurn == this.stopOnTurn && maxPhase > this.stopAtStep.getIndex()));
 
         for (Player player : currentGame.getPlayers().values()) {
@@ -506,8 +507,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Add a card to specified zone of specified player.
      *
      * @param gameZone {@link mage.constants.Zone} to add cards to.
-     * @param player {@link Player} to add cards for. Use either playerA or
-     * playerB.
+     * @param player   {@link Player} to add cards for. Use either playerA or
+     *                 playerB.
      * @param cardName Card name in string format.
      */
     @Override
@@ -519,10 +520,10 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Add any amount of cards to specified zone of specified player.
      *
      * @param gameZone {@link mage.constants.Zone} to add cards to.
-     * @param player {@link Player} to add cards for. Use either playerA or
-     * playerB.
+     * @param player   {@link Player} to add cards for. Use either playerA or
+     *                 playerB.
      * @param cardName Card name in string format.
-     * @param count Amount of cards to be added.
+     * @param count    Amount of cards to be added.
      */
     @Override
     public void addCard(Zone gameZone, TestPlayer player, String cardName, int count) {
@@ -533,13 +534,13 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Add any amount of cards to specified zone of specified player.
      *
      * @param gameZone {@link mage.constants.Zone} to add cards to.
-     * @param player {@link Player} to add cards for. Use either playerA or
-     * playerB.
+     * @param player   {@link Player} to add cards for. Use either playerA or
+     *                 playerB.
      * @param cardName Card name in string format.
-     * @param count Amount of cards to be added.
-     * @param tapped In case gameZone is Battlefield, determines whether
-     * permanent should be tapped. In case gameZone is other than Battlefield,
-     * {@link IllegalArgumentException} is thrown
+     * @param count    Amount of cards to be added.
+     * @param tapped   In case gameZone is Battlefield, determines whether
+     *                 permanent should be tapped. In case gameZone is other than Battlefield,
+     *                 {@link IllegalArgumentException} is thrown
      */
     @Override
     public void addCard(Zone gameZone, TestPlayer player, String cardName, int count, boolean tapped) {
@@ -620,7 +621,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Set player's initial life count.
      *
      * @param player {@link Player} to set life count for.
-     * @param life Life count to set.
+     * @param life   Life count to set.
      */
     @Override
     public void setLife(TestPlayer player, int life) {
@@ -697,7 +698,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert player's life count after test execution.
      *
      * @param player {@link Player} to get life for comparison.
-     * @param life Expected player's life to compare with.
+     * @param life   Expected player's life to compare with.
      */
     @Override
     public void assertLife(Player player, int life) throws AssertionError {
@@ -714,14 +715,14 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * params 3b. all: there is at least one creature with the cardName with the
      * different p\t params
      *
-     * @param player {@link Player} to get creatures for comparison.
-     * @param cardName Card name to compare with.
-     * @param power Expected power to compare with.
+     * @param player    {@link Player} to get creatures for comparison.
+     * @param cardName  Card name to compare with.
+     * @param power     Expected power to compare with.
      * @param toughness Expected toughness to compare with.
-     * @param scope {@link mage.filter.Filter.ComparisonScope} Use ANY, if you
-     * want "at least one creature with given name should have specified p\t"
-     * Use ALL, if you want "all creature with gived name should have specified
-     * p\t"
+     * @param scope     {@link mage.filter.Filter.ComparisonScope} Use ANY, if you
+     *                  want "at least one creature with given name should have specified p\t"
+     *                  Use ALL, if you want "all creature with gived name should have specified
+     *                  p\t"
      */
     @Override
     public void assertPowerToughness(Player player, String cardName, int power, int toughness, Filter.ComparisonScope scope)
@@ -811,8 +812,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * @param cardName
      * @param ability
      * @param mustHave true if creature should contain ability, false if it
-     * should NOT contain it instead
-     * @param count number of permanents with that ability
+     *                 should NOT contain it instead
+     * @param count    number of permanents with that ability
      * @throws AssertionError
      */
     public void assertAbility(Player player, String cardName, Ability ability, boolean mustHave, int count) throws AssertionError {
@@ -845,7 +846,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert permanent count under player's control.
      *
      * @param player {@link Player} which permanents should be counted.
-     * @param count Expected count.
+     * @param count  Expected count.
      */
     @Override
     public void assertPermanentCount(Player player, int count) throws AssertionError {
@@ -861,9 +862,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     /**
      * Assert permanent count under player's control.
      *
-     * @param player {@link Player} which permanents should be counted.
+     * @param player   {@link Player} which permanents should be counted.
      * @param cardName Name of the cards that should be counted.
-     * @param count Expected count.
+     * @param count    Expected count.
      */
     @Override
     public void assertPermanentCount(Player player, String cardName, int count) throws AssertionError {
@@ -913,8 +914,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert counter count on a permanent
      *
      * @param cardName Name of the cards that should be counted.
-     * @param type Type of the counter that should be counted.
-     * @param count Expected count.
+     * @param type     Type of the counter that should be counted.
+     * @param count    Expected count.
      */
     public void assertCounterCount(String cardName, CounterType type, int count) throws AssertionError {
         this.assertCounterCount(null, cardName, type, count);
@@ -937,8 +938,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert counter count on a card in exile
      *
      * @param cardName Name of the cards that should be counted.
-     * @param type Type of the counter that should be counted.
-     * @param count Expected count.
+     * @param type     Type of the counter that should be counted.
+     * @param count    Expected count.
      */
     public void assertCounterOnExiledCardCount(String cardName, CounterType type, int count) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -961,8 +962,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert counter count on a player
      *
      * @param player The player whos counters should be counted.
-     * @param type Type of the counter that should be counted.
-     * @param count Expected count.
+     * @param type   Type of the counter that should be counted.
+     * @param count  Expected count.
      */
     public void assertCounterCount(Player player, CounterType type, int count) throws AssertionError {
         Assert.assertEquals("(Battlefield) Counter counts are not equal (" + player.getName() + ':' + type + ')', count, player.getCounters().getCount(type));
@@ -972,7 +973,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert whether a permanent is a specified type or not
      *
      * @param cardName Name of the permanent that should be checked.
-     * @param type A type to test for
+     * @param type     A type to test for
      * @param mustHave true if creature should have type, false if it should not
      */
     public void assertType(String cardName, CardType type, boolean mustHave) throws AssertionError {
@@ -997,8 +998,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert whether a permanent is a specified type
      *
      * @param cardName Name of the permanent that should be checked.
-     * @param type A type to test for
-     * @param subType a subtype to test for
+     * @param type     A type to test for
+     * @param subType  a subtype to test for
      */
     public void assertType(String cardName, CardType type, SubType subType) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -1013,7 +1014,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert whether a permanent is not a specified type
      *
      * @param cardName Name of the permanent that should be checked.
-     * @param type A type to test for
+     * @param type     A type to test for
      */
     public void assertNotType(String cardName, CardType type) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -1025,7 +1026,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert whether a permanent is not a specified subtype
      *
      * @param cardName Name of the permanent that should be checked.
-     * @param subType a subtype to test for
+     * @param subType  a subtype to test for
      */
     public void assertNotSubtype(String cardName, SubType subType) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -1038,10 +1039,10 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     /**
      * Assert permanent color
      *
-     * @param player player to check
-     * @param cardName card name on battlefield from player
+     * @param player       player to check
+     * @param cardName     card name on battlefield from player
      * @param searchColors colors list with searchable values
-     * @param mustHave must or not must have that colors
+     * @param mustHave     must or not must have that colors
      */
     public void assertColor(Player player, String cardName, ObjectColor searchColors, boolean mustHave) {
         //Assert.assertNotEquals("", cardName);
@@ -1076,7 +1077,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert whether a permanent is tapped or not
      *
      * @param cardName Name of the permanent that should be checked.
-     * @param tapped Whether the permanent is tapped or not
+     * @param tapped   Whether the permanent is tapped or not
      */
     public void assertTapped(String cardName, boolean tapped) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -1103,8 +1104,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert whether X permanents of the same name are tapped or not.
      *
      * @param cardName Name of the permanent that should be checked.
-     * @param tapped Whether the permanent is tapped or not
-     * @param count The amount of this permanents that should be tapped
+     * @param tapped   Whether the permanent is tapped or not
+     * @param count    The amount of this permanents that should be tapped
      */
     public void assertTappedCount(String cardName, boolean tapped, int count) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -1126,7 +1127,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     /**
      * Assert whether a permanent is attacking or not
      *
-     * @param cardName Name of the permanent that should be checked.
+     * @param cardName  Name of the permanent that should be checked.
      * @param attacking Whether the permanent is attacking or not
      */
     public void assertAttacking(String cardName, boolean attacking) throws AssertionError {
@@ -1148,7 +1149,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert card count in player's hand.
      *
      * @param player {@link Player} who's hand should be counted.
-     * @param count Expected count.
+     * @param count  Expected count.
      */
     public void assertHandCount(Player player, int count) throws AssertionError {
         int actual = currentGame.getPlayer(player.getId()).getHand().size();
@@ -1158,9 +1159,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     /**
      * Assert card count in player's hand.
      *
-     * @param player {@link Player} who's hand should be counted.
+     * @param player   {@link Player} who's hand should be counted.
      * @param cardName Name of the cards that should be counted.
-     * @param count Expected count.
+     * @param count    Expected count.
      */
     public void assertHandCount(Player player, String cardName, int count) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -1208,7 +1209,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert card count in player's graveyard.
      *
      * @param player {@link Player} who's graveyard should be counted.
-     * @param count Expected count.
+     * @param count  Expected count.
      */
     public void assertGraveyardCount(Player player, int count) throws AssertionError {
         int actual = currentGame.getPlayer(player.getId()).getGraveyard().size();
@@ -1219,7 +1220,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert card count in exile.
      *
      * @param cardName Name of the cards that should be counted.
-     * @param count Expected count.
+     * @param count    Expected count.
      */
     public void assertExileCount(String cardName, int count) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -1257,9 +1258,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     /**
      * Assert card count in player's exile.
      *
-     * @param owner {@link Player} who's exile should be counted.
+     * @param owner    {@link Player} who's exile should be counted.
      * @param cardName Name of the cards that should be counted.
-     * @param count Expected count.
+     * @param count    Expected count.
      */
     public void assertExileCount(Player owner, String cardName, int count) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -1278,9 +1279,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     /**
      * Assert card count in player's graveyard.
      *
-     * @param player {@link Player} who's graveyard should be counted.
+     * @param player   {@link Player} who's graveyard should be counted.
      * @param cardName Name of the cards that should be counted.
-     * @param count Expected count.
+     * @param count    Expected count.
      */
     public void assertGraveyardCount(Player player, String cardName, int count) throws AssertionError {
         assertAliaseSupportInActivateCommand(cardName, true);
@@ -1299,7 +1300,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert library card count.
      *
      * @param player {@link Player} who's library should be counted.
-     * @param count Expected count.
+     * @param count  Expected count.
      */
     public void assertLibraryCount(Player player, int count) throws AssertionError {
         List<Card> libraryList = player.getLibrary().getCards(currentGame);
@@ -1310,9 +1311,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     /**
      * Assert specific card count in player's library.
      *
-     * @param player {@link Player} who's library should be counted.
+     * @param player   {@link Player} who's library should be counted.
      * @param cardName Name of the cards that should be counted.
-     * @param count Expected count.
+     * @param count    Expected count.
      */
     public void assertLibraryCount(Player player, String cardName, int count) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -1350,6 +1351,12 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
         Assert.assertEquals("(Targets of " + player.getName() + ") Count are not equal (found " + player.getTargets() + ")", count, player.getTargets().size());
     }
 
+    /**
+     * Raise error on any unused commands, choices or targets
+     * If you want to test that ability can't be activated then use call checkPlayableAbility()
+     *
+     * @throws AssertionError
+     */
     public void assertAllCommandsUsed() throws AssertionError {
         for (Player player : currentGame.getPlayers().values()) {
             TestPlayer testPlayer = (TestPlayer) player;
@@ -1493,8 +1500,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * @param player
      * @param cardName
      * @param targetName for modes you can add "mode=3" before target name,
-     * multiple targets can be seperated by ^, not target marks as
-     * TestPlayer.NO_TARGET
+     *                   multiple targets can be seperated by ^, not target marks as
+     *                   TestPlayer.NO_TARGET
      */
     public void castSpell(int turnNum, PhaseStep step, TestPlayer player, String cardName, String targetName) {
         //Assert.assertNotEquals("", cardName);
@@ -1517,8 +1524,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * @param step
      * @param player
      * @param cardName
-     * @param targetName for modal spells add the mode to the name e.g.
-     * "mode=2SilvercoatLion^mode3=PillarfieldOx"
+     * @param targetName   for modal spells add the mode to the name e.g.
+     *                     "mode=2SilvercoatLion^mode3=PillarfieldOx"
      * @param spellOnStack
      */
     public void castSpell(int turnNum, PhaseStep step, TestPlayer player, String cardName, String targetName, String spellOnStack) {
@@ -1605,7 +1612,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * @param step
      * @param player
      * @param ability
-     * @param targetName use NO_TARGET if there is no target to set
+     * @param targetName   use NO_TARGET if there is no target to set
      * @param spellOnStack
      */
     public void activateAbility(int turnNum, PhaseStep step, TestPlayer player, String ability, String targetName, String spellOnStack) {
@@ -1618,8 +1625,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * @param step
      * @param player
      * @param ability
-     * @param targetName if not target has to be defined use the constant
-     * NO_TARGET
+     * @param targetName   if not target has to be defined use the constant
+     *                     NO_TARGET
      * @param spellOnStack
      * @param clause
      */
@@ -1705,10 +1712,10 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      *
      * @param player
      * @param choice starting with "1" for mode 1, "2" for mode 2 and so on (to
-     * set multiple modes call the command multiple times). If a spell mode can
-     * be used only once like Demonic Pact, the value has to be set to the
-     * number of the remaining modes (e.g. if only 2 are left the number need to
-     * be 1 or 2).
+     *               set multiple modes call the command multiple times). If a spell mode can
+     *               be used only once like Demonic Pact, the value has to be set to the
+     *               number of the remaining modes (e.g. if only 2 are left the number need to
+     *               be 1 or 2).
      */
     public void setModeChoice(TestPlayer player, String choice) {
         player.addModeChoice(choice);
@@ -1719,12 +1726,12 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      *
      * @param player
      * @param target you can add multiple targets by separating them by the "^"
-     * character e.g. "creatureName1^creatureName2" you can qualify the target
-     * additional by setcode e.g. "creatureName-M15" you can add [no copy] to
-     * the end of the target name to prohibit targets that are copied you can
-     * add [only copy] to the end of the target name to allow only targets that
-     * are copies. For modal spells use a prefix with the mode number:
-     * mode=1Lightning Bolt^mode=2Silvercoat Lion
+     *               character e.g. "creatureName1^creatureName2" you can qualify the target
+     *               additional by setcode e.g. "creatureName-M15" you can add [no copy] to
+     *               the end of the target name to prohibit targets that are copied you can
+     *               add [only copy] to the end of the target name to allow only targets that
+     *               are copies. For modal spells use a prefix with the mode number:
+     *               mode=1Lightning Bolt^mode=2Silvercoat Lion
      */
     // TODO: mode options doesn't work here (see BrutalExpulsionTest)
     public void addTarget(TestPlayer player, String target) {
@@ -1744,7 +1751,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     /**
      * @param player
      * @param target use TestPlayer.TARGET_SKIP to 0 targets selects or to stop
-     * "up two xxx" selection
+     *               "up two xxx" selection
      * @param amount
      */
     public void addTargetAmount(TestPlayer player, String target, int amount) {
diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java
index a7cdb25fdd..435772ef44 100644
--- a/Mage/src/main/java/mage/game/GameImpl.java
+++ b/Mage/src/main/java/mage/game/GameImpl.java
@@ -1,9 +1,5 @@
 package mage.game;
 
-import java.io.IOException;
-import java.io.Serializable;
-import java.util.*;
-import java.util.Map.Entry;
 import mage.MageException;
 import mage.MageObject;
 import mage.abilities.*;
@@ -71,6 +67,11 @@ import mage.util.functions.ApplyToPermanent;
 import mage.watchers.common.*;
 import org.apache.log4j.Logger;
 
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.*;
+import java.util.Map.Entry;
+
 public abstract class GameImpl implements Game, Serializable {
 
     private static final int ROLLBACK_TURNS_MAX = 4;
@@ -1548,7 +1549,7 @@ public abstract class GameImpl implements Game, Serializable {
     /**
      * @param emblem
      * @param sourceObject
-     * @param toPlayerId controller and owner of the emblem
+     * @param toPlayerId   controller and owner of the emblem
      */
     @Override
     public void addEmblem(Emblem emblem, MageObject sourceObject, UUID toPlayerId) {
@@ -1566,8 +1567,8 @@ public abstract class GameImpl implements Game, Serializable {
     /**
      * @param plane
      * @param sourceObject
-     * @param toPlayerId controller and owner of the plane (may only be one per
-     * game..)
+     * @param toPlayerId   controller and owner of the plane (may only be one per
+     *                     game..)
      * @return boolean - whether the plane was added successfully or not
      */
     @Override
@@ -1803,7 +1804,7 @@ public abstract class GameImpl implements Game, Serializable {
                     break;
                 }
                 // triggered abilities that don't use the stack have to be executed first (e.g. Banisher Priest Return exiled creature
-                for (Iterator<TriggeredAbility> it = abilities.iterator(); it.hasNext();) {
+                for (Iterator<TriggeredAbility> it = abilities.iterator(); it.hasNext(); ) {
                     TriggeredAbility triggeredAbility = it.next();
                     if (!triggeredAbility.isUsesStack()) {
                         state.removeTriggeredAbility(triggeredAbility);
@@ -2594,7 +2595,7 @@ public abstract class GameImpl implements Game, Serializable {
         }
         //20100423 - 800.4a
         Set<Card> toOutside = new HashSet<>();
-        for (Iterator<Permanent> it = getBattlefield().getAllPermanents().iterator(); it.hasNext();) {
+        for (Iterator<Permanent> it = getBattlefield().getAllPermanents().iterator(); it.hasNext(); ) {
             Permanent perm = it.next();
             if (perm.isOwnedBy(playerId)) {
                 if (perm.getAttachedTo() != null) {
@@ -2639,7 +2640,7 @@ public abstract class GameImpl implements Game, Serializable {
         player.moveCards(toOutside, Zone.OUTSIDE, null, this);
         // triggered abilities that don't use the stack have to be executed
         List<TriggeredAbility> abilities = state.getTriggered(player.getId());
-        for (Iterator<TriggeredAbility> it = abilities.iterator(); it.hasNext();) {
+        for (Iterator<TriggeredAbility> it = abilities.iterator(); it.hasNext(); ) {
             TriggeredAbility triggeredAbility = it.next();
             if (!triggeredAbility.isUsesStack()) {
                 state.removeTriggeredAbility(triggeredAbility);
@@ -2659,7 +2660,7 @@ public abstract class GameImpl implements Game, Serializable {
 
         // Remove cards from the player in all exile zones
         for (ExileZone exile : this.getExile().getExileZones()) {
-            for (Iterator<UUID> it = exile.iterator(); it.hasNext();) {
+            for (Iterator<UUID> it = exile.iterator(); it.hasNext(); ) {
                 Card card = this.getCard(it.next());
                 if (card != null && card.isOwnedBy(playerId)) {
                     it.remove();
@@ -2669,7 +2670,7 @@ public abstract class GameImpl implements Game, Serializable {
 
         //Remove all commander/emblems/plane the player controls
         boolean addPlaneAgain = false;
-        for (Iterator<CommandObject> it = this.getState().getCommand().iterator(); it.hasNext();) {
+        for (Iterator<CommandObject> it = this.getState().getCommand().iterator(); it.hasNext(); ) {
             CommandObject obj = it.next();
             if (obj.isControlledBy(playerId)) {
                 if (obj instanceof Emblem) {
diff --git a/Mage/src/main/java/mage/util/ManaUtil.java b/Mage/src/main/java/mage/util/ManaUtil.java
index ef24b04b43..44689f497e 100644
--- a/Mage/src/main/java/mage/util/ManaUtil.java
+++ b/Mage/src/main/java/mage/util/ManaUtil.java
@@ -1,6 +1,5 @@
 package mage.util;
 
-import java.util.*;
 import mage.MageObject;
 import mage.Mana;
 import mage.ManaSymbol;
@@ -19,6 +18,8 @@ import mage.filter.FilterMana;
 import mage.game.Game;
 import mage.players.Player;
 
+import java.util.*;
+
 /**
  * @author noxx
  */
@@ -46,7 +47,7 @@ public final class ManaUtil {
      * In case we can't auto choose we'll simply return the useableAbilities map
      * back to caller without any modification.
      *
-     * @param unpaid Mana we need to pay. Can be null (it is for X costs now).
+     * @param unpaid           Mana we need to pay. Can be null (it is for X costs now).
      * @param useableAbilities List of mana abilities permanent may produce
      * @return List of mana abilities permanent may produce and are reasonable
      * for unpaid mana
@@ -403,7 +404,7 @@ public final class ManaUtil {
     }
 
     /**
-     * This activates the special button inthe feedback panel of the client if
+     * This activates the special button in the feedback panel of the client if
      * there exists special ways to pay the mana (e.g. Delve, Convoke)
      *
      * @param source ability the mana costs have to be paid for

From c5cb7b97a77ef502b078c7181d45ae1cdd3eb99b Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Fri, 19 Jun 2020 12:59:06 +0200
Subject: [PATCH 406/586] * Added test for Ajani, Inspiring leader does not
 trigger Exquisite Blood + Defiant Bloodlord #6464. Not reprodicable.

---
 .../test/cards/single/ExquisiteBloodTest.java | 96 ++++++++++++++++++-
 1 file changed, 93 insertions(+), 3 deletions(-)

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ExquisiteBloodTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/ExquisiteBloodTest.java
index 509fec3e15..19cf871c96 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/single/ExquisiteBloodTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/ExquisiteBloodTest.java
@@ -2,6 +2,7 @@ package org.mage.test.cards.single;
 
 import mage.constants.PhaseStep;
 import mage.constants.Zone;
+import mage.counters.CounterType;
 import org.junit.Test;
 import org.mage.test.serverside.base.CardTestPlayerBase;
 
@@ -12,12 +13,12 @@ import org.mage.test.serverside.base.CardTestPlayerBase;
 public class ExquisiteBloodTest extends CardTestPlayerBase {
 
     @Test
-    public void testCard() {
+    public void BasicCardTest() {
         addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
         addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
 
-        // card we test
-        addCard(Zone.BATTLEFIELD, playerA, "Exquisite Blood", 1);
+        // Whenever an opponent loses life, you gain that much life.
+        addCard(Zone.BATTLEFIELD, playerA, "Exquisite Blood", 1); // Enchantment {4}{B}
 
         addCard(Zone.HAND, playerA, "Lightning Bolt");
         addCard(Zone.HAND, playerA, "Bump in the Night");
@@ -42,4 +43,93 @@ public class ExquisiteBloodTest extends CardTestPlayerBase {
         assertLife(playerA, 26);
     }
 
+    /**
+     *  Ajani, Inspiring leader does not trigger Exquisite Blood + Defiant Bloodlord #6464
+     */
+    @Test
+    public void triggerCascadeTest() {
+        // +2: You gain 2 life. Put two +1/+1 counters on up to one target creature.
+        // −3: Exile target creature. Its controller gains 2 life.
+        // −10: Creatures you control gain flying and double strike until end of turn.        
+        addCard(Zone.BATTLEFIELD, playerA, "Ajani, Inspiring Leader", 1); // Planeswalker (5)        
+
+        // Flying
+        // Whenever you gain life, target opponent loses that much life.
+        addCard(Zone.BATTLEFIELD, playerA, "Defiant Bloodlord", 1); // Creature 4/5 {5}{B}{B}        
+        
+        // Whenever an opponent loses life, you gain that much life.
+        addCard(Zone.BATTLEFIELD, playerA, "Exquisite Blood", 1); // Enchantment {4}{B}
+
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+2:", "Defiant Bloodlord");
+        addTarget(playerA, playerB);  // Target opponent of Defiant Bloodlord triggered ability (looping until opponent is dead)
+        addTarget(playerA, playerB);  
+        addTarget(playerA, playerB);  
+        addTarget(playerA, playerB);
+        addTarget(playerA, playerB);
+        addTarget(playerA, playerB);
+        addTarget(playerA, playerB);
+        addTarget(playerA, playerB);
+        addTarget(playerA, playerB);
+        addTarget(playerA, playerB);
+        
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+        
+        assertAllCommandsUsed();
+
+        assertPowerToughness(playerA, "Defiant Bloodlord", 6, 7);
+        assertCounterCount("Ajani, Inspiring Leader", CounterType.LOYALTY, 7);
+
+        assertLife(playerB, 0); // Player B is dead, game ends
+        assertLife(playerA, 40);
+        
+        
+    }
+
+    /**
+     *  Ajani, Inspiring leader does not trigger Exquisite Blood + Defiant Bloodlord #6464
+     */
+    @Test
+    public void triggerCascadeAjaniSecondAbilityTest() {
+        // +2: You gain 2 life. Put two +1/+1 counters on up to one target creature.
+        // −3: Exile target creature. Its controller gains 2 life.
+        // −10: Creatures you control gain flying and double strike until end of turn.        
+        addCard(Zone.BATTLEFIELD, playerA, "Ajani, Inspiring Leader", 1); // Planeswalker (5)        
+        addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); // Creature 2/2
+
+        // Flying
+        // Whenever you gain life, target opponent loses that much life.
+        addCard(Zone.BATTLEFIELD, playerA, "Defiant Bloodlord", 1); // Creature 4/5 {5}{B}{B}        
+        
+        // Whenever an opponent loses life, you gain that much life.
+        addCard(Zone.BATTLEFIELD, playerA, "Exquisite Blood", 1); // Enchantment {4}{B}
+
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "-3:", "Silvercoat Lion");
+        addTarget(playerA, playerB);  // Target opponent of Defiant Bloodlord triggered ability (looping until opponent is dead)
+        addTarget(playerA, playerB);  
+        addTarget(playerA, playerB);  
+        addTarget(playerA, playerB);
+        addTarget(playerA, playerB);
+        addTarget(playerA, playerB);
+        addTarget(playerA, playerB);
+        addTarget(playerA, playerB);
+        addTarget(playerA, playerB);
+        addTarget(playerA, playerB);
+        
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+        
+        assertAllCommandsUsed();
+
+        assertExileCount(playerA, "Silvercoat Lion", 1);
+        assertPowerToughness(playerA, "Defiant Bloodlord", 4, 5);
+        assertCounterCount("Ajani, Inspiring Leader", CounterType.LOYALTY, 2);
+
+        assertLife(playerB, 0); // Player B is dead, game ends
+        assertLife(playerA, 40);
+        
+        
+    }
 }

From da52fc430cf4fdb6a51032d2a0e6fab354f62990 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Fri, 19 Jun 2020 08:24:16 -0400
Subject: [PATCH 407/586] fixed Thieves' Guild Enforcer giving all permanents
 deathtouch

---
 Mage.Sets/src/mage/cards/t/ThievesGuildEnforcer.java | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/t/ThievesGuildEnforcer.java b/Mage.Sets/src/mage/cards/t/ThievesGuildEnforcer.java
index 2e87972f01..489eac53a5 100644
--- a/Mage.Sets/src/mage/cards/t/ThievesGuildEnforcer.java
+++ b/Mage.Sets/src/mage/cards/t/ThievesGuildEnforcer.java
@@ -8,7 +8,7 @@ import mage.abilities.condition.Condition;
 import mage.abilities.decorator.ConditionalContinuousEffect;
 import mage.abilities.effects.common.PutTopCardOfLibraryIntoGraveEachPlayerEffect;
 import mage.abilities.effects.common.continuous.BoostSourceEffect;
-import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
+import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
 import mage.abilities.keyword.DeathtouchAbility;
 import mage.abilities.keyword.FlashAbility;
 import mage.cards.CardImpl;
@@ -56,7 +56,7 @@ public final class ThievesGuildEnforcer extends CardImpl {
                 ThievesGuildEnforcerCondition.instance, "as long as an opponent " +
                 "has eight or more cards in their graveyard, {this} gets +2/+1"
         ));
-        ability.addEffect(new ConditionalContinuousEffect(new GainAbilityControlledEffect(
+        ability.addEffect(new ConditionalContinuousEffect(new GainAbilitySourceEffect(
                 DeathtouchAbility.getInstance(), Duration.WhileOnBattlefield
         ), ThievesGuildEnforcerCondition.instance, "and has deathtouch"));
         this.addAbility(ability);

From 85083a4433a25b07fd4b9340b1c195cc8e32f426 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Fri, 19 Jun 2020 21:52:31 +0400
Subject: [PATCH 408/586] * Devour abilities - fixed that it doesn't trigger
 simultaneous events after multiple permanents sacrifice (#6254, #6273)

---
 .../triggers/dies/ZulaportCutthroatTest.java  | 25 ++++++++++++++++---
 .../effects/common/DevourEffect.java          | 20 +++++++--------
 2 files changed, 32 insertions(+), 13 deletions(-)

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/ZulaportCutthroatTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/ZulaportCutthroatTest.java
index c7a050b85e..dc91f4ae4f 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/ZulaportCutthroatTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/ZulaportCutthroatTest.java
@@ -1,4 +1,3 @@
-
 package org.mage.test.cards.triggers.dies;
 
 import mage.constants.PhaseStep;
@@ -7,7 +6,6 @@ import org.junit.Test;
 import org.mage.test.serverside.base.CardTestPlayerBase;
 
 /**
- *
  * @author LevelX2
  */
 public class ZulaportCutthroatTest extends CardTestPlayerBase {
@@ -16,7 +14,6 @@ public class ZulaportCutthroatTest extends CardTestPlayerBase {
      * Zulaport's ability doesn't trigger when it dies. I'm not sure if that's
      * always the case, but I've encountered that bug at least several times
      * today.
-     *
      */
     @Test
     public void testDiesAndControllerDamage() {
@@ -40,4 +37,26 @@ public class ZulaportCutthroatTest extends CardTestPlayerBase {
 
     }
 
+    @Test
+    public void testTriggersWhenDevoured() {
+        // Whenever Zulaport Cutthroat or another creature you control dies, each opponent loses 1 life and you gain 1 life.
+        addCard(Zone.BATTLEFIELD, playerA, "Zulaport Cutthroat", 1); // 1/1
+        addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1); // 2/2
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 3);
+        // Devour 1
+        addCard(Zone.HAND, playerA, "Gluttonous Slime");
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Gluttonous Slime");
+        setChoice(playerA, "Yes"); // Devour
+        addTarget(playerA, "Zulaport Cutthroat^Grizzly Bears");
+        setChoice(playerA, "Whenever {this}"); // Two triggers
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        assertLife(playerA, 22);
+        assertLife(playerB, 18);
+    }
 }
diff --git a/Mage/src/main/java/mage/abilities/effects/common/DevourEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DevourEffect.java
index d2d8a6a7d2..553bf2b6c1 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/DevourEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/DevourEffect.java
@@ -88,19 +88,19 @@ public class DevourEffect extends ReplacementEffectImpl {
                 controller.chooseTarget(Outcome.Detriment, target, source, game);
                 if (!target.getTargets().isEmpty()) {
                     List<SubTypeList> cardSubtypes = new ArrayList<>();
-                    int devouredCreatures = target.getTargets().size();
+                    int devouredCreatures = 0;
+                    for (UUID targetId : target.getTargets()) {
+                        Permanent targetCreature = game.getPermanent(targetId);
+                        if (targetCreature != null && targetCreature.sacrifice(source.getSourceId(), game)) {
+                            cardSubtypes.add(targetCreature.getSubtype(game));
+                            devouredCreatures++;
+                        }
+                    }
                     if (!game.isSimulation()) {
                         game.informPlayers(creature.getLogName() + " devours " + devouredCreatures + " creatures");
                     }
-                    for (UUID targetId : target.getTargets()) {
-                        Permanent targetCreature = game.getPermanent(targetId);
-                        if (targetCreature != null) {
-                            cardSubtypes.add(targetCreature.getSubtype(game));
-                        }
-                        if (targetCreature == null || !targetCreature.sacrifice(source.getSourceId(), game)) {
-                            return false;
-                        }
-                    }
+                    game.getState().processAction(game); // need for multistep effects
+
                     int amountCounters;
                     if (devourFactor == DevourFactor.DevourX) {
                         amountCounters = devouredCreatures * devouredCreatures;

From f2c09a7193b35c0f1b7cb746fde8b7eb1dc32b6f Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Fri, 19 Jun 2020 14:36:01 -0500
Subject: [PATCH 409/586] - Text fix

---
 Mage.Sets/src/mage/cards/r/Retaliation.java | 20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/r/Retaliation.java b/Mage.Sets/src/mage/cards/r/Retaliation.java
index 21e41b0057..87b2954d07 100644
--- a/Mage.Sets/src/mage/cards/r/Retaliation.java
+++ b/Mage.Sets/src/mage/cards/r/Retaliation.java
@@ -1,9 +1,9 @@
-
 package mage.cards.r;
 
 import java.util.UUID;
 import mage.abilities.common.BecomesBlockedByCreatureTriggeredAbility;
 import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.continuous.BoostSourceEffect;
 import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
 import mage.cards.CardImpl;
@@ -20,12 +20,20 @@ import mage.filter.StaticFilters;
 public final class Retaliation extends CardImpl {
 
     public Retaliation(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{G}");
+        super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}");
 
-        // Creatures you control have "Whenever this creature becomes blocked by a creature, this creature gets +1/+1 until end of turn."
-        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, 
-                new GainAbilityControlledEffect(new BecomesBlockedByCreatureTriggeredAbility(new BoostSourceEffect(1, 1, Duration.EndOfTurn), false), 
-                        Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURES)));
+        // Creatures you control have "Whenever this creature becomes blocked by a creature, 
+        // this creature gets +1/+1 until end of turn."
+        Effect effect = new GainAbilityControlledEffect(
+                new BecomesBlockedByCreatureTriggeredAbility(
+                        new BoostSourceEffect(
+                                1, 1,
+                                Duration.EndOfTurn), false),
+                Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURES);
+        effect.setText("Creatures you control have \"Whenever this creature becomes blocked by a creature, "
+                + "this creature gets +1/+1 until end of turn.");
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
+                effect));
     }
 
     public Retaliation(final Retaliation card) {

From 2e7d01965e5b780a2d7a1ff2419d12bd48989b52 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Fri, 19 Jun 2020 23:42:45 +0400
Subject: [PATCH 410/586] * Madness ability - fixed that it ask about madness
 cast two times (fixes #6674);

---
 .../cards/abilities/keywords/MadnessTest.java | 17 ++-----
 .../abilities/keyword/MadnessAbility.java     | 51 +++++++++----------
 2 files changed, 28 insertions(+), 40 deletions(-)

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MadnessTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MadnessTest.java
index bb4c94e383..3c41b01166 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MadnessTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MadnessTest.java
@@ -23,15 +23,11 @@ public class MadnessTest extends CardTestPlayerBase {
      * card into their graveyard.” 702.34b Casting a spell using its
      * madness ability follows the rules for paying alternative costs in rules
      * 601.2b and 601.2e–g.
-     *
-     */
-    /**
+     * <p>
      * Arrogant Wurm 3GG Creature -- Wurm 4/4 Trample Madness {2}{G} (If you
      * discard this card, you may cast it for its madness cost instead of
      * putting it into your graveyard.)
-     *
-     */
-    /**
+     * <p>
      * Raven's Crime B Sorcery Target player discards a card. Retrace (You may
      * cast this card from your graveyard by discarding a land card in addition
      * to paying its other costs.)
@@ -45,7 +41,6 @@ public class MadnessTest extends CardTestPlayerBase {
 
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Raven's Crime", playerA);
         setChoice(playerA, "Yes"); // use madness triggered ability
-        setChoice(playerA, "Yes"); // use madness cast
 
         setStrictChooseMode(true);
         setStopAt(1, PhaseStep.BEGIN_COMBAT);
@@ -125,7 +120,6 @@ public class MadnessTest extends CardTestPlayerBase {
 
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Haunting Hymn", playerA);
         setChoice(playerA, "Yes"); // use madness triggered ability
-        setChoice(playerA, "Yes"); // use madness cast
         setChoice(playerA, "X=4");
         addTargetAmount(playerA, "Pillarfield Ox", 4);
 
@@ -177,15 +171,14 @@ public class MadnessTest extends CardTestPlayerBase {
         addCard(Zone.HAND, playerA, "Forest");
 
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Falkenrath Gorger");
-        
+
         setChoice(playerA, "Yes"); // Discard a card and put a +1/+1 counter on that creature, it gains haste until end of turn, and it becomes a Vampire in addition to its other types?
         setChoice(playerA, "Asylum Visitor"); // Card to discard from Falkenrath entering by Olivia effect
         setChoice(playerA, "Asylum Visito"); // Madness {1}{B}
-        setChoice(playerA, "Yes"); // use madness triggered ability        
-        setChoice(playerA, "Yes"); // use madness cast        
+        setChoice(playerA, "Yes"); // use madness triggered ability
         setChoice(playerA, "Yes"); // Discard a card and put a +1/+1 counter on that creature, it gains haste until end of turn, and it becomes a Vampire in addition to its other types?
         setChoice(playerA, "Forest");
-        
+
         setStrictChooseMode(true);
         setStopAt(1, PhaseStep.BEGIN_COMBAT);
         execute();
diff --git a/Mage/src/main/java/mage/abilities/keyword/MadnessAbility.java b/Mage/src/main/java/mage/abilities/keyword/MadnessAbility.java
index 719ad2266a..91b58c68e9 100644
--- a/Mage/src/main/java/mage/abilities/keyword/MadnessAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/MadnessAbility.java
@@ -1,6 +1,5 @@
 package mage.abilities.keyword;
 
-import java.util.UUID;
 import mage.MageObject;
 import mage.MageObjectReference;
 import mage.abilities.Ability;
@@ -13,35 +12,33 @@ import mage.abilities.costs.mana.ManaCosts;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.ReplacementEffectImpl;
 import mage.cards.Card;
-import mage.constants.Duration;
-import mage.constants.Outcome;
-import mage.constants.SpellAbilityCastMode;
-import mage.constants.SpellAbilityType;
-import mage.constants.Zone;
+import mage.constants.*;
 import mage.game.Game;
 import mage.game.events.GameEvent;
 import mage.game.events.ZoneChangeEvent;
 import mage.game.stack.Spell;
 import mage.players.Player;
 
+import java.util.UUID;
+
 /**
  * 702.33. Madness
- *
+ * <p>
  * 702.33a. Madness is a keyword that represents two abilities.
- *
+ * <p>
  * The first is a static ability that functions while the card with madness is
  * in a player's hand. The second is a triggered ability that functions when the
  * first ability is applied.
- *
+ * <p>
  * "Madness [cost]" means "If a player would discard this card, that player
  * discards it, but may exile it instead of putting it into their graveyard" and
  * "When this card is exiled this way, its owner may cast it by paying [cost]
  * rather than paying its mana cost. If that player doesn't, they put this
  * card into their graveyard.
- *
+ * <p>
  * 702.33b. Casting a spell using its madness ability follows the rules for
  * paying alternative costs in rules 601.2b and 601.2e-g.
- *
+ * <p>
  * SOI Changes: If you discard a card with madness, you exile it instead of
  * putting it into your graveyard. Note that the mandatory discard into exile is
  * a small change from previous rules. Before, you could discard a card with
@@ -52,7 +49,7 @@ import mage.players.Player;
  */
 public class MadnessAbility extends StaticAbility {
 
-    private String rule;
+    private final String rule;
 
     @SuppressWarnings("unchecked")
     public MadnessAbility(Card card, ManaCosts madnessCost) {
@@ -205,26 +202,24 @@ class MadnessCastEffect extends OneShotEffect {
 
     @Override
     public boolean apply(Game game, Ability source) {
-        Player owner = null;
         Card card = game.getCard(source.getSourceId());
-        if (card != null) {
-            owner = game.getPlayer(card.getOwnerId());
+        if (card == null) {
+            return false;
         }
-        if (owner != null && card != null
-                && owner.chooseUse(outcome, "Cast " + card.getLogName() + " by madness?", source, game)) {
-
-            // replace with the new cost
-            SpellAbility castByMadness = card.getSpellAbility().copy();
-            ManaCosts<ManaCost> costRef = castByMadness.getManaCostsToPay();
-            castByMadness.setSpellAbilityType(SpellAbilityType.BASE_ALTERNATE);
-            castByMadness.setSpellAbilityCastMode(SpellAbilityCastMode.MADNESS);
-            costRef.clear();
-            costRef.add(madnessCost);
-            boolean result = owner.cast(castByMadness, game, false, new MageObjectReference(source.getSourceObject(game), game));
-            return result;
 
+        Player owner = game.getPlayer(card.getOwnerId());
+        if (owner == null) {
+            return false;
         }
-        return false;
+
+        // replace with the new cost
+        SpellAbility castByMadness = card.getSpellAbility().copy();
+        ManaCosts<ManaCost> costRef = castByMadness.getManaCostsToPay();
+        castByMadness.setSpellAbilityType(SpellAbilityType.BASE_ALTERNATE);
+        castByMadness.setSpellAbilityCastMode(SpellAbilityCastMode.MADNESS);
+        costRef.clear();
+        costRef.add(madnessCost);
+        return owner.cast(castByMadness, game, false, new MageObjectReference(source.getSourceObject(game), game));
     }
 
     @Override

From fed6ddff8e564d15e215707b6e13ca2432bf9ff1 Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Fri, 19 Jun 2020 14:58:44 -0500
Subject: [PATCH 411/586] - Text fix

---
 Mage.Sets/src/mage/cards/m/MagmaSliver.java | 21 ++++++++++++++++-----
 1 file changed, 16 insertions(+), 5 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/m/MagmaSliver.java b/Mage.Sets/src/mage/cards/m/MagmaSliver.java
index eadc0ecd65..967629a898 100644
--- a/Mage.Sets/src/mage/cards/m/MagmaSliver.java
+++ b/Mage.Sets/src/mage/cards/m/MagmaSliver.java
@@ -1,4 +1,3 @@
-
 package mage.cards.m;
 
 import java.util.UUID;
@@ -9,6 +8,7 @@ import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.costs.common.TapSourceCost;
 import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
 import mage.abilities.dynamicvalue.common.StaticValue;
+import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.continuous.BoostTargetEffect;
 import mage.abilities.effects.common.continuous.GainAbilityAllEffect;
 import mage.cards.CardImpl;
@@ -35,11 +35,22 @@ public final class MagmaSliver extends CardImpl {
         this.power = new MageInt(3);
         this.toughness = new MageInt(3);
 
-        // All Slivers have "{tap}: Target Sliver creature gets +X/+0 until end of turn, where X is the number of Slivers on the battlefield."
-        Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostTargetEffect(new PermanentsOnBattlefieldCount(StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS), StaticValue.get(0), Duration.EndOfTurn, true), new TapSourceCost());
-        Target target = new TargetCreaturePermanent(new FilterCreaturePermanent(SubType.SLIVER, "Sliver creature"));
+        // All Slivers have "{tap}: Target Sliver creature gets +X/+0 until end of turn, 
+        // where X is the number of Slivers on the battlefield."
+        Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD,
+                new BoostTargetEffect(new PermanentsOnBattlefieldCount(
+                        StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS),
+                        StaticValue.get(0), Duration.EndOfTurn, true),
+                new TapSourceCost());
+        Target target = new TargetCreaturePermanent(
+                new FilterCreaturePermanent(SubType.SLIVER, "Sliver creature"));
         ability.addTarget(target);
-        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(ability, Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS)));
+        Effect effect = new GainAbilityAllEffect(ability, Duration.WhileOnBattlefield,
+                StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS);
+        effect.setText("All Slivers have \"{T}: Target Sliver creature gets +X/+0 until end of turn,"
+                + "where X is the number of Slivers on the battlefield.\"");
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
+                effect));
     }
 
     public MagmaSliver(final MagmaSliver card) {

From f7fd9767db00882456ac9453a565eff7a7a6c380 Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Fri, 19 Jun 2020 15:27:00 -0500
Subject: [PATCH 412/586] - Text fix

---
 Mage.Sets/src/mage/cards/h/HedronBlade.java | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/Mage.Sets/src/mage/cards/h/HedronBlade.java b/Mage.Sets/src/mage/cards/h/HedronBlade.java
index 463af05b0c..41df7e6add 100644
--- a/Mage.Sets/src/mage/cards/h/HedronBlade.java
+++ b/Mage.Sets/src/mage/cards/h/HedronBlade.java
@@ -100,6 +100,7 @@ class HedronBladeTriggeredAbility extends TriggeredAbilityImpl {
 
     @Override
     public String getRule() {
-        return "Whenever equipped creature becomes blocked by one or more colorless creatures, " + super.getRule();
+        return "Whenever equipped creature becomes blocked by one or more colorless creatures, "
+                + "" + "it gains deathtouch until end of turn.";
     }
 }

From cd8d12365f8119dcfe19176a7142e77f80f423bf Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Sat, 20 Jun 2020 00:29:33 +0400
Subject: [PATCH 413/586] * Add mana of the chosen color - added info about
 chosen color to ability choose dialog (#6677);

---
 .../effects/mana/AddManaChosenColorEffect.java    | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/Mage/src/main/java/mage/abilities/effects/mana/AddManaChosenColorEffect.java b/Mage/src/main/java/mage/abilities/effects/mana/AddManaChosenColorEffect.java
index 22740daf39..554d8463df 100644
--- a/Mage/src/main/java/mage/abilities/effects/mana/AddManaChosenColorEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/mana/AddManaChosenColorEffect.java
@@ -1,13 +1,9 @@
-/*
- * To change this license header, choose License Headers in Project Properties.
- * To change this template file, choose Tools | Templates
- * and open the template in the editor.
- */
 package mage.abilities.effects.mana;
 
 import mage.Mana;
 import mage.ObjectColor;
 import mage.abilities.Ability;
+import mage.abilities.Mode;
 import mage.abilities.effects.common.ManaEffect;
 import mage.constants.ColoredManaSymbol;
 import mage.game.Game;
@@ -17,6 +13,8 @@ import mage.game.Game;
  */
 public class AddManaChosenColorEffect extends ManaEffect {
 
+    private ObjectColor chosenColorInfo = null;
+
     public AddManaChosenColorEffect() {
         super();
         staticText = "Add one mana of the chosen color";
@@ -24,6 +22,7 @@ public class AddManaChosenColorEffect extends ManaEffect {
 
     public AddManaChosenColorEffect(final AddManaChosenColorEffect effect) {
         super(effect);
+        chosenColorInfo = effect.chosenColorInfo;
     }
 
     @Override
@@ -31,6 +30,7 @@ public class AddManaChosenColorEffect extends ManaEffect {
         if (game != null) {
             ObjectColor color = (ObjectColor) game.getState().getValue(source.getSourceId() + "_color");
             if (color != null) {
+                this.chosenColorInfo = color;
                 return new Mana(ColoredManaSymbol.lookup(color.toString().charAt(0)));
             }
         }
@@ -41,4 +41,9 @@ public class AddManaChosenColorEffect extends ManaEffect {
     public AddManaChosenColorEffect copy() {
         return new AddManaChosenColorEffect(this);
     }
+
+    @Override
+    public String getText(Mode mode) {
+        return super.getText(mode) + (chosenColorInfo == null ? "" : " {" + chosenColorInfo.toString() + "}");
+    }
 }

From 9ede6ae9b567ba57ac28823f1791882f47380d4e Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Fri, 19 Jun 2020 15:41:04 -0500
Subject: [PATCH 414/586] - Text fix

---
 Mage.Sets/src/mage/cards/n/NecromanticSummons.java | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/n/NecromanticSummons.java b/Mage.Sets/src/mage/cards/n/NecromanticSummons.java
index 772ec918db..ef4c4f2ea1 100644
--- a/Mage.Sets/src/mage/cards/n/NecromanticSummons.java
+++ b/Mage.Sets/src/mage/cards/n/NecromanticSummons.java
@@ -1,4 +1,3 @@
-
 package mage.cards.n;
 
 import java.util.UUID;
@@ -34,8 +33,11 @@ public final class NecromanticSummons extends CardImpl {
         this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect());
         this.getSpellAbility().addTarget(new TargetCardInGraveyard(new FilterCreatureCard("creature card from a graveyard")));
 
-        // <i>Spell mastery</i> &mdash; If there are two or more instant and/or sorcery cards in your graveyard, that creature enters the battlefield with two additional +1/+1 counters on it.
-        this.getSpellAbility().addEffect(new InfoEffect("\"<br><i>Spell mastery</i> &mdash; If there are two or more instant and/or sorcery cards in your graveyard, that creature enters the battlefield with two additional +1/+1 counters on it\""));
+        // <i>Spell mastery</i> &mdash; If there are two or more instant and/or sorcery cards in your graveyard, 
+        // that creature enters the battlefield with two additional +1/+1 counters on it.
+        this.getSpellAbility().addEffect(new InfoEffect("<br><i>Spell mastery</i> &mdash; If there are two or more "
+                + "instant and/or sorcery cards in your graveyard, that creature enters the "
+                + "battlefield with two additional +1/+1 counters on it."));
     }
 
     public NecromanticSummons(final NecromanticSummons card) {

From 6b14c5a4d32edb6009c555caa651e5163fa45f67 Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Fri, 19 Jun 2020 15:45:11 -0500
Subject: [PATCH 415/586] - Text fix

---
 Mage.Sets/src/mage/cards/b/BreakOpen.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Mage.Sets/src/mage/cards/b/BreakOpen.java b/Mage.Sets/src/mage/cards/b/BreakOpen.java
index 60df5e30ab..5a5209b330 100644
--- a/Mage.Sets/src/mage/cards/b/BreakOpen.java
+++ b/Mage.Sets/src/mage/cards/b/BreakOpen.java
@@ -17,7 +17,7 @@ import mage.target.common.TargetCreaturePermanent;
  */
 public final class BreakOpen extends CardImpl {
 
-    private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Face-down creature an opponent controls");
+    private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("face-down creature an opponent controls");
 
     static {
         filter.add(FaceDownPredicate.instance);

From c22c93ea90f148363c24ab151c7a281a8927c819 Mon Sep 17 00:00:00 2001
From: arcox <10953229+arcox@users.noreply.github.com>
Date: Fri, 19 Jun 2020 20:51:16 +0000
Subject: [PATCH 416/586] Implement Tome Anima from Core 2021 (#6668)

* Implement Tome Anima from Core 2021

* newline at end of file
---
 Mage.Sets/src/mage/cards/t/TomeAnima.java | 58 +++++++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java  |  1 +
 2 files changed, 59 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/t/TomeAnima.java

diff --git a/Mage.Sets/src/mage/cards/t/TomeAnima.java b/Mage.Sets/src/mage/cards/t/TomeAnima.java
new file mode 100644
index 0000000000..8b4351c2a5
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/t/TomeAnima.java
@@ -0,0 +1,58 @@
+package mage.cards.t;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.condition.Condition;
+import mage.abilities.decorator.ConditionalContinuousEffect;
+import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
+import mage.abilities.keyword.CantBeBlockedSourceAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+import mage.game.Game;
+import mage.watchers.common.CardsDrawnThisTurnWatcher;
+
+import java.util.UUID;
+
+/**
+ * @author arcox
+ */
+public final class TomeAnima extends CardImpl {
+
+    public TomeAnima(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}");
+
+        this.subtype.add(SubType.SPIRIT);
+        this.power = new MageInt(3);
+        this.toughness = new MageInt(3);
+
+        // Tome Anima can’t be blocked as long as you’ve drawn two or more cards this turn.
+        this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect(
+                new GainAbilitySourceEffect(new CantBeBlockedSourceAbility(), Duration.WhileOnBattlefield),
+                TomeAnimaCondition.instance,
+                "{this} can’t be blocked as long as you’ve drawn two or more cards this turn"
+        )), new CardsDrawnThisTurnWatcher());
+    }
+
+    private TomeAnima(final TomeAnima card) {
+        super(card);
+    }
+
+    @Override
+    public TomeAnima copy() {
+        return new TomeAnima(this);
+    }
+}
+
+enum TomeAnimaCondition implements Condition {
+    instance;
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        CardsDrawnThisTurnWatcher watcher = game.getState().getWatcher(CardsDrawnThisTurnWatcher.class);
+        return watcher != null && watcher.getCardsDrawnThisTurn(source.getControllerId()) > 1;
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index b9445adc03..e70532caf7 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -272,6 +272,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Tide Skimmer", 79, Rarity.UNCOMMON, mage.cards.t.TideSkimmer.class));
         cards.add(new SetCardInfo("Titanic Growth", 210, Rarity.COMMON, mage.cards.t.TitanicGrowth.class));
         cards.add(new SetCardInfo("Tolarian Kraken", 80, Rarity.UNCOMMON, mage.cards.t.TolarianKraken.class));
+        cards.add(new SetCardInfo("Tome Anima", 81, Rarity.COMMON, mage.cards.t.TomeAnima.class));
         cards.add(new SetCardInfo("Tormod's Crypt", 241, Rarity.UNCOMMON, mage.cards.t.TormodsCrypt.class));
         cards.add(new SetCardInfo("Traitorous Greed", 166, Rarity.UNCOMMON, mage.cards.t.TraitorousGreed.class));
         cards.add(new SetCardInfo("Tranquil Cove", 258, Rarity.COMMON, mage.cards.t.TranquilCove.class));

From 71729f5a78d87794ee5dcbd65038bc54ec8ade23 Mon Sep 17 00:00:00 2001
From: arcox <10953229+arcox@users.noreply.github.com>
Date: Fri, 19 Jun 2020 20:52:46 +0000
Subject: [PATCH 417/586] Implement Track Down from Core 2021 (#6667)

---
 Mage.Sets/src/mage/cards/t/TrackDown.java | 84 +++++++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java  |  1 +
 2 files changed, 85 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/t/TrackDown.java

diff --git a/Mage.Sets/src/mage/cards/t/TrackDown.java b/Mage.Sets/src/mage/cards/t/TrackDown.java
new file mode 100644
index 0000000000..986845a13d
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/t/TrackDown.java
@@ -0,0 +1,84 @@
+package mage.cards.t;
+
+import java.util.UUID;
+
+import mage.MageObject;
+import mage.abilities.Ability;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.keyword.ScryEffect;
+import mage.cards.Card;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.cards.CardsImpl;
+import mage.constants.CardType;
+import mage.constants.Outcome;
+import mage.game.Game;
+import mage.players.Player;
+
+/**
+ * @author arcox
+ */
+public final class TrackDown extends CardImpl {
+
+    public TrackDown(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{G}");
+
+        // Scry 3, then reveal the top card of your library. If it’s a creature or land card, draw a card.
+        this.getSpellAbility().addEffect(new ScryEffect(3));
+        this.getSpellAbility().addEffect(new TrackDownEffect());
+    }
+
+    public TrackDown(final TrackDown card) {
+        super(card);
+    }
+
+    @Override
+    public TrackDown copy() {
+        return new TrackDown(this);
+    }
+}
+
+class TrackDownEffect extends OneShotEffect {
+
+    public TrackDownEffect() {
+        super(Outcome.DrawCard);
+        this.staticText = "then reveal the top card of your library. If it’s a creature or land card, draw a card";
+    }
+
+    public TrackDownEffect(final TrackDownEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public TrackDownEffect copy() {
+        return new TrackDownEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Player controller = game.getPlayer(source.getControllerId());
+        MageObject sourceObject = game.getObject(source.getSourceId());
+
+        if (sourceObject == null || controller == null) {
+            return false;
+        }
+
+        if (!controller.getLibrary().hasCards()) {
+            return false;
+        }
+
+        Card card = controller.getLibrary().getFromTop(game);
+        if (card == null) {
+            return false;
+        }
+
+        CardsImpl cards = new CardsImpl();
+        cards.add(card);
+        controller.revealCards(sourceObject.getName(), cards, game);
+        if (card.isLand() || card.isCreature()) {
+            controller.drawCards(1, source.getSourceId(), game);
+        }
+
+        return true;
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index e70532caf7..9753ea0531 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -274,6 +274,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Tolarian Kraken", 80, Rarity.UNCOMMON, mage.cards.t.TolarianKraken.class));
         cards.add(new SetCardInfo("Tome Anima", 81, Rarity.COMMON, mage.cards.t.TomeAnima.class));
         cards.add(new SetCardInfo("Tormod's Crypt", 241, Rarity.UNCOMMON, mage.cards.t.TormodsCrypt.class));
+        cards.add(new SetCardInfo("Track Down", 211, Rarity.COMMON, mage.cards.t.TrackDown.class));
         cards.add(new SetCardInfo("Traitorous Greed", 166, Rarity.UNCOMMON, mage.cards.t.TraitorousGreed.class));
         cards.add(new SetCardInfo("Tranquil Cove", 258, Rarity.COMMON, mage.cards.t.TranquilCove.class));
         cards.add(new SetCardInfo("Transmogrify", 167, Rarity.RARE, mage.cards.t.Transmogrify.class));

From 520937e68a8d2d07aa1d42b08548adbebafbc237 Mon Sep 17 00:00:00 2001
From: Daniel Peregolise <danielperegolise@gmail.com>
Date: Fri, 19 Jun 2020 16:54:27 -0400
Subject: [PATCH 418/586] Verbiage fixes (#6557)

* fix verbiage for ShredMemory card filter

* fixing verbiage for DriverOfTheDead
---
 Mage.Sets/src/mage/cards/d/DriverOfTheDead.java | 2 +-
 Mage.Sets/src/mage/cards/s/ShredMemory.java     | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/d/DriverOfTheDead.java b/Mage.Sets/src/mage/cards/d/DriverOfTheDead.java
index 67b28222df..06d477e38b 100644
--- a/Mage.Sets/src/mage/cards/d/DriverOfTheDead.java
+++ b/Mage.Sets/src/mage/cards/d/DriverOfTheDead.java
@@ -21,7 +21,7 @@ import mage.target.common.TargetCardInYourGraveyard;
  */
 public final class DriverOfTheDead extends CardImpl {
 
-    private static final FilterCreatureCard filter = new FilterCreatureCard("creature card with converted mana cost 2 or less from your graveyard to the battlefield");
+    private static final FilterCreatureCard filter = new FilterCreatureCard("creature card with converted mana cost 2 or less from your graveyard");
 
     static {
         filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 3));
diff --git a/Mage.Sets/src/mage/cards/s/ShredMemory.java b/Mage.Sets/src/mage/cards/s/ShredMemory.java
index f84304230c..b90c52d431 100644
--- a/Mage.Sets/src/mage/cards/s/ShredMemory.java
+++ b/Mage.Sets/src/mage/cards/s/ShredMemory.java
@@ -21,7 +21,7 @@ public final class ShredMemory extends CardImpl {
 
         // Exile up to four target cards from a single graveyard.
         this.getSpellAbility().addEffect(new ExileTargetEffect());
-        this.getSpellAbility().addTarget(new TargetCardInASingleGraveyard(0, 4, new FilterCard("cards")));
+        this.getSpellAbility().addTarget(new TargetCardInASingleGraveyard(0, 4, new FilterCard("cards from a single graveyard")));
         // Transmute {1}{B}{B}
         this.addAbility(new TransmuteAbility("{1}{B}{B}"));
     }

From 05950dcc2ff17affe47194e6b525aeddf7ef4a72 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Sat, 20 Jun 2020 02:17:20 +0400
Subject: [PATCH 419/586] Improved text generation for
 TargetCardInASingleGraveyard (additional to #6557)

---
 .../src/mage/cards/c/CarrionBeetles.java      | 48 ++++---------------
 Mage.Sets/src/mage/cards/d/Decompose.java     | 11 ++---
 Mage.Sets/src/mage/cards/f/FamishedGhoul.java | 11 ++---
 Mage.Sets/src/mage/cards/g/Gravegouger.java   |  8 ++--
 Mage.Sets/src/mage/cards/m/MartyrOfBones.java | 12 ++---
 Mage.Sets/src/mage/cards/n/NightSoil.java     | 10 ++--
 Mage.Sets/src/mage/cards/r/RagDealer.java     |  9 ++--
 Mage.Sets/src/mage/cards/r/RapidDecay.java    |  9 ++--
 Mage.Sets/src/mage/cards/r/RatsFeast.java     |  4 +-
 Mage.Sets/src/mage/cards/s/ScarabFeast.java   |  9 ++--
 .../src/mage/cards/s/SereneRemembrance.java   |  5 +-
 Mage.Sets/src/mage/cards/s/ShredMemory.java   | 12 ++---
 .../src/mage/cards/s/SpellweaverHelix.java    | 46 ++----------------
 .../effects/common/ExileTargetEffect.java     | 13 +++--
 .../common/ExileTargetForSourceEffect.java    |  4 ++
 .../src/main/java/mage/filter/FilterCard.java | 14 ++++--
 .../src/main/java/mage/filter/FilterImpl.java |  9 ++--
 .../common/TargetCardInASingleGraveyard.java  | 11 ++---
 18 files changed, 88 insertions(+), 157 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/c/CarrionBeetles.java b/Mage.Sets/src/mage/cards/c/CarrionBeetles.java
index beb63b6366..a8eb3bd570 100644
--- a/Mage.Sets/src/mage/cards/c/CarrionBeetles.java
+++ b/Mage.Sets/src/mage/cards/c/CarrionBeetles.java
@@ -1,40 +1,36 @@
-
 package mage.cards.c;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.common.TapSourceCost;
 import mage.abilities.costs.mana.ManaCostsImpl;
-import mage.abilities.effects.OneShotEffect;
-import mage.cards.Card;
+import mage.abilities.effects.common.ExileTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.SubType;
-import mage.constants.Outcome;
 import mage.constants.Zone;
-import mage.filter.FilterCard;
-import mage.game.Game;
+import mage.filter.StaticFilters;
 import mage.target.common.TargetCardInASingleGraveyard;
 
+import java.util.UUID;
+
 /**
- *
  * @author fireshoes
  */
 public final class CarrionBeetles extends CardImpl {
 
     public CarrionBeetles(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{B}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}");
         this.subtype.add(SubType.INSECT);
         this.power = new MageInt(1);
         this.toughness = new MageInt(1);
 
-        // {2}{B}, {tap}: Exile up to three target cards from a single graveyard.
-        Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CarrionBeetlesExileEffect(), new ManaCostsImpl("{2}{B}"));
+        // {2}{B}, {T}: Exile up to three target cards from a single graveyard.
+        Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileTargetEffect(), new ManaCostsImpl("{2}{B}"));
         ability.addCost(new TapSourceCost());
-        ability.addTarget(new TargetCardInASingleGraveyard(0, 3, new FilterCard("up to three target cards from a single graveyard")));
+        ability.addTarget(new TargetCardInASingleGraveyard(0, 3, StaticFilters.FILTER_CARD_CARDS));
         this.addAbility(ability);
     }
 
@@ -47,31 +43,3 @@ public final class CarrionBeetles extends CardImpl {
         return new CarrionBeetles(this);
     }
 }
-
-class CarrionBeetlesExileEffect extends OneShotEffect {
-
-    public CarrionBeetlesExileEffect() {
-            super(Outcome.Exile);
-            this.staticText = "Exile up to three target cards from a single graveyard";
-    }
-
-    public CarrionBeetlesExileEffect(final CarrionBeetlesExileEffect effect) {
-            super(effect);
-    }
-
-    @Override
-    public CarrionBeetlesExileEffect copy() {
-            return new CarrionBeetlesExileEffect(this);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        for (UUID targetID : source.getTargets().get(0).getTargets()) {
-            Card card = game.getCard(targetID);
-            if (card != null) {
-                card.moveToExile(null, "", source.getSourceId(), game);
-            }
-        }
-        return true;
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/d/Decompose.java b/Mage.Sets/src/mage/cards/d/Decompose.java
index c3cf1b5d0c..4989f1075a 100644
--- a/Mage.Sets/src/mage/cards/d/Decompose.java
+++ b/Mage.Sets/src/mage/cards/d/Decompose.java
@@ -1,26 +1,25 @@
-
 package mage.cards.d;
 
-import java.util.UUID;
 import mage.abilities.effects.common.ExileTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.filter.FilterCard;
+import mage.filter.StaticFilters;
 import mage.target.common.TargetCardInASingleGraveyard;
 
+import java.util.UUID;
+
 /**
- *
  * @author ilcartographer
  */
 public final class Decompose extends CardImpl {
 
     public Decompose(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{B}");
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}");
 
         // Exile up to three target cards from a single graveyard.
         this.getSpellAbility().addEffect(new ExileTargetEffect());
-        this.getSpellAbility().addTarget(new TargetCardInASingleGraveyard(0, 3, new FilterCard("cards from a single graveyard")));
+        this.getSpellAbility().addTarget(new TargetCardInASingleGraveyard(0, 3, StaticFilters.FILTER_CARD_CARDS));
     }
 
     public Decompose(final Decompose card) {
diff --git a/Mage.Sets/src/mage/cards/f/FamishedGhoul.java b/Mage.Sets/src/mage/cards/f/FamishedGhoul.java
index 984bafd610..83277c2064 100644
--- a/Mage.Sets/src/mage/cards/f/FamishedGhoul.java
+++ b/Mage.Sets/src/mage/cards/f/FamishedGhoul.java
@@ -1,7 +1,5 @@
-
 package mage.cards.f;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleActivatedAbility;
@@ -13,17 +11,18 @@ import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.SubType;
 import mage.constants.Zone;
-import mage.filter.FilterCard;
+import mage.filter.StaticFilters;
 import mage.target.common.TargetCardInASingleGraveyard;
 
+import java.util.UUID;
+
 /**
- *
  * @author cbt33
  */
 public final class FamishedGhoul extends CardImpl {
 
     public FamishedGhoul(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}");
         this.subtype.add(SubType.ZOMBIE);
 
         this.power = new MageInt(3);
@@ -32,7 +31,7 @@ public final class FamishedGhoul extends CardImpl {
         // {1}{B}, Sacrifice Famished Ghoul: Exile up to two target cards from a single graveyard.
         Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileTargetEffect(), new ManaCostsImpl("{1}{B}"));
         ability.addCost(new SacrificeSourceCost());
-        ability.addTarget(new TargetCardInASingleGraveyard(0, 2, new FilterCard("cards from a single graveyard")));
+        ability.addTarget(new TargetCardInASingleGraveyard(0, 2, StaticFilters.FILTER_CARD_CARDS));
         this.addAbility(ability);
     }
 
diff --git a/Mage.Sets/src/mage/cards/g/Gravegouger.java b/Mage.Sets/src/mage/cards/g/Gravegouger.java
index 57f99373d0..ea67ffe519 100644
--- a/Mage.Sets/src/mage/cards/g/Gravegouger.java
+++ b/Mage.Sets/src/mage/cards/g/Gravegouger.java
@@ -4,7 +4,6 @@ import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.common.LeavesBattlefieldTriggeredAbility;
-import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.ExileTargetForSourceEffect;
 import mage.abilities.effects.common.ReturnFromExileForSourceEffect;
 import mage.cards.CardImpl;
@@ -12,7 +11,7 @@ import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.SubType;
 import mage.constants.Zone;
-import mage.filter.FilterCard;
+import mage.filter.StaticFilters;
 import mage.target.common.TargetCardInASingleGraveyard;
 
 import java.util.UUID;
@@ -30,9 +29,8 @@ public final class Gravegouger extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Gravegouger enters the battlefield, exile up to two target cards from a single graveyard.
-        Effect effect = new ExileTargetForSourceEffect();
-        Ability ability = new EntersBattlefieldTriggeredAbility(effect, false);
-        ability.addTarget(new TargetCardInASingleGraveyard(0, 2, new FilterCard("cards from a single graveyard")));
+        Ability ability = new EntersBattlefieldTriggeredAbility(new ExileTargetForSourceEffect(), false);
+        ability.addTarget(new TargetCardInASingleGraveyard(0, 2, StaticFilters.FILTER_CARD_CARDS));
         this.addAbility(ability);
 
         // When Gravegouger leaves the battlefield, return the exiled cards to their owner's graveyard.
diff --git a/Mage.Sets/src/mage/cards/m/MartyrOfBones.java b/Mage.Sets/src/mage/cards/m/MartyrOfBones.java
index a90e986f1b..dd47c908df 100644
--- a/Mage.Sets/src/mage/cards/m/MartyrOfBones.java
+++ b/Mage.Sets/src/mage/cards/m/MartyrOfBones.java
@@ -1,4 +1,3 @@
-
 package mage.cards.m;
 
 import mage.MageInt;
@@ -11,7 +10,6 @@ import mage.abilities.costs.VariableCostImpl;
 import mage.abilities.costs.common.RevealTargetFromHandCost;
 import mage.abilities.costs.common.SacrificeSourceCost;
 import mage.abilities.costs.mana.GenericManaCost;
-import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.ExileTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -19,6 +17,7 @@ import mage.constants.CardType;
 import mage.constants.SubType;
 import mage.constants.Zone;
 import mage.filter.FilterCard;
+import mage.filter.StaticFilters;
 import mage.filter.predicate.mageobject.ColorPredicate;
 import mage.game.Game;
 import mage.players.Player;
@@ -41,13 +40,12 @@ public final class MartyrOfBones extends CardImpl {
         this.power = new MageInt(1);
         this.toughness = new MageInt(1);
 
-        //TODO: Make ability properly copiable
+        // TODO: Make ability properly copiable
         // {1}, Reveal X black cards from your hand, Sacrifice Martyr of Bones: Exile up to X target cards from a single graveyard.
-        Effect effect = new ExileTargetEffect(null, null, Zone.GRAVEYARD);
-        Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new GenericManaCost(1));
+        Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileTargetEffect(), new GenericManaCost(1));
         ability.addCost(new RevealVariableBlackCardsFromHandCost());
         ability.addCost(new SacrificeSourceCost());
-        ability.addTarget(new TargetCardInASingleGraveyard(0, 1, new FilterCard("cards in a single graveyard")));
+        ability.addTarget(new TargetCardInASingleGraveyard(0, 1, StaticFilters.FILTER_CARD_CARDS));
         ability.setTargetAdjuster(MartyrOfBonesAdjuster.instance);
         this.addAbility(ability);
     }
@@ -74,7 +72,7 @@ enum MartyrOfBonesAdjuster implements TargetAdjuster {
             }
         }
         ability.getTargets().clear();
-        ability.addTarget(new TargetCardInASingleGraveyard(0, amount, new FilterCard()));
+        ability.addTarget(new TargetCardInASingleGraveyard(0, amount, StaticFilters.FILTER_CARD_CARDS));
     }
 }
 
diff --git a/Mage.Sets/src/mage/cards/n/NightSoil.java b/Mage.Sets/src/mage/cards/n/NightSoil.java
index 7817ea9b16..4e72c20f7e 100644
--- a/Mage.Sets/src/mage/cards/n/NightSoil.java
+++ b/Mage.Sets/src/mage/cards/n/NightSoil.java
@@ -1,7 +1,5 @@
-
 package mage.cards.n;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.common.ExileFromGraveCost;
@@ -15,19 +13,19 @@ import mage.filter.common.FilterCreatureCard;
 import mage.game.permanent.token.SaprolingToken;
 import mage.target.common.TargetCardInASingleGraveyard;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class NightSoil extends CardImpl {
 
     public NightSoil(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{G}{G}");
-
+        super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{G}{G}");
 
         // {1}, Exile two creature cards from a single graveyard: Create a 1/1 green Saproling creature token.
         Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new SaprolingToken()), new GenericManaCost(1));
-        ability.addCost(new ExileFromGraveCost(new TargetCardInASingleGraveyard(2,2, new FilterCreatureCard("two creature cards from a single graveyard"))));
+        ability.addCost(new ExileFromGraveCost(new TargetCardInASingleGraveyard(2, 2, new FilterCreatureCard("two creature cards"))));
         this.addAbility(ability);
     }
 
diff --git a/Mage.Sets/src/mage/cards/r/RagDealer.java b/Mage.Sets/src/mage/cards/r/RagDealer.java
index a10c1d4138..0f97076fed 100644
--- a/Mage.Sets/src/mage/cards/r/RagDealer.java
+++ b/Mage.Sets/src/mage/cards/r/RagDealer.java
@@ -1,7 +1,5 @@
-
 package mage.cards.r;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleActivatedAbility;
@@ -13,11 +11,12 @@ import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.SubType;
 import mage.constants.Zone;
-import mage.filter.FilterCard;
+import mage.filter.StaticFilters;
 import mage.target.common.TargetCardInASingleGraveyard;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX, North
  */
 public final class RagDealer extends CardImpl {
@@ -33,7 +32,7 @@ public final class RagDealer extends CardImpl {
         // {2}{B}, {T}: Exile up to three target cards from a single graveyard.
         Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileTargetEffect(), new ManaCostsImpl("{2}{B}"));
         ability.addCost(new TapSourceCost());
-        ability.addTarget(new TargetCardInASingleGraveyard(0, 3, new FilterCard("cards from a single graveyard")));
+        ability.addTarget(new TargetCardInASingleGraveyard(0, 3, StaticFilters.FILTER_CARD_CARDS));
         this.addAbility(ability);
     }
 
diff --git a/Mage.Sets/src/mage/cards/r/RapidDecay.java b/Mage.Sets/src/mage/cards/r/RapidDecay.java
index 459a1d0596..f83aeca414 100644
--- a/Mage.Sets/src/mage/cards/r/RapidDecay.java
+++ b/Mage.Sets/src/mage/cards/r/RapidDecay.java
@@ -1,18 +1,17 @@
-
 package mage.cards.r;
 
-import java.util.UUID;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.common.ExileTargetEffect;
 import mage.abilities.keyword.CyclingAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.filter.FilterCard;
+import mage.filter.StaticFilters;
 import mage.target.common.TargetCardInASingleGraveyard;
 
+import java.util.UUID;
+
 /**
- *
  * @author fireshoes
  */
 public final class RapidDecay extends CardImpl {
@@ -22,7 +21,7 @@ public final class RapidDecay extends CardImpl {
 
         // Exile up to three target cards from a single graveyard.
         this.getSpellAbility().addEffect(new ExileTargetEffect());
-        this.getSpellAbility().addTarget(new TargetCardInASingleGraveyard(0, 3, new FilterCard("cards from a single graveyard")));
+        this.getSpellAbility().addTarget(new TargetCardInASingleGraveyard(0, 3, StaticFilters.FILTER_CARD_CARDS));
 
         // Cycling {2}
         this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}")));
diff --git a/Mage.Sets/src/mage/cards/r/RatsFeast.java b/Mage.Sets/src/mage/cards/r/RatsFeast.java
index 395650b520..38bc8555eb 100644
--- a/Mage.Sets/src/mage/cards/r/RatsFeast.java
+++ b/Mage.Sets/src/mage/cards/r/RatsFeast.java
@@ -22,7 +22,7 @@ public final class RatsFeast extends CardImpl {
 
         // Exile X target cards from a single graveyard.
         this.getSpellAbility().addEffect(new ExileTargetEffect(
-                "Exile X target cards from a single graveyard.", true
+                "Exile X target cards from a single graveyard", true
         ));
         this.getSpellAbility().setTargetAdjuster(RatsFeastAdjuster.instance);
     }
@@ -44,6 +44,6 @@ enum RatsFeastAdjuster implements TargetAdjuster {
     public void adjustTargets(Ability ability, Game game) {
         int xValue = ability.getManaCostsToPay().getX();
         ability.getTargets().clear();
-        ability.addTarget(new TargetCardInASingleGraveyard(xValue, xValue, StaticFilters.FILTER_CARD));
+        ability.addTarget(new TargetCardInASingleGraveyard(xValue, xValue, StaticFilters.FILTER_CARD_CARDS));
     }
 }
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/s/ScarabFeast.java b/Mage.Sets/src/mage/cards/s/ScarabFeast.java
index 8e8b1dfa6b..6a3e02693c 100644
--- a/Mage.Sets/src/mage/cards/s/ScarabFeast.java
+++ b/Mage.Sets/src/mage/cards/s/ScarabFeast.java
@@ -1,25 +1,26 @@
 package mage.cards.s;
 
-import java.util.UUID;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.common.ExileTargetEffect;
 import mage.abilities.keyword.CyclingAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.filter.FilterCard;
+import mage.filter.StaticFilters;
 import mage.target.common.TargetCardInASingleGraveyard;
 
+import java.util.UUID;
+
 /**
  * @author Stravant
  */
 public final class ScarabFeast extends CardImpl {
     public ScarabFeast(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{B}");
+        super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}");
 
         // Exile up to three target cards from a single graveyard.
         getSpellAbility().addEffect(new ExileTargetEffect());
-        getSpellAbility().addTarget(new TargetCardInASingleGraveyard(0, 3, new FilterCard("cards from a single graveyard")));
+        getSpellAbility().addTarget(new TargetCardInASingleGraveyard(0, 3, StaticFilters.FILTER_CARD_CARDS));
 
         // Cycling {B}
         addAbility(new CyclingAbility(new ManaCostsImpl("{B}")));
diff --git a/Mage.Sets/src/mage/cards/s/SereneRemembrance.java b/Mage.Sets/src/mage/cards/s/SereneRemembrance.java
index 441fd9d807..d674b47ff8 100644
--- a/Mage.Sets/src/mage/cards/s/SereneRemembrance.java
+++ b/Mage.Sets/src/mage/cards/s/SereneRemembrance.java
@@ -8,7 +8,7 @@ import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.constants.Zone;
-import mage.filter.FilterCard;
+import mage.filter.StaticFilters;
 import mage.game.Game;
 import mage.players.Player;
 import mage.target.common.TargetCardInASingleGraveyard;
@@ -23,10 +23,9 @@ public final class SereneRemembrance extends CardImpl {
     public SereneRemembrance(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{G}");
 
-
         // Shuffle Serene Remembrance and up to three target cards from a single graveyard into their owners' libraries.
         this.getSpellAbility().addEffect(new SereneRemembranceEffect());
-        this.getSpellAbility().addTarget(new TargetCardInASingleGraveyard(0, 3, new FilterCard("up to three target cards from a single graveyard")));
+        this.getSpellAbility().addTarget(new TargetCardInASingleGraveyard(0, 3, StaticFilters.FILTER_CARD_CARDS));
 
     }
 
diff --git a/Mage.Sets/src/mage/cards/s/ShredMemory.java b/Mage.Sets/src/mage/cards/s/ShredMemory.java
index b90c52d431..d3005cd630 100644
--- a/Mage.Sets/src/mage/cards/s/ShredMemory.java
+++ b/Mage.Sets/src/mage/cards/s/ShredMemory.java
@@ -1,27 +1,27 @@
-
 package mage.cards.s;
 
-import java.util.UUID;
 import mage.abilities.effects.common.ExileTargetEffect;
 import mage.abilities.keyword.TransmuteAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.filter.FilterCard;
+import mage.filter.StaticFilters;
 import mage.target.common.TargetCardInASingleGraveyard;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class ShredMemory extends CardImpl {
 
     public ShredMemory(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{B}");
+        super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{B}");
 
         // Exile up to four target cards from a single graveyard.
         this.getSpellAbility().addEffect(new ExileTargetEffect());
-        this.getSpellAbility().addTarget(new TargetCardInASingleGraveyard(0, 4, new FilterCard("cards from a single graveyard")));
+        this.getSpellAbility().addTarget(new TargetCardInASingleGraveyard(0, 4, StaticFilters.FILTER_CARD_CARDS));
+
         // Transmute {1}{B}{B}
         this.addAbility(new TransmuteAbility("{1}{B}{B}"));
     }
diff --git a/Mage.Sets/src/mage/cards/s/SpellweaverHelix.java b/Mage.Sets/src/mage/cards/s/SpellweaverHelix.java
index 25c5b0f25f..cc3f0bbd38 100644
--- a/Mage.Sets/src/mage/cards/s/SpellweaverHelix.java
+++ b/Mage.Sets/src/mage/cards/s/SpellweaverHelix.java
@@ -1,12 +1,12 @@
 package mage.cards.s;
 
-import java.util.UUID;
 import mage.MageObjectReference;
 import mage.abilities.Ability;
 import mage.abilities.TriggeredAbilityImpl;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.ExileTargetForSourceEffect;
 import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -21,15 +21,15 @@ import mage.game.stack.Spell;
 import mage.players.Player;
 import mage.target.common.TargetCardInASingleGraveyard;
 import mage.target.targetpointer.FixedTarget;
-import mage.util.CardUtil;
+
+import java.util.UUID;
 
 /**
- *
  * @author emerald000
  */
 public final class SpellweaverHelix extends CardImpl {
 
-    private static final FilterCard filter = new FilterCard("sorcery cards from a single graveyard");
+    private static final FilterCard filter = new FilterCard("sorcery cards");
 
     static {
         filter.add(CardType.SORCERY.getPredicate());
@@ -39,7 +39,7 @@ public final class SpellweaverHelix extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}");
 
         // Imprint - When Spellweaver Helix enters the battlefield, you may exile two target sorcery cards from a single graveyard.
-        Ability ability = new EntersBattlefieldTriggeredAbility(new SpellweaverHelixImprintEffect(), true, "Imprint &mdash; ");
+        Ability ability = new EntersBattlefieldTriggeredAbility(new ExileTargetForSourceEffect(), true, "Imprint &mdash; ");
         ability.addTarget(new TargetCardInASingleGraveyard(2, 2, filter));
         this.addAbility(ability);
 
@@ -57,42 +57,6 @@ public final class SpellweaverHelix extends CardImpl {
     }
 }
 
-class SpellweaverHelixImprintEffect extends OneShotEffect {
-
-    SpellweaverHelixImprintEffect() {
-        super(Outcome.Exile);
-        this.staticText = "you may exile two target sorcery cards from a single graveyard";
-    }
-
-    SpellweaverHelixImprintEffect(final SpellweaverHelixImprintEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public SpellweaverHelixImprintEffect copy() {
-        return new SpellweaverHelixImprintEffect(this);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        Player controller = game.getPlayer(source.getControllerId());
-        if (controller != null) {
-            Permanent sourcePermanent = game.getPermanent(source.getSourceId());
-            for (UUID targetId : this.getTargetPointer().getTargets(game, source)) {
-                Card card = game.getCard(targetId);
-                if (card != null) {
-                    controller.moveCardsToExile(card, source, game, true, CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()), source.getSourceObject(game).getIdName());
-                    if (sourcePermanent != null) {
-                        sourcePermanent.imprint(targetId, game);
-                    }
-                }
-            }
-            return true;
-        }
-        return false;
-    }
-}
-
 class SpellweaverHelixTriggeredAbility extends TriggeredAbilityImpl {
 
     SpellweaverHelixTriggeredAbility() {
diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileTargetEffect.java
index 0d1285a3f9..7978af7f73 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/ExileTargetEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/ExileTargetEffect.java
@@ -1,8 +1,5 @@
 package mage.abilities.effects.common;
 
-import java.util.LinkedHashSet;
-import java.util.Set;
-import java.util.UUID;
 import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.Mode;
@@ -20,13 +17,16 @@ import mage.target.targetpointer.FirstTargetPointer;
 import mage.target.targetpointer.SecondTargetPointer;
 import mage.util.CardUtil;
 
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.util.UUID;
+
 /**
- *
  * @author BetaSteward_at_googlemail.com
  */
 public class ExileTargetEffect extends OneShotEffect {
 
-    private Zone onlyFromZone;
+    private final Zone onlyFromZone;
     private String exileZone = null;
     private UUID exileId = null;
     protected boolean multitargetHandling;
@@ -42,6 +42,9 @@ public class ExileTargetEffect extends OneShotEffect {
         this.multitargetHandling = multitargetHandling;
     }
 
+    /**
+     * Exile cards to normal exile window (but it can exile to source's exile window after toSourceExileZone change)
+     */
     public ExileTargetEffect() {
         this(null, "");
     }
diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileTargetForSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileTargetForSourceEffect.java
index bee973d554..6be4f53124 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/ExileTargetForSourceEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/ExileTargetForSourceEffect.java
@@ -21,6 +21,10 @@ import java.util.UUID;
  */
 public class ExileTargetForSourceEffect extends OneShotEffect {
 
+    /**
+     * Exile cards to source's exile window (e.g. if it have another effect like return from exile later)
+     * TODO: delete that effect and replace it by ExileTargetEffect (it have special param for same purpose)
+     */
     public ExileTargetForSourceEffect() {
         super(Outcome.Exile);
     }
diff --git a/Mage/src/main/java/mage/filter/FilterCard.java b/Mage/src/main/java/mage/filter/FilterCard.java
index b9c3e18fa6..f5560b493f 100644
--- a/Mage/src/main/java/mage/filter/FilterCard.java
+++ b/Mage/src/main/java/mage/filter/FilterCard.java
@@ -1,14 +1,15 @@
 package mage.filter;
 
+import mage.cards.Card;
+import mage.constants.TargetController;
+import mage.filter.predicate.*;
+import mage.game.Game;
+
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
 import java.util.UUID;
 import java.util.stream.Collectors;
-import mage.cards.Card;
-import mage.constants.TargetController;
-import mage.filter.predicate.*;
-import mage.game.Game;
 
 /**
  * @author BetaSteward_at_googlemail.com
@@ -95,4 +96,9 @@ public class FilterCard extends FilterObject<Card> {
             throw new IllegalArgumentException("Card filter doesn't support controller predicate");
         }
     }
+
+    public FilterCard withMessage(String message) {
+        this.setMessage(message);
+        return this;
+    }
 }
diff --git a/Mage/src/main/java/mage/filter/FilterImpl.java b/Mage/src/main/java/mage/filter/FilterImpl.java
index b30a61f4f4..704acf389e 100644
--- a/Mage/src/main/java/mage/filter/FilterImpl.java
+++ b/Mage/src/main/java/mage/filter/FilterImpl.java
@@ -1,16 +1,16 @@
 package mage.filter;
 
-import java.util.ArrayList;
-import java.util.List;
 import mage.filter.predicate.Predicate;
 import mage.filter.predicate.Predicates;
 import mage.game.Game;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
- *
+ * @param <E>
  * @author BetaSteward_at_googlemail.com
  * @author North
- * @param <E>
  */
 public abstract class FilterImpl<E> implements Filter<E> {
 
@@ -78,5 +78,4 @@ public abstract class FilterImpl<E> implements Filter<E> {
     public List<Predicate<? super E>> getPredicates() {
         return predicates;
     }
-
 }
diff --git a/Mage/src/main/java/mage/target/common/TargetCardInASingleGraveyard.java b/Mage/src/main/java/mage/target/common/TargetCardInASingleGraveyard.java
index 86721777d3..88e0f66386 100644
--- a/Mage/src/main/java/mage/target/common/TargetCardInASingleGraveyard.java
+++ b/Mage/src/main/java/mage/target/common/TargetCardInASingleGraveyard.java
@@ -1,8 +1,5 @@
-
-
 package mage.target.common;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.cards.Card;
 import mage.constants.Zone;
@@ -10,17 +7,17 @@ import mage.filter.FilterCard;
 import mage.game.Game;
 import mage.target.TargetCard;
 
+import java.util.UUID;
 
 /**
- *
  * @author LevelX2
  */
 
-
 public class TargetCardInASingleGraveyard extends TargetCard {
 
     public TargetCardInASingleGraveyard(int minNumTargets, int maxNumTargets, FilterCard filter) {
-        super(minNumTargets, maxNumTargets, Zone.GRAVEYARD, filter);
+        // workaround to add extra message to final ability text
+        super(minNumTargets, maxNumTargets, Zone.GRAVEYARD, filter.copy().withMessage(filter.getMessage() + " from a single graveyard"));
     }
 
     public TargetCardInASingleGraveyard(final TargetCardInASingleGraveyard target) {
@@ -41,9 +38,9 @@ public class TargetCardInASingleGraveyard extends TargetCard {
         return super.canTarget(id, source, game);
     }
 
-
     @Override
     public TargetCardInASingleGraveyard copy() {
         return new TargetCardInASingleGraveyard(this);
     }
+
 }

From ed2e524753bc092949ccd981f99259ab7a0eeeda Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Fri, 19 Jun 2020 18:56:31 -0400
Subject: [PATCH 420/586] updated JMP spoiler and reprints

---
 Mage.Sets/src/mage/sets/Jumpstart.java |  95 +++++++++++++++++++++-
 Utils/mtg-cards-data.txt               | 105 ++++++++++++++++++++++++-
 2 files changed, 194 insertions(+), 6 deletions(-)

diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index 95455a6afe..408c38b1e6 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -20,6 +20,9 @@ public final class Jumpstart extends ExpansionSet {
         this.blockName = "Jumpstart";
         this.hasBasicLands = true;
 
+        cards.add(new SetCardInfo("Aegis Turtle", 138, Rarity.COMMON, mage.cards.a.AegisTurtle.class));
+        cards.add(new SetCardInfo("Aegis of the Heavens", 79, Rarity.UNCOMMON, mage.cards.a.AegisOfTheHeavens.class));
+        cards.add(new SetCardInfo("Aerial Assault", 80, Rarity.COMMON, mage.cards.a.AerialAssault.class));
         cards.add(new SetCardInfo("Aether Spellbomb", 456, Rarity.COMMON, mage.cards.a.AetherSpellbomb.class));
         cards.add(new SetCardInfo("Affa Guard Hound", 81, Rarity.UNCOMMON, mage.cards.a.AffaGuardHound.class));
         cards.add(new SetCardInfo("Affectionate Indrik", 373, Rarity.UNCOMMON, mage.cards.a.AffectionateIndrik.class));
@@ -28,14 +31,19 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Alloy Myr", 457, Rarity.COMMON, mage.cards.a.AlloyMyr.class));
         cards.add(new SetCardInfo("Ambassador Oak", 375, Rarity.COMMON, mage.cards.a.AmbassadorOak.class));
         cards.add(new SetCardInfo("Ancestral Statue", 458, Rarity.COMMON, mage.cards.a.AncestralStatue.class));
+        cards.add(new SetCardInfo("Angel of the Dire Hour", 85, Rarity.RARE, mage.cards.a.AngelOfTheDireHour.class));
         cards.add(new SetCardInfo("Arcane Encyclopedia", 459, Rarity.UNCOMMON, mage.cards.a.ArcaneEncyclopedia.class));
         cards.add(new SetCardInfo("Archaeomender", 9, Rarity.COMMON, mage.cards.a.Archaeomender.class));
+        cards.add(new SetCardInfo("Archon of Justice", 89, Rarity.RARE, mage.cards.a.ArchonOfJustice.class));
+        cards.add(new SetCardInfo("Archon of Redemption", 90, Rarity.RARE, mage.cards.a.ArchonOfRedemption.class));
         cards.add(new SetCardInfo("Ashmouth Hound", 290, Rarity.COMMON, mage.cards.a.AshmouthHound.class));
         cards.add(new SetCardInfo("Assassin's Strike", 200, Rarity.UNCOMMON, mage.cards.a.AssassinsStrike.class));
         cards.add(new SetCardInfo("Assault Formation", 378, Rarity.RARE, mage.cards.a.AssaultFormation.class));
         cards.add(new SetCardInfo("Auger Spree", 449, Rarity.COMMON, mage.cards.a.AugerSpree.class));
         cards.add(new SetCardInfo("Awakener Druid", 379, Rarity.UNCOMMON, mage.cards.a.AwakenerDruid.class));
         cards.add(new SetCardInfo("Bake into a Pie", 201, Rarity.COMMON, mage.cards.b.BakeIntoAPie.class));
+        cards.add(new SetCardInfo("Ball Lightning", 291, Rarity.RARE, mage.cards.b.BallLightning.class));
+        cards.add(new SetCardInfo("Battlefield Promotion", 91, Rarity.COMMON, mage.cards.b.BattlefieldPromotion.class));
         cards.add(new SetCardInfo("Befuddle", 140, Rarity.COMMON, mage.cards.b.Befuddle.class));
         cards.add(new SetCardInfo("Black Cat", 203, Rarity.COMMON, mage.cards.b.BlackCat.class));
         cards.add(new SetCardInfo("Blighted Bat", 205, Rarity.COMMON, mage.cards.b.BlightedBat.class));
@@ -48,7 +56,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Bogbrew Witch", 211, Rarity.RARE, mage.cards.b.BogbrewWitch.class));
         cards.add(new SetCardInfo("Bone Picker", 212, Rarity.UNCOMMON, mage.cards.b.BonePicker.class));
         cards.add(new SetCardInfo("Bone Splinters", 213, Rarity.COMMON, mage.cards.b.BoneSplinters.class));
-        cards.add(new SetCardInfo("Brightmare", 2, Rarity.COMMON, mage.cards.b.Brightmare.class));
+        cards.add(new SetCardInfo("Brightmare", 2, Rarity.UNCOMMON, mage.cards.b.Brightmare.class));
         cards.add(new SetCardInfo("Brindle Shoat", 380, Rarity.UNCOMMON, mage.cards.b.BrindleShoat.class));
         cards.add(new SetCardInfo("Brushstrider", 381, Rarity.UNCOMMON, mage.cards.b.Brushstrider.class));
         cards.add(new SetCardInfo("Bubbling Cauldron", 460, Rarity.UNCOMMON, mage.cards.b.BubblingCauldron.class));
@@ -57,26 +65,38 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Cadaver Imp", 215, Rarity.COMMON, mage.cards.c.CadaverImp.class));
         cards.add(new SetCardInfo("Carven Caryatid", 382, Rarity.UNCOMMON, mage.cards.c.CarvenCaryatid.class));
         cards.add(new SetCardInfo("Cathar's Companion", 94, Rarity.COMMON, mage.cards.c.CatharsCompanion.class));
+        cards.add(new SetCardInfo("Cathars' Crusade", 95, Rarity.RARE, mage.cards.c.CatharsCrusade.class));
         cards.add(new SetCardInfo("Cauldron Familiar", 216, Rarity.COMMON, mage.cards.c.CauldronFamiliar.class));
         cards.add(new SetCardInfo("Cemetery Recruitment", 217, Rarity.COMMON, mage.cards.c.CemeteryRecruitment.class));
+        cards.add(new SetCardInfo("Chain Lightning", 302, Rarity.UNCOMMON, mage.cards.c.ChainLightning.class));
         cards.add(new SetCardInfo("Chamber Sentry", 461, Rarity.RARE, mage.cards.c.ChamberSentry.class));
+        cards.add(new SetCardInfo("Chart a Course", 142, Rarity.UNCOMMON, mage.cards.c.ChartACourse.class));
         cards.add(new SetCardInfo("Child of Night", 218, Rarity.COMMON, mage.cards.c.ChildOfNight.class));
         cards.add(new SetCardInfo("Chromatic Sphere", 462, Rarity.COMMON, mage.cards.c.ChromaticSphere.class));
         cards.add(new SetCardInfo("Cinder Elemental", 304, Rarity.UNCOMMON, mage.cards.c.CinderElemental.class));
         cards.add(new SetCardInfo("Cloudreader Sphinx", 143, Rarity.COMMON, mage.cards.c.CloudreaderSphinx.class));
+        cards.add(new SetCardInfo("Cloudshift", 97, Rarity.COMMON, mage.cards.c.Cloudshift.class));
+        cards.add(new SetCardInfo("Coastal Piracy", 144, Rarity.UNCOMMON, mage.cards.c.CoastalPiracy.class));
         cards.add(new SetCardInfo("Commune with Dinosaurs", 384, Rarity.COMMON, mage.cards.c.CommuneWithDinosaurs.class));
         cards.add(new SetCardInfo("Craterhoof Behemoth", 385, Rarity.MYTHIC, mage.cards.c.CraterhoofBehemoth.class));
         cards.add(new SetCardInfo("Crookclaw Transmuter", 145, Rarity.COMMON, mage.cards.c.CrookclawTransmuter.class));
         cards.add(new SetCardInfo("Crow of Dark Tidings", 221, Rarity.COMMON, mage.cards.c.CrowOfDarkTidings.class));
         cards.add(new SetCardInfo("Crushing Canopy", 386, Rarity.COMMON, mage.cards.c.CrushingCanopy.class));
+        cards.add(new SetCardInfo("Cryptic Serpent", 146, Rarity.UNCOMMON, mage.cards.c.CrypticSerpent.class));
         cards.add(new SetCardInfo("Curiosity", 147, Rarity.UNCOMMON, mage.cards.c.Curiosity.class));
+        cards.add(new SetCardInfo("Curious Obsession", 148, Rarity.UNCOMMON, mage.cards.c.CuriousObsession.class));
+        cards.add(new SetCardInfo("Dauntless Onslaught", 99, Rarity.UNCOMMON, mage.cards.d.DauntlessOnslaught.class));
         cards.add(new SetCardInfo("Dawntreader Elk", 387, Rarity.COMMON, mage.cards.d.DawntreaderElk.class));
         cards.add(new SetCardInfo("Death's Approach", 222, Rarity.COMMON, mage.cards.d.DeathsApproach.class));
+        cards.add(new SetCardInfo("Departed Deckhand", 149, Rarity.UNCOMMON, mage.cards.d.DepartedDeckhand.class));
         cards.add(new SetCardInfo("Dinrova Horror", 450, Rarity.UNCOMMON, mage.cards.d.DinrovaHorror.class));
+        cards.add(new SetCardInfo("Divine Arrow", 100, Rarity.COMMON, mage.cards.d.DivineArrow.class));
+        cards.add(new SetCardInfo("Douse in Gloom", 223, Rarity.COMMON, mage.cards.d.DouseInGloom.class));
         cards.add(new SetCardInfo("Drainpipe Vermin", 224, Rarity.COMMON, mage.cards.d.DrainpipeVermin.class));
         cards.add(new SetCardInfo("Drana, Liberator of Malakir", 225, Rarity.MYTHIC, mage.cards.d.DranaLiberatorOfMalakir.class));
         cards.add(new SetCardInfo("Dreamstone Hedron", 464, Rarity.UNCOMMON, mage.cards.d.DreamstoneHedron.class));
         cards.add(new SetCardInfo("Drover of the Mighty", 388, Rarity.UNCOMMON, mage.cards.d.DroverOfTheMighty.class));
+        cards.add(new SetCardInfo("Duelist's Heritage", 101, Rarity.RARE, mage.cards.d.DuelistsHeritage.class));
         cards.add(new SetCardInfo("Dutiful Attendant", 226, Rarity.COMMON, mage.cards.d.DutifulAttendant.class));
         cards.add(new SetCardInfo("Dwynen's Elite", 389, Rarity.UNCOMMON, mage.cards.d.DwynensElite.class));
         cards.add(new SetCardInfo("Elemental Uprising", 390, Rarity.COMMON, mage.cards.e.ElementalUprising.class));
@@ -96,6 +116,8 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Fanatical Firebrand", 315, Rarity.COMMON, mage.cards.f.FanaticalFirebrand.class));
         cards.add(new SetCardInfo("Fell Specter", 233, Rarity.UNCOMMON, mage.cards.f.FellSpecter.class));
         cards.add(new SetCardInfo("Feral Hydra", 395, Rarity.UNCOMMON, mage.cards.f.FeralHydra.class));
+        cards.add(new SetCardInfo("Feral Invocation", 396, Rarity.COMMON, mage.cards.f.FeralInvocation.class));
+        cards.add(new SetCardInfo("Feral Prowler", 397, Rarity.COMMON, mage.cards.f.FeralProwler.class));
         cards.add(new SetCardInfo("Fertilid", 398, Rarity.COMMON, mage.cards.f.Fertilid.class));
         cards.add(new SetCardInfo("Festering Newt", 234, Rarity.COMMON, mage.cards.f.FesteringNewt.class));
         cards.add(new SetCardInfo("Flames of the Firebrand", 317, Rarity.UNCOMMON, mage.cards.f.FlamesOfTheFirebrand.class));
@@ -108,82 +130,128 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Ghoulcaller Gisa", 236, Rarity.MYTHIC, mage.cards.g.GhoulcallerGisa.class));
         cards.add(new SetCardInfo("Ghoulcaller's Accomplice", 237, Rarity.COMMON, mage.cards.g.GhoulcallersAccomplice.class));
         cards.add(new SetCardInfo("Ghoulraiser", 238, Rarity.COMMON, mage.cards.g.Ghoulraiser.class));
+        cards.add(new SetCardInfo("Gifted Aetherborn", 239, Rarity.UNCOMMON, mage.cards.g.GiftedAetherborn.class));
         cards.add(new SetCardInfo("Gingerbrute", 466, Rarity.COMMON, mage.cards.g.Gingerbrute.class));
+        cards.add(new SetCardInfo("Gird for Battle", 106, Rarity.UNCOMMON, mage.cards.g.GirdForBattle.class));
         cards.add(new SetCardInfo("Grave Bramble", 401, Rarity.COMMON, mage.cards.g.GraveBramble.class));
         cards.add(new SetCardInfo("Gravewaker", 241, Rarity.RARE, mage.cards.g.Gravewaker.class));
         cards.add(new SetCardInfo("Grim Lavamancer", 331, Rarity.RARE, mage.cards.g.GrimLavamancer.class));
+        cards.add(new SetCardInfo("Healer's Hawk", 107, Rarity.COMMON, mage.cards.h.HealersHawk.class));
         cards.add(new SetCardInfo("Hedron Archive", 468, Rarity.UNCOMMON, mage.cards.h.HedronArchive.class));
+        cards.add(new SetCardInfo("Herald's Horn", 469, Rarity.UNCOMMON, mage.cards.h.HeraldsHorn.class));
+        cards.add(new SetCardInfo("High Sentinels of Arashin", 108, Rarity.RARE, mage.cards.h.HighSentinelsOfArashin.class));
+        cards.add(new SetCardInfo("Homing Lightning", 335, Rarity.UNCOMMON, mage.cards.h.HomingLightning.class));
         cards.add(new SetCardInfo("Hungry Flames", 336, Rarity.COMMON, mage.cards.h.HungryFlames.class));
         cards.add(new SetCardInfo("Hunter's Insight", 402, Rarity.UNCOMMON, mage.cards.h.HuntersInsight.class));
+        cards.add(new SetCardInfo("Initiate's Companion", 403, Rarity.COMMON, mage.cards.i.InitiatesCompanion.class));
         cards.add(new SetCardInfo("Innocent Blood", 244, Rarity.COMMON, mage.cards.i.InnocentBlood.class));
+        cards.add(new SetCardInfo("Inspired Charge", 110, Rarity.COMMON, mage.cards.i.InspiredCharge.class));
+        cards.add(new SetCardInfo("Inspiring Captain", 111, Rarity.COMMON, mage.cards.i.InspiringCaptain.class));
+        cards.add(new SetCardInfo("Inspiring Unicorn", 112, Rarity.UNCOMMON, mage.cards.i.InspiringUnicorn.class));
         cards.add(new SetCardInfo("Ironroot Warlord", 452, Rarity.UNCOMMON, mage.cards.i.IronrootWarlord.class));
         cards.add(new SetCardInfo("Irresistible Prey", 406, Rarity.UNCOMMON, mage.cards.i.IrresistiblePrey.class));
         cards.add(new SetCardInfo("Isamaru, Hound of Konda", 113, Rarity.RARE, mage.cards.i.IsamaruHoundOfKonda.class));
-        cards.add(new SetCardInfo("Island", 49, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Island", 48, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Jousting Dummy", 470, Rarity.COMMON, mage.cards.j.JoustingDummy.class));
         cards.add(new SetCardInfo("Juggernaut", 471, Rarity.UNCOMMON, mage.cards.j.Juggernaut.class));
         cards.add(new SetCardInfo("Kalastria Nightwatch", 245, Rarity.COMMON, mage.cards.k.KalastriaNightwatch.class));
+        cards.add(new SetCardInfo("Keeper of Fables", 407, Rarity.UNCOMMON, mage.cards.k.KeeperOfFables.class));
         cards.add(new SetCardInfo("Kels, Fight Fixer", 15, Rarity.RARE, mage.cards.k.KelsFightFixer.class));
+        cards.add(new SetCardInfo("Kira, Great Glass-Spinner", 154, Rarity.RARE, mage.cards.k.KiraGreatGlassSpinner.class));
+        cards.add(new SetCardInfo("Kitesail Corsair", 155, Rarity.COMMON, mage.cards.k.KitesailCorsair.class));
         cards.add(new SetCardInfo("Knight of the Tusk", 114, Rarity.COMMON, mage.cards.k.KnightOfTheTusk.class));
         cards.add(new SetCardInfo("Last Gasp", 247, Rarity.COMMON, mage.cards.l.LastGasp.class));
         cards.add(new SetCardInfo("Lawmage's Binding", 453, Rarity.COMMON, mage.cards.l.LawmagesBinding.class));
         cards.add(new SetCardInfo("Leaf Gilder", 408, Rarity.COMMON, mage.cards.l.LeafGilder.class));
         cards.add(new SetCardInfo("Leave in the Dust", 156, Rarity.COMMON, mage.cards.l.LeaveInTheDust.class));
+        cards.add(new SetCardInfo("Lightning Axe", 341, Rarity.UNCOMMON, mage.cards.l.LightningAxe.class));
+        cards.add(new SetCardInfo("Lightning Bolt", 342, Rarity.UNCOMMON, mage.cards.l.LightningBolt.class));
+        cards.add(new SetCardInfo("Lightning Diadem", 343, Rarity.COMMON, mage.cards.l.LightningDiadem.class));
+        cards.add(new SetCardInfo("Lightning Elemental", 344, Rarity.COMMON, mage.cards.l.LightningElemental.class));
+        cards.add(new SetCardInfo("Lightning Shrieker", 345, Rarity.COMMON, mage.cards.l.LightningShrieker.class));
         cards.add(new SetCardInfo("Lightning Visionary", 22, Rarity.COMMON, mage.cards.l.LightningVisionary.class));
         cards.add(new SetCardInfo("Lightning-Core Excavator", 32, Rarity.COMMON, mage.cards.l.LightningCoreExcavator.class));
+        cards.add(new SetCardInfo("Lightwalker", 118, Rarity.COMMON, mage.cards.l.Lightwalker.class));
         cards.add(new SetCardInfo("Liliana's Elite", 250, Rarity.UNCOMMON, mage.cards.l.LilianasElite.class));
+        cards.add(new SetCardInfo("Liliana's Reaver", 251, Rarity.RARE, mage.cards.l.LilianasReaver.class));
         cards.add(new SetCardInfo("Living Lightning", 23, Rarity.UNCOMMON, mage.cards.l.LivingLightning.class));
+        cards.add(new SetCardInfo("Long Road Home", 120, Rarity.UNCOMMON, mage.cards.l.LongRoadHome.class));
         cards.add(new SetCardInfo("Lurking Predators", 410, Rarity.RARE, mage.cards.l.LurkingPredators.class));
         cards.add(new SetCardInfo("Macabre Waltz", 252, Rarity.COMMON, mage.cards.m.MacabreWaltz.class));
         cards.add(new SetCardInfo("Maelstrom Archangel", 454, Rarity.MYTHIC, mage.cards.m.MaelstromArchangel.class));
         cards.add(new SetCardInfo("Magma Jet", 346, Rarity.UNCOMMON, mage.cards.m.MagmaJet.class));
         cards.add(new SetCardInfo("Magmaquake", 347, Rarity.RARE, mage.cards.m.Magmaquake.class));
         cards.add(new SetCardInfo("Malakir Familiar", 253, Rarity.UNCOMMON, mage.cards.m.MalakirFamiliar.class));
+        cards.add(new SetCardInfo("Mana Geode", 472, Rarity.COMMON, mage.cards.m.ManaGeode.class));
         cards.add(new SetCardInfo("Marauder's Axe", 473, Rarity.COMMON, mage.cards.m.MaraudersAxe.class));
         cards.add(new SetCardInfo("Mark of the Vampire", 254, Rarity.COMMON, mage.cards.m.MarkOfTheVampire.class));
+        cards.add(new SetCardInfo("Mentor of the Meek", 121, Rarity.RARE, mage.cards.m.MentorOfTheMeek.class));
+        cards.add(new SetCardInfo("Mesa Unicorn", 122, Rarity.COMMON, mage.cards.m.MesaUnicorn.class));
         cards.add(new SetCardInfo("Meteor Golem", 474, Rarity.UNCOMMON, mage.cards.m.MeteorGolem.class));
         cards.add(new SetCardInfo("Miasmic Mummy", 256, Rarity.COMMON, mage.cards.m.MiasmicMummy.class));
+        cards.add(new SetCardInfo("Mikaeus, the Lunarch", 123, Rarity.MYTHIC, mage.cards.m.MikaeusTheLunarch.class));
         cards.add(new SetCardInfo("Mire Triton", 257, Rarity.UNCOMMON, mage.cards.m.MireTriton.class));
         cards.add(new SetCardInfo("Mirrodin's Core", 492, Rarity.UNCOMMON, mage.cards.m.MirrodinsCore.class));
         cards.add(new SetCardInfo("Molten Ravager", 351, Rarity.COMMON, mage.cards.m.MoltenRavager.class));
+        cards.add(new SetCardInfo("Moment of Heroism", 124, Rarity.COMMON, mage.cards.m.MomentOfHeroism.class));
         cards.add(new SetCardInfo("Momentous Fall", 411, Rarity.RARE, mage.cards.m.MomentousFall.class));
         cards.add(new SetCardInfo("Mountain", 64, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Myr Sire", 475, Rarity.COMMON, mage.cards.m.MyrSire.class));
+        cards.add(new SetCardInfo("Mystic Archaeologist", 158, Rarity.RARE, mage.cards.m.MysticArchaeologist.class));
         cards.add(new SetCardInfo("Narcolepsy", 159, Rarity.COMMON, mage.cards.n.Narcolepsy.class));
         cards.add(new SetCardInfo("Nature's Way", 412, Rarity.UNCOMMON, mage.cards.n.NaturesWay.class));
         cards.add(new SetCardInfo("New Horizons", 414, Rarity.COMMON, mage.cards.n.NewHorizons.class));
         cards.add(new SetCardInfo("Nocturnal Feeder", 16, Rarity.COMMON, mage.cards.n.NocturnalFeeder.class));
         cards.add(new SetCardInfo("Nyxathid", 259, Rarity.RARE, mage.cards.n.Nyxathid.class));
+        cards.add(new SetCardInfo("Octoprophet", 161, Rarity.COMMON, mage.cards.o.Octoprophet.class));
         cards.add(new SetCardInfo("Oneirophage", 162, Rarity.UNCOMMON, mage.cards.o.Oneirophage.class));
         cards.add(new SetCardInfo("Oracle of Mul Daya", 415, Rarity.RARE, mage.cards.o.OracleOfMulDaya.class));
         cards.add(new SetCardInfo("Orazca Frillback", 416, Rarity.COMMON, mage.cards.o.OrazcaFrillback.class));
         cards.add(new SetCardInfo("Ormos, Archive Keeper", 13, Rarity.RARE, mage.cards.o.OrmosArchiveKeeper.class));
         cards.add(new SetCardInfo("Overgrown Battlement", 417, Rarity.UNCOMMON, mage.cards.o.OvergrownBattlement.class));
         cards.add(new SetCardInfo("Pacifism", 125, Rarity.COMMON, mage.cards.p.Pacifism.class));
+        cards.add(new SetCardInfo("Parasitic Implant", 262, Rarity.COMMON, mage.cards.p.ParasiticImplant.class));
+        cards.add(new SetCardInfo("Path to Exile", 127, Rarity.UNCOMMON, mage.cards.p.PathToExile.class));
+        cards.add(new SetCardInfo("Patron of the Valiant", 128, Rarity.UNCOMMON, mage.cards.p.PatronOfTheValiant.class));
         cards.add(new SetCardInfo("Peel from Reality", 163, Rarity.COMMON, mage.cards.p.PeelFromReality.class));
         cards.add(new SetCardInfo("Penumbra Bobcat", 418, Rarity.COMMON, mage.cards.p.PenumbraBobcat.class));
+        cards.add(new SetCardInfo("Perilous Myr", 476, Rarity.UNCOMMON, mage.cards.p.PerilousMyr.class));
+        cards.add(new SetCardInfo("Phyrexian Broodlings", 263, Rarity.COMMON, mage.cards.p.PhyrexianBroodlings.class));
+        cards.add(new SetCardInfo("Phyrexian Debaser", 264, Rarity.COMMON, mage.cards.p.PhyrexianDebaser.class));
+        cards.add(new SetCardInfo("Phyrexian Gargantua", 265, Rarity.UNCOMMON, mage.cards.p.PhyrexianGargantua.class));
         cards.add(new SetCardInfo("Phyrexian Rager", 266, Rarity.COMMON, mage.cards.p.PhyrexianRager.class));
+        cards.add(new SetCardInfo("Phyrexian Reclamation", 267, Rarity.UNCOMMON, mage.cards.p.PhyrexianReclamation.class));
         cards.add(new SetCardInfo("Phyrexian Tower", 493, Rarity.RARE, mage.cards.p.PhyrexianTower.class));
         cards.add(new SetCardInfo("Pillar of Flame", 355, Rarity.COMMON, mage.cards.p.PillarOfFlame.class));
-        cards.add(new SetCardInfo("Plains", 41, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Pirate's Cutlass", 477, Rarity.COMMON, mage.cards.p.PiratesCutlass.class));
+        cards.add(new SetCardInfo("Plains", 38, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Pouncing Cheetah", 419, Rarity.COMMON, mage.cards.p.PouncingCheetah.class));
         cards.add(new SetCardInfo("Prescient Chimera", 164, Rarity.COMMON, mage.cards.p.PrescientChimera.class));
         cards.add(new SetCardInfo("Presence of Gond", 420, Rarity.COMMON, mage.cards.p.PresenceOfGond.class));
         cards.add(new SetCardInfo("Primordial Sage", 422, Rarity.RARE, mage.cards.p.PrimordialSage.class));
         cards.add(new SetCardInfo("Prophetic Prism", 478, Rarity.COMMON, mage.cards.p.PropheticPrism.class));
+        cards.add(new SetCardInfo("Prosperous Pirates", 165, Rarity.COMMON, mage.cards.p.ProsperousPirates.class));
         cards.add(new SetCardInfo("Pyroclastic Elemental", 356, Rarity.UNCOMMON, mage.cards.p.PyroclasticElemental.class));
+        cards.add(new SetCardInfo("Rageblood Shaman", 357, Rarity.RARE, mage.cards.r.RagebloodShaman.class));
         cards.add(new SetCardInfo("Raging Regisaur", 455, Rarity.UNCOMMON, mage.cards.r.RagingRegisaur.class));
         cards.add(new SetCardInfo("Rampaging Brontodon", 423, Rarity.RARE, mage.cards.r.RampagingBrontodon.class));
         cards.add(new SetCardInfo("Ravenous Baloth", 424, Rarity.RARE, mage.cards.r.RavenousBaloth.class));
         cards.add(new SetCardInfo("Ravenous Chupacabra", 269, Rarity.UNCOMMON, mage.cards.r.RavenousChupacabra.class));
+        cards.add(new SetCardInfo("Read the Runes", 167, Rarity.RARE, mage.cards.r.ReadTheRunes.class));
         cards.add(new SetCardInfo("Reanimate", 270, Rarity.RARE, mage.cards.r.Reanimate.class));
         cards.add(new SetCardInfo("Release the Dogs", 4, Rarity.UNCOMMON, mage.cards.r.ReleaseTheDogs.class));
         cards.add(new SetCardInfo("Rhystic Study", 169, Rarity.RARE, mage.cards.r.RhysticStudy.class));
+        cards.add(new SetCardInfo("Riddle of Lightning", 359, Rarity.UNCOMMON, mage.cards.r.RiddleOfLightning.class));
         cards.add(new SetCardInfo("Riptide Laboratory", 494, Rarity.RARE, mage.cards.r.RiptideLaboratory.class));
         cards.add(new SetCardInfo("Rise of the Dark Realms", 271, Rarity.MYTHIC, mage.cards.r.RiseOfTheDarkRealms.class));
+        cards.add(new SetCardInfo("Rishadan Airship", 170, Rarity.COMMON, mage.cards.r.RishadanAirship.class));
+        cards.add(new SetCardInfo("Ronom Unicorn", 131, Rarity.COMMON, mage.cards.r.RonomUnicorn.class));
         cards.add(new SetCardInfo("Roving Keep", 480, Rarity.COMMON, mage.cards.r.RovingKeep.class));
         cards.add(new SetCardInfo("Rumbling Baloth", 426, Rarity.COMMON, mage.cards.r.RumblingBaloth.class));
         cards.add(new SetCardInfo("Runed Servitor", 481, Rarity.COMMON, mage.cards.r.RunedServitor.class));
         cards.add(new SetCardInfo("Rupture Spire", 495, Rarity.COMMON, mage.cards.r.RuptureSpire.class));
         cards.add(new SetCardInfo("Sage's Row Savant", 171, Rarity.COMMON, mage.cards.s.SagesRowSavant.class));
+        cards.add(new SetCardInfo("Sailor of Means", 172, Rarity.COMMON, mage.cards.s.SailorOfMeans.class));
         cards.add(new SetCardInfo("Sangromancer", 272, Rarity.RARE, mage.cards.s.Sangromancer.class));
         cards.add(new SetCardInfo("Savage Stomp", 427, Rarity.UNCOMMON, mage.cards.s.SavageStomp.class));
         cards.add(new SetCardInfo("Scarecrone", 482, Rarity.RARE, mage.cards.s.Scarecrone.class));
@@ -193,15 +261,21 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Seismic Elemental", 362, Rarity.UNCOMMON, mage.cards.s.SeismicElemental.class));
         cards.add(new SetCardInfo("Selvala, Heart of the Wilds", 429, Rarity.MYTHIC, mage.cards.s.SelvalaHeartOfTheWilds.class));
         cards.add(new SetCardInfo("Sengir Vampire", 275, Rarity.UNCOMMON, mage.cards.s.SengirVampire.class));
+        cards.add(new SetCardInfo("Serendib Efreet", 175, Rarity.RARE, mage.cards.s.SerendibEfreet.class));
         cards.add(new SetCardInfo("Settle the Score", 276, Rarity.UNCOMMON, mage.cards.s.SettleTheScore.class));
         cards.add(new SetCardInfo("Shambling Goblin", 277, Rarity.COMMON, mage.cards.s.ShamblingGoblin.class));
         cards.add(new SetCardInfo("Sharding Sphinx", 176, Rarity.RARE, mage.cards.s.ShardingSphinx.class));
         cards.add(new SetCardInfo("Sheoldred, Whispering One", 278, Rarity.MYTHIC, mage.cards.s.SheoldredWhisperingOne.class));
+        cards.add(new SetCardInfo("Sigiled Starfish", 177, Rarity.UNCOMMON, mage.cards.s.SigiledStarfish.class));
         cards.add(new SetCardInfo("Signpost Scarecrow", 485, Rarity.COMMON, mage.cards.s.SignpostScarecrow.class));
         cards.add(new SetCardInfo("Silhana Wayfinder", 430, Rarity.UNCOMMON, mage.cards.s.SilhanaWayfinder.class));
         cards.add(new SetCardInfo("Skittering Surveyor", 486, Rarity.COMMON, mage.cards.s.SkitteringSurveyor.class));
+        cards.add(new SetCardInfo("Sky Tether", 133, Rarity.UNCOMMON, mage.cards.s.SkyTether.class));
+        cards.add(new SetCardInfo("Slate Street Ruffian", 279, Rarity.COMMON, mage.cards.s.SlateStreetRuffian.class));
         cards.add(new SetCardInfo("Somberwald Stag", 431, Rarity.UNCOMMON, mage.cards.s.SomberwaldStag.class));
+        cards.add(new SetCardInfo("Soul Salvage", 280, Rarity.COMMON, mage.cards.s.SoulSalvage.class));
         cards.add(new SetCardInfo("Soul of the Harvest", 432, Rarity.RARE, mage.cards.s.SoulOfTheHarvest.class));
+        cards.add(new SetCardInfo("Spectral Sailor", 178, Rarity.UNCOMMON, mage.cards.s.SpectralSailor.class));
         cards.add(new SetCardInfo("Spitting Earth", 364, Rarity.COMMON, mage.cards.s.SpittingEarth.class));
         cards.add(new SetCardInfo("Sporemound", 433, Rarity.COMMON, mage.cards.s.Sporemound.class));
         cards.add(new SetCardInfo("Storm Sculptor", 179, Rarity.COMMON, mage.cards.s.StormSculptor.class));
@@ -209,10 +283,13 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Suspicious Bookcase", 487, Rarity.UNCOMMON, mage.cards.s.SuspiciousBookcase.class));
         cards.add(new SetCardInfo("Swamp", 54, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Swarm of Bloodflies", 282, Rarity.UNCOMMON, mage.cards.s.SwarmOfBloodflies.class));
+        cards.add(new SetCardInfo("Sweep Away", 180, Rarity.COMMON, mage.cards.s.SweepAway.class));
         cards.add(new SetCardInfo("Sylvan Brushstrider", 434, Rarity.COMMON, mage.cards.s.SylvanBrushstrider.class));
         cards.add(new SetCardInfo("Sylvan Ranger", 435, Rarity.COMMON, mage.cards.s.SylvanRanger.class));
+        cards.add(new SetCardInfo("Take Heart", 134, Rarity.COMMON, mage.cards.t.TakeHeart.class));
         cards.add(new SetCardInfo("Talrand's Invocation", 182, Rarity.UNCOMMON, mage.cards.t.TalrandsInvocation.class));
         cards.add(new SetCardInfo("Talrand, Sky Summoner", 181, Rarity.RARE, mage.cards.t.TalrandSkySummoner.class));
+        cards.add(new SetCardInfo("Tandem Tactics", 135, Rarity.COMMON, mage.cards.t.TandemTactics.class));
         cards.add(new SetCardInfo("Tempting Witch", 283, Rarity.UNCOMMON, mage.cards.t.TemptingWitch.class));
         cards.add(new SetCardInfo("Terramorphic Expanse", 78, Rarity.COMMON, mage.cards.t.TerramorphicExpanse.class));
         cards.add(new SetCardInfo("Terrarion", 488, Rarity.COMMON, mage.cards.t.Terrarion.class));
@@ -224,22 +301,34 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Thriving Isle", 36, Rarity.COMMON, mage.cards.t.ThrivingIsle.class));
         cards.add(new SetCardInfo("Thriving Moor", 37, Rarity.COMMON, mage.cards.t.ThrivingMoor.class));
         cards.add(new SetCardInfo("Thundering Spineback", 437, Rarity.UNCOMMON, mage.cards.t.ThunderingSpineback.class));
+        cards.add(new SetCardInfo("Time to Feed", 438, Rarity.COMMON, mage.cards.t.TimeToFeed.class));
         cards.add(new SetCardInfo("Tithebearer Giant", 284, Rarity.COMMON, mage.cards.t.TithebearerGiant.class));
         cards.add(new SetCardInfo("Towering Titan", 31, Rarity.MYTHIC, mage.cards.t.ToweringTitan.class));
         cards.add(new SetCardInfo("Trusty Retriever", 8, Rarity.COMMON, mage.cards.t.TrustyRetriever.class));
         cards.add(new SetCardInfo("Ulvenwald Hydra", 439, Rarity.MYTHIC, mage.cards.u.UlvenwaldHydra.class));
+        cards.add(new SetCardInfo("Unstable Obelisk", 489, Rarity.UNCOMMON, mage.cards.u.UnstableObelisk.class));
+        cards.add(new SetCardInfo("Valorous Stance", 136, Rarity.UNCOMMON, mage.cards.v.ValorousStance.class));
         cards.add(new SetCardInfo("Vampire Neonate", 285, Rarity.COMMON, mage.cards.v.VampireNeonate.class));
         cards.add(new SetCardInfo("Vastwood Zendikon", 440, Rarity.COMMON, mage.cards.v.VastwoodZendikon.class));
         cards.add(new SetCardInfo("Vedalken Archmage", 187, Rarity.RARE, mage.cards.v.VedalkenArchmage.class));
         cards.add(new SetCardInfo("Verdant Embrace", 441, Rarity.RARE, mage.cards.v.VerdantEmbrace.class));
         cards.add(new SetCardInfo("Volcanic Fallout", 368, Rarity.UNCOMMON, mage.cards.v.VolcanicFallout.class));
+        cards.add(new SetCardInfo("Voyage's End", 189, Rarity.COMMON, mage.cards.v.VoyagesEnd.class));
         cards.add(new SetCardInfo("Wailing Ghoul", 286, Rarity.COMMON, mage.cards.w.WailingGhoul.class));
         cards.add(new SetCardInfo("Wall of Blossoms", 442, Rarity.UNCOMMON, mage.cards.w.WallOfBlossoms.class));
         cards.add(new SetCardInfo("Wall of Vines", 443, Rarity.COMMON, mage.cards.w.WallOfVines.class));
+        cards.add(new SetCardInfo("Warden of Evos Isle", 191, Rarity.UNCOMMON, mage.cards.w.WardenOfEvosIsle.class));
         cards.add(new SetCardInfo("Warmonger's Chariot", 490, Rarity.UNCOMMON, mage.cards.w.WarmongersChariot.class));
+        cards.add(new SetCardInfo("Waterknot", 192, Rarity.COMMON, mage.cards.w.Waterknot.class));
+        cards.add(new SetCardInfo("Weaver of Lightning", 371, Rarity.UNCOMMON, mage.cards.w.WeaverOfLightning.class));
+        cards.add(new SetCardInfo("Whelming Wave", 193, Rarity.RARE, mage.cards.w.WhelmingWave.class));
         cards.add(new SetCardInfo("Wight of Precinct Six", 287, Rarity.COMMON, mage.cards.w.WightOfPrecinctSix.class));
         cards.add(new SetCardInfo("Wildheart Invoker", 444, Rarity.COMMON, mage.cards.w.WildheartInvoker.class));
+        cards.add(new SetCardInfo("Wildsize", 445, Rarity.COMMON, mage.cards.w.Wildsize.class));
+        cards.add(new SetCardInfo("Windreader Sphinx", 194, Rarity.RARE, mage.cards.w.WindreaderSphinx.class));
+        cards.add(new SetCardInfo("Windstorm Drake", 195, Rarity.UNCOMMON, mage.cards.w.WindstormDrake.class));
         cards.add(new SetCardInfo("Winged Words", 196, Rarity.COMMON, mage.cards.w.WingedWords.class));
+        cards.add(new SetCardInfo("Wishful Merfolk", 197, Rarity.COMMON, mage.cards.w.WishfulMerfolk.class));
         cards.add(new SetCardInfo("Witch of the Moors", 18, Rarity.RARE, mage.cards.w.WitchOfTheMoors.class));
         cards.add(new SetCardInfo("Wizard's Retort", 198, Rarity.UNCOMMON, mage.cards.w.WizardsRetort.class));
         cards.add(new SetCardInfo("Woodborn Behemoth", 446, Rarity.UNCOMMON, mage.cards.w.WoodbornBehemoth.class));
diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt
index 8c51d89f3b..e92660fc54 100644
--- a/Utils/mtg-cards-data.txt
+++ b/Utils/mtg-cards-data.txt
@@ -37723,13 +37723,20 @@ Garruk, Savage Herald|Core Set 2021|336|M|{4}{G}{G}|Legendary Planeswalker - Gar
 Garruk's Warsteed|Core Set 2021|337|R|{3}{G}{G}|Creature - Rhino|3|5|Vigilance$When Garruk's Warsteed enters the battlefield, you may search your library and/or graveyard for a card named Garruk, Savage Herald, reveal it, and put it into your hand. If you search your library this way, shuffle it.|
 Predatory Wurm|Core Set 2021|338|U|{3}{G}|Creature - Wurm|4|4|Vigilance$Predatory Wurm gets +2/+2 as long as you control a Garruk planeswalker.|
 Wildwood Patrol|Core Set 2021|339|C|{2}{G}|Creature - Centaur Scout|4|2|Trample|
+Fortify|Jumpstart|FOR|C|{2}{W}|Instant|||Choose one —$• Creatures you control get +2/+0 until end of turn.$• Creatures you control get +0/+2 until end of turn.|
+Lena, Selfless Champion|Jumpstart|LSC|R|{4}{W}{W}|Legendary Creature - Human Knight|3|3|When Lena, Selfless Champion enters the battlefield, create a 1/1 white Soldier creature token for each nontoken creature you control.$Sacrifice Lena: Creatures you control with power less than Lena's power gain indestructible until end of turn.|
+Raise the Alarm|Jumpstart|RTA|C|{1}{W}|Instant|||Create two 1/1 white Soldier creature tokens.|
 Blessed Sanctuary|Jumpstart|1|R|{3}{W}{W}|Enchantment|||Prevent all noncombat damage that would be dealt to you and creatures you control.$Whenever a nontoken creature enters the battlefield under your control, create a 2/2 white Unicorn creature token.|
-Brightmare|Jumpstart|2|C|{2}{W}|Creature - Unicorn|2|3|When Brightmare enters the battlefield, tap up to one target creature. You gain life equal to that creature's power.|
+Brightmare|Jumpstart|2|U|{2}{W}|Creature - Unicorn|2|3|When Brightmare enters the battlefield, tap up to one target creature. You gain life equal to that creature's power.|
 Emiel the Blessed|Jumpstart|3|M|{2}{W}{W}|Legendary Creature - Unicorn|4|4|{3}: Exile another target creature you control, then return it to the battlefield under its owner's control.$Whenever another creature enters the battlefield under your control, you may pay {G/W}. If you do, put a +1/+1 counter on it. If it's a Unicorn, put two +1/+1 counters on it instead.|
 Release the Dogs|Jumpstart|4|U|{3}{W}|Sorcery|||Create four 1/1 white Dog creature tokens.|
+Steel-Plume Marshal|Jumpstart|5|R|{3}{W}{W}|Creature - Bird Soldier|3|3|Flying$Whenever Steel-Plume Marshal attacks, other attacking creatures you control with flying get +2/+2 until end of turn.|
+Stone Haven Pilgrim|Jumpstart|6|U|{1}{W}|Creature - Kor Cleric|2|2|Whenever Stone Haven Pilgrim attacks, if you control an artifact or enchantment, Stone Haven Pilgrim gets +1/+1 and gains lifelink until end of turn.|
 Supply Runners|Jumpstart|7|U|{4}{W}|Creature - Dog|2|2|When Supply Runners enters the battlefield, put a +1/+1 counter on each other creature you control.|
 Trusty Retriever|Jumpstart|8|C|{3}{W}|Creature - Dog|2|3|When Trusty Retriever enters the battlefield, choose one —$• Put a +1/+1 counter on Trusty Retriever.$• Return target artifact or enchantment card from your graveyard to your hand.|
 Archaeomender|Jumpstart|9|C|{2}{U}|Creature - Human Wizard|2|3|When Archaeomender enters the battlefield, return target artifact card from your graveyard to your hand.|
+Corsair Captain|Jumpstart|11|R|{2}{U}|Creature - Human Pirate|2|2|When Corsair Captain enters the battlefield, create a treasure token.$Other Pirates you control get +1/+1.|
+Inniaz, the Gale Force|Jumpstart|12|R|{3}{U}{U}|Legendary Creature - Djinn|4|4|Flying${2}{W/U}: Attacking creatures with flying get +1/+1 until end of turn.$Whenever three or more creatures you control with flying attack, each player gains control of a nonland permanent of your choice controlled by the player to their right.|
 Ormos, Archive Keeper|Jumpstart|13|R|{4}{U}{U}|Legendary Creature - Sphinx|5|5|Flying$If you would draw a card while your library has no cards in it, instead put five +1/+1 counters on Ormos, Archive Keeper.${1}{U}{U}, Discard three cards with different names: Draw five cards.|
 Scholar of the Lost Trove|Jumpstart|14|R|{5}{U}{U}|Creature - Sphinx|5|5|Flying$When Scholar of the Lost Trove enters the battlefield, you may cast target instant, sorcery, or artifact card from your graveyard without paying its mana cost. If an instant or sorcery spell cast this way would be put into your graveyard this turn, exile it instead.|
 Kels, Fight Fixer|Jumpstart|15|R|{2}{B}{B}|Legendary Creature - Azra Warlock|4|3|Menace$Whenever you sacrifice a creature, you may pay {U/B}. If you do, draw a card.${1}, Sacrifice a creature: Kels, Fight Fixer gains indestructible until end of turn.|
@@ -37739,6 +37746,9 @@ Witch of the Moors|Jumpstart|18|R|{3}{B}{B}|Creature - Human Warlock|4|4|Deathto
 Lightning Phoenix|Jumpstart|21|R|{2}{R}|Creature - Phoenix|2|2|Flying, haste$Lightning Phoenix can't block.$At the beginning of your end step, if an opponent was dealt 3 or more damage this turn, you may pay {R}. If you do, return Lightning Phoenix from your graveyard to the battlefield.|
 Lightning Visionary|Jumpstart|22|C|{1}{R}|Creature - Minotaur Shaman|2|1|Prowess|
 Living Lightning|Jumpstart|23|U|{3}{R}|Creature - Elemental Shaman|3|2|When Living Lightning dies, return target instant or sorcery card from your graveyard to your hand.|
+Muxus, Goblin Grandee|Jumpstart|24|R|{4}{R}{R}|Legendary Creature - Goblin Noble|4|4|When Muxus, Goblin Grandee enters the battlefield, reveal the top six cards of your library. Put all Goblin creature cards with converted mana cost 5 or less from among them onto the battlefield and the rest on the bottom of your library in a random order.$Whenever Muxus attacks, it gets +1/+1 until end of turn for each other Goblin you control.|
+Sethron, Hurloon General|Jumpstart|25|R|{3}{R}{R}|Legendary Creature - Minotaur Warrior|4|4|Whenever Sethron, Hurloon General or another nontoken Minotaur enters the battlefield under your control, create a 2/3 red Minotaur creature token.${2}{B/R}: Minotaurs you control get +1/+0 and gain menace and haste until end of turn.|
+Spiteful Prankster|Jumpstart|26|U|{2}{R}|Creature - Devil|3|2|As long as it's your turn, Spiteful Prankster has first strike.$Whenever another creature dies, Spiteful Prankster deals 1 damage to target player or planeswalker.|
 Allosaurus Shepherd|Jumpstart|28|M|{G}|Creature - Elf Shaman|1|1|Allosaurus Shepherd can't be countered.$Green spells you control can't be countered.${4}{G}{G}: Until end of turn, each Elf creature you control has base power and toughness 5/5 and becomes a Dinosaur in addition to its other creature types.|
 Neyith of the Dire Hunt|Jumpstart|30|R|{2}{G}{G}|Legendary Creature - Human Warrior|3|3|Whenever one or more creatures you control fight or become blocked, draw a card.$At the beginning of combat on your turn, you may pay {2}{R/G}. If you do, double target creature's power until end of turn. That creature must be blocked this combat if able.|
 Towering Titan|Jumpstart|31|M|{4}{G}{G}|Creature - Giant|0|0|Towering Titan enters the battlefield with X +1/+1 counters on it, where X is the total toughness of other creatures you control.$Sacrifice a creature with defender: All creatures gain trample until end of turn.|
@@ -37748,39 +37758,94 @@ Thriving Grove|Jumpstart|34|C||Land|||Thriving Grove enters the battlefield tapp
 Thriving Heath|Jumpstart|35|C||Land|||Thriving Heath enters the battlefield tapped.$As Thriving Heath enters the battlefield, choose a color other than white.${T}: Add {W} or one mana of the chosen color.|
 Thriving Isle|Jumpstart|36|C||Land|||Thriving Isle enters the battlefield tapped.$As Thriving Isle enters the battlefield, choose a color other than blue.${T}: Add {U} or one mana of the chosen color.|
 Thriving Moor|Jumpstart|37|C||Land|||Thriving Moor enters the battlefield tapped.$As Thriving Moor enters the battlefield, choose a color other than black.${T}: Add {B} or one mana of the chosen color.|
-Plains|Jumpstart|41|C||Basic Land - Plains|||({T}: Add {W}.)|
-Island|Jumpstart|49|C||Basic Land - Island|||({T}: Add {U}.)|
+Plains|Jumpstart|38|C||Basic Land - Plains|||({T}: Add {W}.)|
+Island|Jumpstart|48|C||Basic Land - Island|||({T}: Add {U}.)|
 Swamp|Jumpstart|54|C||Basic Land - Swamp|||({T}: Add {B}.)|
 Mountain|Jumpstart|64|C||Basic Land - Mountain|||({T}: Add {R}.)|
 Forest|Jumpstart|70|C||Basic Land - Forest|||({T}: Add {G}.)|
 Terramorphic Expanse|Jumpstart|78|C||Land|||{T}, Sacrifice Terramorphic Expanse: Search your library for a basic land card, put it onto the battlefield tapped, then shuffle your library.|
+Aegis of the Heavens|Jumpstart|79|U|{1}{W}|Instant|||Target creature gets +1/+7 until end of turn.|
+Aerial Assault|Jumpstart|80|C|{2}{W}|Sorcery|||Destroy target tapped creature. You gain 1 life for each creature you control with flying.|
 Affa Guard Hound|Jumpstart|81|U|{2}{W}|Creature - Hound|2|2|Flash$When Affa Guard Hound enters the battlefield, target creature gets +0/+3 until end of turn.|
+Angel of the Dire Hour|Jumpstart|85|R|{5}{W}{W}|Creature - Angel|5|4|Flash$Flying$When Angel of the Dire Hour enters the battlefield, if you cast it from your hand, exile all attacking creatures.|
+Archon of Justice|Jumpstart|89|R|{3}{W}{W}|Creature - Archon|4|4|Flying$When Archon of Justice dies, exile target permanent.|
+Archon of Redemption|Jumpstart|90|R|{3}{W}{W}|Creature - Archon|3|4|Flying$Whenever Archon of Redemption or another creature with flying enters the battlefield under your control, you may gain life equal to that creature's power.|
+Battlefield Promotion|Jumpstart|91|C|{1}{W}|Instant|||Put a +1/+1 counter on target creature. That creature gains first strike until end of turn. You gain 2 life.|
 Cathar's Companion|Jumpstart|94|C|{2}{W}|Creature - Hound|3|1|Whenever you cast a noncreature spell, Cathar's Companion gains indestructible until end of turn.|
+Cathars' Crusade|Jumpstart|95|R|{3}{W}{W}|Enchantment|||Whenever a creature enters the battlefield under your control, put a +1/+1 counter on each creature you control.|
+Cloudshift|Jumpstart|97|C|{W}|Instant|||Exile target creature you control, then return that card to the battlefield under your control.|
+Dauntless Onslaught|Jumpstart|99|U|{2}{W}|Instant|||Up to two target creatures each get +2/+2 until end of turn.|
+Divine Arrow|Jumpstart|100|C|{1}{W}|Instant|||Divine Arrow deals 4 damage to target attacking or blocking creature.|
+Duelist's Heritage|Jumpstart|101|R|{2}{W}|Enchantment|||Whenever one or more creatures attack, you may have target attacking creature gain double strike until end of turn.|
+Gird for Battle|Jumpstart|106|U|{W}|Sorcery|||Put a +1/+1 counter on each of up to two target creatures.|
+Healer's Hawk|Jumpstart|107|C|{W}|Creature - Bird|1|1|Flying, lifelink|
+High Sentinels of Arashin|Jumpstart|108|R|{3}{W}|Creature - Bird Soldier|3|4|Flying$High Sentinels of Arashin gets +1/+1 for each other creature you control with a +1/+1 counter on it.${3}{W}: Put a +1/+1 counter on target creature.|
+Inspired Charge|Jumpstart|110|C|{2}{W}{W}|Instant|||Creatures you control get +2/+1 until end of turn.|
+Inspiring Captain|Jumpstart|111|C|{3}{W}|Creature - Human Knight|3|3|When Inspiring Captain enters the battlefield, creatures you control get +1/+1 until end of turn.|
+Inspiring Unicorn|Jumpstart|112|U|{2}{W}{W}|Creature - Unicorn|2|2|Whenever Inspiring Unicorn attacks, creatures you control get +1/+1 until end of turn.|
 Isamaru, Hound of Konda|Jumpstart|113|R|{W}|Legendary Creature - Hound|2|2||
 Knight of the Tusk|Jumpstart|114|C|{4}{W}{W}|Creature - Human Knight|3|7|Vigilance|
+Lightwalker|Jumpstart|118|C|{1}{W}|Creature - Human Warrior|2|1|Lightwalker has flying as long as it has a +1/+1 counter on it.|
+Long Road Home|Jumpstart|120|U|{1}{W}|Instant|||Exile target creature. At the beginning of the next end step, return that card to the battlefield under its owner's control with a +1/+1 counter on it.|
+Mentor of the Meek|Jumpstart|121|R|{2}{W}|Creature - Human Soldier|2|2|Whenever another creature with power 2 or less enters the battlefield under your control, you may pay {1}. If you do, draw a card.|
+Mesa Unicorn|Jumpstart|122|C|{1}{W}|Creature - Unicorn|2|2|Lifelink|
+Mikaeus, the Lunarch|Jumpstart|123|M|{X}{W}|Legendary Creature - Human Cleric|0|0|Mikaeus, the Lunarch enters the battlefield with X +1/+1 counters on it.${T}: Put a +1/+1 counter on Mikaeus.${T}, Remove a +1/+1 counter from Mikaeus: Put a +1/+1 counter on each other creature you control.|
+Moment of Heroism|Jumpstart|124|C|{1}{W}|Instant|||Target creature gets +2/+2 and gains lifelink until end of turn.|
 Pacifism|Jumpstart|125|C|{1}{W}|Enchantment - Aura|||Enchant creature$Enchanted creature can't attack or block.|
+Path to Exile|Jumpstart|127|U|{W}|Instant|||Exile target creature. Its controller may search their library for a basic land card, put that card onto the battlefield tapped, then shuffle their library.|
+Patron of the Valiant|Jumpstart|128|U|{3}{W}{W}|Creature - Angel|4|4|Flying$When Patron of the Valiant enters the battlefield, put a +1/+1 counter on each creature you control with a +1/+1 counter on it.|
+Ronom Unicorn|Jumpstart|131|C|{1}{W}|Creature - Unicorn|2|2|Sacrifice Ronom Unicorn: Destroy target enchantment.|
+Sky Tether|Jumpstart|133|U|{W}|Enchantment - Aura|||Enchant creature$Enchanted creature has defender and loses flying.|
+Take Heart|Jumpstart|134|C|{W}|Instant|||Target creature gets +2/+2 until end of turn. You gain 1 life for each attacking creature you control.|
+Tandem Tactics|Jumpstart|135|C|{1}{W}|Instant|||Up to two target creatures each get +1/+2 until end of turn. You gain 2 life.|
+Valorous Stance|Jumpstart|136|U|{1}{W}|Instant|||Choose one —$• Target creature gains indestructible until end of turn.$• Destroy target creature with toughness 4 or greater.|
+Aegis Turtle|Jumpstart|138|C|{U}|Creature - Turtle|0|5||
 Befuddle|Jumpstart|140|C|{2}{U}|Instant|||Target creature gets -4/-0 until end of turn.$Draw a card.|
+Chart a Course|Jumpstart|142|U|{1}{U}|Sorcery|||Draw two cards. Then discard a card unless you attacked this turn.|
 Cloudreader Sphinx|Jumpstart|143|C|{4}{U}|Creature - Sphinx|3|4|Flying$When Cloudreader Sphinx enters the battlefield, scry 2.|
+Coastal Piracy|Jumpstart|144|U|{2}{U}{U}|Enchantment|||Whenever a creature you control deals combat damage to an opponent, you may draw a card.|
 Crookclaw Transmuter|Jumpstart|145|C|{3}{U}|Creature - Bird Wizard|3|1|Flash$Flying$When Crookclaw Transmuter enters the battlefield, switch target creature's power and toughness until end of turn.|
+Cryptic Serpent|Jumpstart|146|U|{5}{U}{U}|Creature - Serpent|6|5|This spell costs {1} less to cast for each instant and sorcery card in your graveyard.|
 Curiosity|Jumpstart|147|U|{U}|Enchantment - Aura|||Enchant creature$Whenever enchanted creature deals damage to an opponent, you may draw a card.|
+Curious Obsession|Jumpstart|148|U|{U}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +1/+1 and has "Whenever this creature deals combat damage to a player, you may draw a card."$At the beginning of your end step, if you didn't attack with a creature this turn, sacrifice Curious Obsession.|
+Departed Deckhand|Jumpstart|149|U|{1}{U}|Creature - Spirit Pirate|2|2|When Departed Deckhand becomes the target of a spell, sacrifice it.$Departed Deckhand can't be blocked except by Spirits.${3}{U}: Another target creature you control can't be blocked this turn except by Spirits.|
 Erratic Visionary|Jumpstart|150|C|{1}{U}|Creature - Human Wizard|1|3|{1}{U}, {T}: Draw a card, then discard a card.|
 Exclude|Jumpstart|152|U|{2}{U}|Instant|||Counter target creature spell.$Draw a card.|
 Exclusion Mage|Jumpstart|153|U|{2}{U}|Creature - Human Wizard|2|2|When Exclusion Mage enters the battlefield, return target creature an opponent controls to its owner's hand.|
+Kira, Great Glass-Spinner|Jumpstart|154|R|{1}{U}{U}|Legendary Creature - Spirit|2|2|Flying$Creatures you control have "Whenever this creature becomes the target of a spell or ability for the first time each turn, counter that spell or ability."|
+Kitesail Corsair|Jumpstart|155|C|{1}{U}|Creature - Human Pirate|2|1|Kitesail Corsair has flying as long as it's attacking.|
 Leave in the Dust|Jumpstart|156|C|{3}{U}|Instant|||Return target nonland permanent to its owner's hand.$Draw a card.|
+Mystic Archaeologist|Jumpstart|158|R|{1}{U}|Creature - Human Wizard|2|1|{3}{U}{U}: Draw two cards.|
 Narcolepsy|Jumpstart|159|C|{1}{U}|Enchantment - Aura|||Enchant creature$At the beginning of each upkeep, if enchanted creature is untapped, tap it.|
+Octoprophet|Jumpstart|161|C|{3}{U}|Creature - Octopus|3|3|When Octoprophet enters the battlefield, scry 2.|
 Oneirophage|Jumpstart|162|U|{3}{U}|Creature - Squid Illusion|1|2|Flying$Whenever you draw a card, put a +1/+1 counter on Oneirophage.|
 Peel from Reality|Jumpstart|163|C|{1}{U}|Instant|||Return target creature you control and target creature you don't control to their owners' hands.|
 Prescient Chimera|Jumpstart|164|C|{3}{U}{U}|Creature - Chimera|3|4|Flying$Whenever you cast an instant or sorcery spell, scry 1.|
+Prosperous Pirates|Jumpstart|165|C|{4}{U}|Creature - Human Pirate|3|4|When Prosperous Pirates enters the battlefield, create two Treasure tokens.|
+Read the Runes|Jumpstart|167|R|{X}{U}|Instant|||Draw X cards. For each card drawn this way, discard a card unless you sacrifice a permanent.|
 Rhystic Study|Jumpstart|169|R|{2}{U}|Enchantment|||Whenever an opponent casts a spell, you may draw a card unless that player pays {1}.|
+Rishadan Airship|Jumpstart|170|C|{2}{U}|Creature - Human Pirate|3|1|Flying$Rishadan Airship can block only creatures with flying.|
 Sage's Row Savant|Jumpstart|171|C|{1}{U}|Creature - Vedalken Wizard|2|1|When Sage's Row Savant enters the battlefield, scry 2.|
+Sailor of Means|Jumpstart|172|C|{2}{U}|Creature - Human Pirate|1|4|When Sailor of Means enters the battlefield, create a Treasure token.|
 Sea Gate Oracle|Jumpstart|173|C|{2}{U}|Creature - Human Wizard|1|3|When Sea Gate Oracle enters the battlefield, look at the top two cards of your library. Put one of them into your hand and the other on the bottom of your library.|
+Serendib Efreet|Jumpstart|175|R|{2}{U}|Creature - Efreet|3|4|Flying$At the beginning of your upkeep, Serendib Efreet deals 1 damage to you.|
 Sharding Sphinx|Jumpstart|176|R|{4}{U}{U}|Artifact Creature - Sphinx|4|4|Flying$Whenever an artifact creature you control deals combat damage to a player, you may create a 1/1 blue Thopter artifact creature token with flying.|
+Sigiled Starfish|Jumpstart|177|U|{1}{U}|Creature - Starfish|0|3|{T}: Scry 1.|
+Spectral Sailor|Jumpstart|178|U|{U}|Creature - Spirit Pirate|1|1|Flash$Flying${3}{U}: Draw a card.|
 Storm Sculptor|Jumpstart|179|C|{3}{U}|Creature - Merfolk Wizard|3|2|Storm Sculptor can't be blocked.$When Storm Sculptor enters the battlefield, return a creature you control to its owner's hand.|
+Sweep Away|Jumpstart|180|C|{2}{U}|Instant|||Return target creature to its owner's hand. If that creature is attacking, you may put it on top of its owner's library instead.|
 Talrand, Sky Summoner|Jumpstart|181|R|{2}{U}{U}|Legendary Creature - Merfolk Wizard|2|2|Whenever you cast an instant or sorcery spell, create a 2/2 blue Drake creature token with flying.|
 Talrand's Invocation|Jumpstart|182|U|{2}{U}{U}|Sorcery|||Create two 2/2 blue Drake creature tokens with flying.|
 Thirst for Knowledge|Jumpstart|183|U|{2}{U}|Instant|||Draw three cards. Then discard two cards unless you discard an artifact card.|
 Vedalken Archmage|Jumpstart|187|R|{2}{U}{U}|Creature - Vedalken Wizard|0|2|Whenever you cast an artifact spell, draw a card.|
+Voyage's End|Jumpstart|189|C|{1}{U}|Instant|||Return target creature to its owner's hand. Scry 1.|
+Warden of Evos Isle|Jumpstart|191|U|{2}{U}|Creature - Bird Wizard|2|2|Flying$Creature spells with flying you cast cost {1} less to cast.|
+Waterknot|Jumpstart|192|C|{1}{U}{U}|Enchantment - Aura|||Enchant creature$When Waterknot enters the battlefield, tap enchanted creature.$Enchanted creature doesn't untap during its controller's untap step.|
+Whelming Wave|Jumpstart|193|R|{2}{U}{U}|Sorcery|||Return all creatures to their owners' hands except for Krakens, Leviathans, Octopuses, and Serpents.|
+Windreader Sphinx|Jumpstart|194|R|{5}{U}{U}|Creature - Sphinx|3|7|Flying$Whenever a creature with flying attacks, you may draw a card.|
+Windstorm Drake|Jumpstart|195|U|{4}{U}|Creature - Drake|3|3|Flying$Other creatures you control with flying get +1/+0.|
 Winged Words|Jumpstart|196|C|{2}{U}|Sorcery|||This spell costs {1} less to cast if you control a creature with flying.$Draw two cards.|
+Wishful Merfolk|Jumpstart|197|C|{1}{U}|Creature - Merfolk|3|2|Defender${1}{U}: Wishful Merfolk loses defender and becomes a Human until end of turn.|
 Wizard's Retort|Jumpstart|198|U|{1}{U}{U}|Instant|||This spell costs {1} less to cast if you control a Wizard.$Counter target spell.|
 Agonizing Syphon|Jumpstart|199|C|{3}{B}|Sorcery|||Agonizing Syphon deals 3 damage to any target and you gain 3 life.|
 Assassin's Strike|Jumpstart|200|U|{4}{B}{B}|Sorcery|||Destroy target creature. Its controller discards a card.|
@@ -37802,6 +37867,7 @@ Cemetery Recruitment|Jumpstart|217|C|{1}{B}|Sorcery|||Return target creature car
 Child of Night|Jumpstart|218|C|{1}{B}|Creature - Vampire|2|1|Lifelink|
 Crow of Dark Tidings|Jumpstart|221|C|{2}{B}|Creature - Zombie Bird|2|1|Flying$When Crow of Dark Tidings enters the battlefield or dies, put the top two cards of your library into your graveyard.|
 Death's Approach|Jumpstart|222|C|{B}|Enchantment - Aura|||Enchant creature$Enchanted creature gets -X/-X, where X is the number of creature cards in its controller's graveyard.|
+Douse in Gloom|Jumpstart|223|C|{2}{B}|Instant|||Douse in Gloom deals 2 damage to target creature and you gain 2 life.|
 Drainpipe Vermin|Jumpstart|224|C|{B}|Creature - Rat|1|1|When Drainpipe Vermin dies, you may pay {B}. If you do, target player discards a card.|
 Drana, Liberator of Malakir|Jumpstart|225|M|{1}{B}{B}|Legendary Creature - Vampire Ally|2|3|Flying, first strike$Whenever Drana, Liberator of Malakir deals combat damage to a player, put a +1/+1 counter on each attacking creature you control.|
 Dutiful Attendant|Jumpstart|226|C|{2}{B}|Creature - Human Warrior|1|2|When Dutiful Attendant dies, return another target creature card from your graveyard to your hand.|
@@ -37817,18 +37883,25 @@ Funeral Rites|Jumpstart|235|C|{2}{B}|Sorcery|||You draw two cards, lose 2 life,
 Ghoulcaller Gisa|Jumpstart|236|M|{3}{B}{B}|Legendary Creature - Human Wizard|3|4|{B}, {T}, Sacrifice another creature: Create X 2/2 black Zombie creature tokens, where X is the sacrificed creature's power.|
 Ghoulcaller's Accomplice|Jumpstart|237|C|{1}{B}|Creature - Human Rogue|2|2|{3}{B}, Exile Ghoulcaller's Accomplice from your graveyard: Create a 2/2 black Zombie creature token. Activate this ability only any time you could cast a sorcery.|
 Ghoulraiser|Jumpstart|238|C|{1}{B}{B}|Creature - Zombie|2|2|When Ghoulraiser enters the battlefield, return a Zombie card at random from your graveyard to your hand.|
+Gifted Aetherborn|Jumpstart|239|U|{B}{B}|Creature - Aetherborn Vampire|2|3|Deathtouch, lifelink|
 Gravewaker|Jumpstart|241|R|{4}{B}{B}|Creature - Bird Spirit|5|5|Flying${5}{B}{B}: Return target creature card from your graveyard to the battlefield tapped.|
 Innocent Blood|Jumpstart|244|C|{B}|Sorcery|||Each player sacrifices a creature.|
 Kalastria Nightwatch|Jumpstart|245|C|{4}{B}|Creature - Vampire Warrior Ally|4|5|Whenever you gain life, Kalastria Nightwatch gains flying until end of turn.|
 Last Gasp|Jumpstart|247|C|{1}{B}|Instant|||Target creature gets -3/-3 until end of turn.|
 Liliana's Elite|Jumpstart|250|U|{2}{B}|Creature - Zombie|1|1|Liliana's Elite gets +1/+1 for each creature card in your graveyard.|
+Liliana's Reaver|Jumpstart|251|R|{2}{B}{B}|Creature - Zombie|4|3|Deathtouch$Whenever Liliana's Reaver deals combat damage to a player, that player discards a card and you create a tapped 2/2 black Zombie creature token.|
 Macabre Waltz|Jumpstart|252|C|{1}{B}|Sorcery|||Return up to two target creature cards from your graveyard to your hand, then discard a card.|
 Malakir Familiar|Jumpstart|253|U|{2}{B}|Creature - Bat|2|1|Flying, deathtouch$Whenever you gain life, Malakir Familiar gets +1/+1 until end of turn.|
 Mark of the Vampire|Jumpstart|254|C|{3}{B}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +2/+2 and has lifelink.|
 Miasmic Mummy|Jumpstart|256|C|{1}{B}|Creature - Zombie Jackal|2|2|When Miasmic Mummy enters the battlefield, each player discards a card.|
 Mire Triton|Jumpstart|257|U|{1}{B}|Creature - Zombie Merfolk|2|1|Deathtouch$When Mire Triton enters the battlefield, put the top two cards of your library into your graveyard and you gain 2 life.|
 Nyxathid|Jumpstart|259|R|{1}{B}{B}|Creature - Elemental|7|7|As Nyxathid enters the battlefield, choose an opponent.$Nyxathid gets -1/-1 for each card in the chosen player's hand.|
+Parasitic Implant|Jumpstart|262|C|{3}{B}|Enchantment - Aura|||Enchant creature$At the beginning of your upkeep, enchanted creature's controller sacrifices it and you create a 1/1 colorless Myr artifact creature token.|
+Phyrexian Broodlings|Jumpstart|263|C|{1}{B}{B}|Creature - Minion|2|2|{1}, Sacrifice a creature: Put a +1/+1 counter on Phyrexian Broodlings.|
+Phyrexian Debaser|Jumpstart|264|C|{3}{B}|Creature - Carrier|2|2|Flying${T}, Sacrifice Phyrexian Debaser: Target creature gets -2/-2 until end of turn.|
+Phyrexian Gargantua|Jumpstart|265|U|{4}{B}{B}|Creature - Horror|4|4|When Phyrexian Gargantua enters the battlefield, you draw two cards and you lose 2 life.|
 Phyrexian Rager|Jumpstart|266|C|{2}{B}|Creature - Horror|2|2|When Phyrexian Rager enters the battlefield, you draw a card and you lose 1 life.|
+Phyrexian Reclamation|Jumpstart|267|U|{B}|Enchantment|||{1}{B}, Pay 2 life: Return target creature card from your graveyard to your hand.|
 Ravenous Chupacabra|Jumpstart|269|U|{2}{B}{B}|Creature - Beast Horror|2|2|When Ravenous Chupacabra enters the battlefield, destroy target creature an opponent controls.|
 Reanimate|Jumpstart|270|R|{B}|Sorcery|||Put target creature card from a graveyard onto the battlefield under your control. You lose life equal to its converted mana cost.|
 Rise of the Dark Realms|Jumpstart|271|M|{7}{B}{B}|Sorcery|||Put all creature cards from all graveyards onto the battlefield under your control.|
@@ -37838,6 +37911,8 @@ Sengir Vampire|Jumpstart|275|U|{3}{B}{B}|Creature - Vampire|4|4|Flying$Whenever
 Settle the Score|Jumpstart|276|U|{2}{B}{B}|Sorcery|||Exile target creature. Put two loyalty counters on a planeswalker you control.|
 Shambling Goblin|Jumpstart|277|C|{B}|Creature - Zombie Goblin|1|1|When Shambling Goblin dies, target creature an opponent controls gets -1/-1 until end of turn.|
 Sheoldred, Whispering One|Jumpstart|278|M|{5}{B}{B}|Legendary Creature - Praetor|6|6|Swampwalk$At the beginning of your upkeep, return target creature card from your graveyard to the battlefield.$At the beginning of each opponent's upkeep, that player sacrifices a creature.|
+Slate Street Ruffian|Jumpstart|279|C|{2}{B}|Creature - Human Warrior|2|2|Whenever Slate Street Ruffian becomes blocked, defending player discards a card.|
+Soul Salvage|Jumpstart|280|C|{2}{B}|Sorcery|||Return up to two target creature cards from your graveyard to your hand.|
 Swarm of Bloodflies|Jumpstart|282|U|{4}{B}|Creature - Insect|0|0|Flying$Swarm of Bloodflies enters the battlefield with two +1/+1 counters on it.$Whenever another creature dies, put a +1/+1 counter on Swarm of Bloodflies.|
 Tempting Witch|Jumpstart|283|U|{2}{B}|Creature - Human Warlock|1|3|When Tempting Witch enters the battlefield, create a Food token.${2}, {T}, Sacrifice a Food: Target player loses 3 life.|
 Tithebearer Giant|Jumpstart|284|C|{5}{B}|Creature - Giant Warrior|4|5|When Tithebearer Giant enters the battlefield, you draw a card and you lose 1 life.|
@@ -37846,20 +37921,31 @@ Wailing Ghoul|Jumpstart|286|C|{1}{B}|Creature - Zombie|1|3|When Wailing Ghoul en
 Wight of Precinct Six|Jumpstart|287|C|{1}{B}|Creature - Zombie|1|1|Wight of Precinct Six gets +1/+1 for each creature card in your opponents' graveyards.|
 Zombie Infestation|Jumpstart|288|U|{1}{B}|Enchantment|||Discard two cards: Create a 2/2 black Zombie creature token.|
 Ashmouth Hound|Jumpstart|290|C|{1}{R}|Creature - Elemental Hound|2|1|Whenever Ashmouth Hound blocks or becomes blocked by a creature, Ashmouth Hound deals 1 damage to that creature.|
+Ball Lightning|Jumpstart|291|R|{R}{R}{R}|Creature - Elemental|6|1|Trample$Haste$At the beginning of the end step, sacrifice Ball Lightning.|
 Bloodrock Cyclops|Jumpstart|297|C|{2}{R}|Creature - Cyclops|3|3|Bloodrock Cyclops attacks each combat if able.|
+Chain Lightning|Jumpstart|302|U|{R}|Sorcery|||Chain Lightning deals 3 damage to any target. Then that player or that permanent's controller may pay {R}{R}. If the player does, they may copy this spell and may choose a new target for that copy.|
 Cinder Elemental|Jumpstart|304|U|{3}{R}|Creature - Elemental|2|2|{X}{R}, {T}, Sacrifice Cinder Elemental: It deals X damage to any target.|
 Fanatical Firebrand|Jumpstart|315|C|{R}|Creature - Goblin Pirate|1|1|Haste${T}, Sacrifice Fanatical Firebrand: It deals 1 damage to any target.|
 Flames of the Firebrand|Jumpstart|317|U|{2}{R}|Sorcery|||Flames of the Firebrand deals 3 damage divided as you choose among one, two, or three targets.|
 Grim Lavamancer|Jumpstart|331|R|{R}|Creature - Human Wizard|1|1|{R}, {T}, Exile two cards from your graveyard: Grim Lavamancer deals 2 damage to any target.|
+Homing Lightning|Jumpstart|335|U|{2}{R}{R}|Instant|||Homing Lightning deals 4 damage to target creature and each other creature with the same name as that creature.|
 Hungry Flames|Jumpstart|336|C|{2}{R}|Instant|||Hungry Flames deals 3 damage to target creature and 2 damage to target player or planeswalker.|
+Lightning Axe|Jumpstart|341|U|{R}|Instant|||As an additional cost to cast this spell, discard a card or pay {5}.$Lightning Axe deals 5 damage to target creature.|
+Lightning Bolt|Jumpstart|342|U|{R}|Instant|||Lightning Bolt deals 3 damage to any target.|
+Lightning Diadem|Jumpstart|343|C|{5}{R}|Enchantment - Aura|||Enchant creature$When Lightning Diadem enters the battlefield, it deals 2 damage to any target.$Enchanted creature gets +2/+2.|
+Lightning Elemental|Jumpstart|344|C|{3}{R}|Creature - Elemental|4|1|Haste|
+Lightning Shrieker|Jumpstart|345|C|{4}{R}|Creature - Dragon|5|5|Flying, trample, haste$At the beginning of the end step, Lightning Shrieker's owner shuffles it into their library.|
 Magma Jet|Jumpstart|346|U|{1}{R}|Instant|||Magma Jet deals 2 damage to any target. Scry 2.|
 Magmaquake|Jumpstart|347|R|{X}{R}{R}|Instant|||Magmaquake deals X damage to each creature without flying and each planeswalker.|
 Molten Ravager|Jumpstart|351|C|{2}{R}|Creature - Elemental|0|4|{R}: Molten Ravager gets +1/+0 until end of turn.|
 Pillar of Flame|Jumpstart|355|C|{R}|Sorcery|||Pillar of Flame deals 2 damage to any target. If a creature dealt damage this way would die this turn, exile it instead.|
 Pyroclastic Elemental|Jumpstart|356|U|{3}{R}{R}|Creature - Elemental|5|4|{1}{R}{R}: Pyroclastic Elemental deals 1 damage to target player.|
+Rageblood Shaman|Jumpstart|357|R|{1}{R}{R}|Creature - Minotaur Shaman|2|3|Trample$Other Minotaur creatures you control get +1/+1 and have trample.|
+Riddle of Lightning|Jumpstart|359|U|{3}{R}{R}|Instant|||Choose any target. Scry 3, then reveal the top card of your library. Riddle of Lightning deals damage equal to that card's converted mana cost to that permanent or player.|
 Seismic Elemental|Jumpstart|362|U|{3}{R}{R}|Creature - Elemental|4|4|When Seismic Elemental enters the battlefield, creatures without flying can't block this turn.|
 Spitting Earth|Jumpstart|364|C|{1}{R}|Sorcery|||Spitting Earth deals damage to target creature equal to the number of Mountains you control.|
 Volcanic Fallout|Jumpstart|368|U|{1}{R}{R}|Instant|||This spell can't be countered.$Volcanic Fallout deals 2 damage to each creature and each player.|
+Weaver of Lightning|Jumpstart|371|U|{2}{R}|Creature - Human Shaman|1|4|Reach$Whenever you cast an instant or sorcery spell, Weaver of Lightning deals 1 damage to target creature an opponent controls.|
 Young Pyromancer|Jumpstart|372|U|{1}{R}|Creature - Human Shaman|2|1|Whenever you cast an instant or sorcery spell, create a 1/1 red Elemental creature token.|
 Affectionate Indrik|Jumpstart|373|U|{5}{G}|Creature - Beast|4|4|When Affectionate Indrik enters the battlefield, you may have it fight target creature you don't control.|
 Aggressive Urge|Jumpstart|374|C|{1}{G}|Instant|||Target creature gets +1/+1 until end of turn.$Draw a card.|
@@ -37881,12 +37967,16 @@ Enlarge|Jumpstart|392|U|{3}{G}{G}|Sorcery|||Target creature gets +7/+7 and gains
 Explore|Jumpstart|393|C|{1}{G}|Sorcery|||You may play an additional land this turn.$Draw a card.|
 Fa'adiyah Seer|Jumpstart|394|C|{1}{G}|Creature - Human Shaman|1|1|{T}: Draw a card and reveal it. If it isn't a land card, discard it.|
 Feral Hydra|Jumpstart|395|U|{X}{G}|Creature - Hydra Beast|0|0|Feral Hydra enters the battlefield with X +1/+1 counters on it.${3}: Put a +1/+1 counter on Feral Hydra. Any player may activate this ability.|
+Feral Invocation|Jumpstart|396|C|{2}{G}|Enchantment - Aura|||Flash$Enchant creature$Enchanted creature gets +2/+2.|
+Feral Prowler|Jumpstart|397|C|{1}{G}|Creature - Cat|1|3|When Feral Prowler dies, draw a card.|
 Fertilid|Jumpstart|398|C|{2}{G}|Creature - Elemental|0|0|Fertilid enters the battlefield with two +1/+1 counters on it.${1}{G}, Remove a +1/+1 counter from Fertilid: Target player searches their library for a basic land card, puts it onto the battlefield tapped, then shuffles their library.|
 Ghalta, Primal Hunger|Jumpstart|399|R|{10}{G}{G}|Legendary Creature - Elder Dinosaur|12|12|This spell costs {X} less to cast, where X is the total power of creatures you control.$Trample|
 Ghirapur Guide|Jumpstart|400|U|{2}{G}|Creature - Elf Scout|3|2|{2}{G}: Target creature you control can't be blocked by creatures with power 2 or less this turn.|
 Grave Bramble|Jumpstart|401|C|{1}{G}{G}|Creature - Plant|3|4|Defender, protection from Zombies|
 Hunter's Insight|Jumpstart|402|U|{2}{G}|Instant|||Choose target creature you control. Whenever that creature deals combat damage to a player or planeswalker this turn, draw that many cards.|
+Initiate's Companion|Jumpstart|403|C|{1}{G}|Creature - Cat|3|1|Whenever Initiate's Companion deals combat damage to a player, untap target creature or land.|
 Irresistible Prey|Jumpstart|406|U|{G}|Sorcery|||Target creature must be blocked this turn if able.$Draw a card.|
+Keeper of Fables|Jumpstart|407|U|{3}{G}{G}|Creature - Cat|4|5|Whenever one or more non-Human creatures you control deal combat damage to a player, draw a card.|
 Leaf Gilder|Jumpstart|408|C|{1}{G}|Creature - Elf Druid|2|1|{T}: Add {G}.|
 Lurking Predators|Jumpstart|410|R|{4}{G}{G}|Enchantment|||Whenever an opponent casts a spell, reveal the top card of your library. If it's a creature card, put it onto the battlefield. Otherwise, you may put that card on the bottom of your library.|
 Momentous Fall|Jumpstart|411|R|{2}{G}{G}|Instant|||As an additional cost to cast this spell, sacrifice a creature.$You draw cards equal to the sacrificed creature's power, then you gain life equal to its toughness.|
@@ -37896,6 +37986,7 @@ Oracle of Mul Daya|Jumpstart|415|R|{3}{G}|Creature - Elf Shaman|2|2|You may play
 Orazca Frillback|Jumpstart|416|C|{2}{G}|Creature - Dinosaur|4|2||
 Overgrown Battlement|Jumpstart|417|U|{1}{G}|Creature - Wall|0|4|Defender${T}: Add {G} for each creature with defender you control.|
 Penumbra Bobcat|Jumpstart|418|C|{2}{G}|Creature - Cat|2|1|When Penumbra Bobcat dies, create a 2/1 black Cat creature token.|
+Pouncing Cheetah|Jumpstart|419|C|{2}{G}|Creature - Cat|3|2|Flash|
 Presence of Gond|Jumpstart|420|C|{2}{G}|Enchantment - Aura|||Enchant creature$Enchanted creature has "{T}: Create a 1/1 green Elf Warrior creature token."|
 Primordial Sage|Jumpstart|422|R|{4}{G}{G}|Creature - Spirit|4|5|Whenever you cast a creature spell, you may draw a card.|
 Rampaging Brontodon|Jumpstart|423|R|{5}{G}{G}|Creature - Dinosaur|7|7|Trample$Whenever Rampaging Brontodon attacks, it gets +1/+1 until end of turn for each land you control.|
@@ -37911,12 +38002,14 @@ Sylvan Brushstrider|Jumpstart|434|C|{2}{G}|Creature - Beast|3|2|When Sylvan Brus
 Sylvan Ranger|Jumpstart|435|C|{1}{G}|Creature - Elf Scout|1|1|When Sylvan Ranger enters the battlefield, you may search your library for a basic land card, reveal it, put it into your hand, then shuffle your library.|
 Thragtusk|Jumpstart|436|R|{4}{G}|Creature - Beast|5|3|When Thragtusk enters the battlefield, you gain 5 life.$When Thragtusk leaves the battlefield, create a 3/3 green Beast creature token.|
 Thundering Spineback|Jumpstart|437|U|{5}{G}{G}|Creature - Dinosaur|5|5|Other Dinosaurs you control get +1/+1.${5}{G}: Create a 3/3 green Dinosaur creature token with trample.|
+Time to Feed|Jumpstart|438|C|{2}{G}|Sorcery|||Choose target creature an opponent controls. When that creature dies this turn, you gain 3 life. Target creature you control fights that creature.|
 Ulvenwald Hydra|Jumpstart|439|M|{4}{G}{G}|Creature - Hydra|*|*|Reach$Ulvenwald Hydra's power and toughness are each equal to the number of lands you control.$When Ulvenwald Hydra enters the battlefield, you may search your library for a land card, put it onto the battlefield tapped, then shuffle your library.|
 Vastwood Zendikon|Jumpstart|440|C|{4}{G}|Enchantment - Aura|||Enchant land$Enchanted land is a 6/4 green Elemental creature. It's still a land.$When enchanted land dies, return that card to its owner's hand.|
 Verdant Embrace|Jumpstart|441|R|{3}{G}{G}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +3/+3 and has "At the beginning of each upkeep, create a 1/1 green Saproling creature token."|
 Wall of Blossoms|Jumpstart|442|U|{1}{G}|Creature - Plant Wall|0|4|Defender$When Wall of Blossoms enters the battlefield, draw a card.|
 Wall of Vines|Jumpstart|443|C|{G}|Creature - Plant Wall|0|3|Defender$Reach|
 Wildheart Invoker|Jumpstart|444|C|{2}{G}{G}|Creature - Elf Shaman|4|3|{8}: Target creature gets +5/+5 and gains trample until end of turn.|
+Wildsize|Jumpstart|445|C|{2}{G}|Instant|||Target creature gets +2/+2 and gains trample until end of turn.$Draw a card.|
 Woodborn Behemoth|Jumpstart|446|U|{3}{G}{G}|Creature - Elemental|4|4|As long as you control eight or more lands, Woodborn Behemoth gets +4/+4 and has trample.|
 Wren's Run Vanquisher|Jumpstart|447|U|{1}{G}|Creature - Elf Warrior|3|3|As an additional cost to cast this spell, reveal an Elf card from your hand or pay {3}.$Deathtouch|
 Zendikar's Roil|Jumpstart|448|U|{3}{G}{G}|Enchantment|||Whenever a land enters the battlefield under your control, create a 2/2 green Elemental creature token.|
@@ -37938,10 +38031,15 @@ Dreamstone Hedron|Jumpstart|464|U|{6}|Artifact|||{T}: Add {C}{C}{C}.${3}, {T}, S
 Gargoyle Sentinel|Jumpstart|465|U|{3}|Artifact Creature - Gargoyle|3|3|Defender${3}: Until end of turn, Gargoyle Sentinel loses defender and gains flying.|
 Gingerbrute|Jumpstart|466|C|{1}|Artifact Creature - Food Golem|1|1|Haste${1}: Gingerbrute can't be blocked this turn except by creatures with haste.${2}, {T}, Sacrifice Gingerbrute: You gain 3 life.|
 Hedron Archive|Jumpstart|468|U|{4}|Artifact|||{T}: Add {C}{C}.${2}, {T}, Sacrifice Hedron Archive: Draw two cards.|
+Herald's Horn|Jumpstart|469|U|{3}|Artifact|||As Herald's Horn enters the battlefield, choose a creature type.$Creature spells you cast of the chosen type cost {1} less to cast.$At the beginning of your upkeep, look at the top card of your library. If it's a creature card of the chosen type, you may reveal it and put it into your hand.|
 Jousting Dummy|Jumpstart|470|C|{2}|Artifact Creature - Scarecrow Knight|2|1|{3}: Jousting Dummy gets +1/+0 until end of turn.|
 Juggernaut|Jumpstart|471|U|{4}|Artifact Creature - Juggernaut|5|3|Juggernaut attacks each combat if able.$Juggernaut can't be blocked by Walls.|
+Mana Geode|Jumpstart|472|C|{3}|Artifact|||When Mana Geode enters the battlefield, scry 1.${T}: Add one mana of any color.|
 Marauder's Axe|Jumpstart|473|C|{2}|Artifact - Equipment|||Equipped creature gets +2/+0.$Equip {2}|
 Meteor Golem|Jumpstart|474|U|{7}|Artifact Creature - Golem|3|3|When Meteor Golem enters the battlefield, destroy target nonland permanent an opponent controls.|
+Myr Sire|Jumpstart|475|C|{2}|Artifact Creature - Myr|1|1|When Myr Sire dies, create a 1/1 colorless Myr artifact creature token.|
+Perilous Myr|Jumpstart|476|U|{2}|Artifact Creature - Myr|1|1|When Perilous Myr dies, it deals 2 damage to any target.|
+Pirate's Cutlass|Jumpstart|477|C|{3}|Artifact - Equipment|||When Pirate's Cutlass enters the battlefield, attach it to target Pirate you control.$Equipped creature gets +2/+1.$Equip {2}|
 Prophetic Prism|Jumpstart|478|C|{2}|Artifact|||When Prophetic Prism enters the battlefield, draw a card.${1}, {T}: Add one mana of any color.|
 Roving Keep|Jumpstart|480|C|{7}|Artifact Creature - Wall|5|7|Defender${7}: Roving Keep gets +2/+0 and gains trample until end of turn. It can attack this turn as though it didn't have defender.|
 Runed Servitor|Jumpstart|481|C|{2}|Artifact Creature - Construct|2|2|When Runed Servitor dies, each player draws a card.|
@@ -37951,6 +38049,7 @@ Signpost Scarecrow|Jumpstart|485|C|{4}|Artifact Creature - Scarecrow|2|4|Vigilan
 Skittering Surveyor|Jumpstart|486|C|{3}|Artifact Creature - Construct|1|2|When Skittering Surveyor enters the battlefield, you may search your library for a basic land card, reveal it, put it into your hand, then shuffle your library.|
 Suspicious Bookcase|Jumpstart|487|U|{2}|Artifact Creature - Wall|0|4|Defender${3}, {T}: Target creature can't be blocked this turn.|
 Terrarion|Jumpstart|488|C|{1}|Artifact|||Terrarion enters the battlefield tapped.${2}, {T}, Sacrifice Terrarion: Add two mana in any combination of colors.$When Terrarion is put into a graveyard from the battlefield, draw a card.|
+Unstable Obelisk|Jumpstart|489|U|{3}|Artifact|||{T}: Add {C}.${7}, {T}, Sacrifice Unstable Obelisk: Destroy target permanent.|
 Warmonger's Chariot|Jumpstart|490|U|{2}|Artifact - Equipment|||Equipped creature gets +2/+2.$As long as equipped creature has defender, it can attack as though it didn't have defender.$Equip {3}|
 Buried Ruin|Jumpstart|491|U||Land|||{T}: Add {C}.${2}, {T}, Sacrifice Buried Ruin: Return target artifact card from your graveyard to your hand.|
 Mirrodin's Core|Jumpstart|492|U||Land|||{T}: Add {C}.${T}: Put a charge counter on Mirrodin's Core.${T}, Remove a charge counter from Mirrodin's Core: Add one mana of any color.|

From ff91ffe1a76e193bf71d882461cea13da4e9ca4d Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Sat, 20 Jun 2020 03:04:33 +0400
Subject: [PATCH 421/586] Removed unnecessary custom text from some cards
 (opponent loses life and you gain life)

---
 Mage.Sets/src/mage/cards/a/AbundantMaw.java   | 11 ++-
 Mage.Sets/src/mage/cards/a/AlmsOfTheVein.java | 16 ++--
 .../mage/cards/b/BishopOfTheBloodstained.java |  7 +-
 Mage.Sets/src/mage/cards/b/BlightKeeper.java  |  7 +-
 Mage.Sets/src/mage/cards/b/BloodTribute.java  |  7 +-
 .../src/mage/cards/b/BloodbornScoundrels.java |  9 +--
 .../src/mage/cards/b/BrushWithDeath.java      |  8 +-
 .../src/mage/cards/b/BumpInTheNight.java      |  7 +-
 .../src/mage/cards/c/CollectiveBrutality.java | 37 +++------
 Mage.Sets/src/mage/cards/d/DakmorGhoul.java   | 12 +--
 .../src/mage/cards/d/DesiccatedNaga.java      |  7 +-
 .../src/mage/cards/d/DiregrafCaptain.java     | 17 ++--
 Mage.Sets/src/mage/cards/e/EbonyCharm.java    | 81 +++----------------
 .../src/mage/cards/e/EssenceDepleter.java     | 12 +--
 .../src/mage/cards/g/GeralfsMessenger.java    |  9 ++-
 .../mage/cards/g/GhostCouncilOfOrzhova.java   | 47 ++---------
 .../src/mage/cards/h/HierophantsChalice.java  |  7 +-
 .../src/mage/cards/n/NihilisticGlee.java      |  7 +-
 .../src/mage/cards/o/ObzedatGhostCouncil.java |  1 +
 .../src/mage/cards/p/PryingQuestions.java     | 14 ++--
 Mage.Sets/src/mage/cards/q/QarsiSadist.java   | 13 ++-
 Mage.Sets/src/mage/cards/s/SanguineBond.java  |  8 +-
 Mage.Sets/src/mage/cards/s/ShadowSlice.java   | 12 ++-
 .../src/mage/cards/s/SkymarchBloodletter.java |  7 +-
 .../src/mage/cards/v/VampireSovereign.java    |  2 +-
 .../src/mage/cards/w/WaywardDisciple.java     | 12 +--
 .../java/mage/verify/VerifyCardDataTest.java  |  9 ++-
 27 files changed, 122 insertions(+), 264 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/a/AbundantMaw.java b/Mage.Sets/src/mage/cards/a/AbundantMaw.java
index 3d558b7498..8892b9070c 100644
--- a/Mage.Sets/src/mage/cards/a/AbundantMaw.java
+++ b/Mage.Sets/src/mage/cards/a/AbundantMaw.java
@@ -1,7 +1,5 @@
-
 package mage.cards.a;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.costs.mana.ManaCostsImpl;
@@ -15,14 +13,15 @@ import mage.constants.CardType;
 import mage.constants.SubType;
 import mage.target.common.TargetOpponent;
 
+import java.util.UUID;
+
 /**
- *
  * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com)
  */
 public final class AbundantMaw extends CardImpl {
 
     public AbundantMaw(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{8}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{8}");
         this.subtype.add(SubType.ELDRAZI);
         this.subtype.add(SubType.LEECH);
         this.power = new MageInt(6);
@@ -30,11 +29,11 @@ public final class AbundantMaw extends CardImpl {
 
         // Emerge {6}{B}
         this.addAbility(new EmergeAbility(this, new ManaCostsImpl<>("{6}{B}")));
-        
+
         // When you cast Abundant Maw, target opponent loses 3 life and you gain 3 life.
         Ability ability = new CastSourceTriggeredAbility(new GainLifeEffect(3));
+        ability.addEffect(new LoseLifeTargetEffect(3).concatBy("and"));
         ability.addTarget(new TargetOpponent());
-        ability.addEffect(new LoseLifeTargetEffect(3));
         this.addAbility(ability);
     }
 
diff --git a/Mage.Sets/src/mage/cards/a/AlmsOfTheVein.java b/Mage.Sets/src/mage/cards/a/AlmsOfTheVein.java
index bca4bcfbdb..6b337a6bf0 100644
--- a/Mage.Sets/src/mage/cards/a/AlmsOfTheVein.java
+++ b/Mage.Sets/src/mage/cards/a/AlmsOfTheVein.java
@@ -1,9 +1,6 @@
-
 package mage.cards.a;
 
-import java.util.UUID;
 import mage.abilities.costs.mana.ManaCostsImpl;
-import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.GainLifeEffect;
 import mage.abilities.effects.common.LoseLifeTargetEffect;
 import mage.abilities.keyword.MadnessAbility;
@@ -12,23 +9,20 @@ import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.target.common.TargetOpponent;
 
+import java.util.UUID;
+
 /**
- *
  * @author fireshoes
  */
 public final class AlmsOfTheVein extends CardImpl {
 
     public AlmsOfTheVein(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{B}");
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}");
 
         // Target opponent loses 3 life and you gain 3 life.
-        Effect effect = new LoseLifeTargetEffect(3);
-        effect.setText("Target opponent loses 3 life");
-        this.getSpellAbility().addEffect(effect);
+        this.getSpellAbility().addEffect(new LoseLifeTargetEffect(3));
+        this.getSpellAbility().addEffect(new GainLifeEffect(3).concatBy("and"));
         this.getSpellAbility().addTarget(new TargetOpponent());
-        effect = new GainLifeEffect(3);
-        effect.setText("and you gain 3 life");
-        this.getSpellAbility().addEffect(effect);
 
         // Madness {B}
         this.addAbility(new MadnessAbility(this, new ManaCostsImpl("{B}")));
diff --git a/Mage.Sets/src/mage/cards/b/BishopOfTheBloodstained.java b/Mage.Sets/src/mage/cards/b/BishopOfTheBloodstained.java
index 7454450cfd..d36f35dd5a 100644
--- a/Mage.Sets/src/mage/cards/b/BishopOfTheBloodstained.java
+++ b/Mage.Sets/src/mage/cards/b/BishopOfTheBloodstained.java
@@ -1,7 +1,5 @@
-
 package mage.cards.b;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
@@ -15,8 +13,9 @@ import mage.constants.TargetController;
 import mage.filter.common.FilterCreaturePermanent;
 import mage.target.common.TargetOpponent;
 
+import java.util.UUID;
+
 /**
- *
  * @author TheElk801
  */
 public final class BishopOfTheBloodstained extends CardImpl {
@@ -36,7 +35,7 @@ public final class BishopOfTheBloodstained extends CardImpl {
         this.power = new MageInt(3);
         this.toughness = new MageInt(3);
 
-        // When Bishop of the Bloodstained enters the battlefield, target opponent loses 1 life for each vampire you control.
+        // When Bishop of the Bloodstained enters the battlefield, target opponent loses 1 life for each Vampire you control.
         Ability ability = new EntersBattlefieldTriggeredAbility(new LoseLifeTargetEffect(new PermanentsOnBattlefieldCount(filter)));
         ability.addTarget(new TargetOpponent());
         this.addAbility(ability);
diff --git a/Mage.Sets/src/mage/cards/b/BlightKeeper.java b/Mage.Sets/src/mage/cards/b/BlightKeeper.java
index 985e69d9ec..e72e5b1a9d 100644
--- a/Mage.Sets/src/mage/cards/b/BlightKeeper.java
+++ b/Mage.Sets/src/mage/cards/b/BlightKeeper.java
@@ -1,7 +1,5 @@
-
 package mage.cards.b;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleActivatedAbility;
@@ -18,8 +16,9 @@ import mage.constants.SubType;
 import mage.constants.Zone;
 import mage.target.common.TargetOpponent;
 
+import java.util.UUID;
+
 /**
- *
  * @author TheElk801
  */
 public final class BlightKeeper extends CardImpl {
@@ -37,7 +36,7 @@ public final class BlightKeeper extends CardImpl {
 
         // {7}{B}, {T}, Sacrifice Blight Keeper: Target opponent loses 4 life and you gain 4 life.
         Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new LoseLifeTargetEffect(4), new ManaCostsImpl("{7}{B}"));
-        ability.addEffect(new GainLifeEffect(4).setText("and you gain 4 life"));
+        ability.addEffect(new GainLifeEffect(4).concatBy("and"));
         ability.addTarget(new TargetOpponent());
         ability.addCost(new TapSourceCost());
         ability.addCost(new SacrificeSourceCost());
diff --git a/Mage.Sets/src/mage/cards/b/BloodTribute.java b/Mage.Sets/src/mage/cards/b/BloodTribute.java
index 694c16ac06..5bc65b7e7e 100644
--- a/Mage.Sets/src/mage/cards/b/BloodTribute.java
+++ b/Mage.Sets/src/mage/cards/b/BloodTribute.java
@@ -1,7 +1,5 @@
-
 package mage.cards.b;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.condition.common.KickedCondition;
 import mage.abilities.costs.common.TapTargetCost;
@@ -24,8 +22,9 @@ import mage.players.Player;
 import mage.target.common.TargetControlledCreaturePermanent;
 import mage.target.common.TargetOpponent;
 
+import java.util.UUID;
+
 /**
- *
  * @author North
  */
 public final class BloodTribute extends CardImpl {
@@ -41,8 +40,8 @@ public final class BloodTribute extends CardImpl {
         this.addAbility(new KickerAbility(new TapTargetCost(new TargetControlledCreaturePermanent(1, 1, filter, true))));
 
         // Target opponent loses half their life, rounded up.
-        this.getSpellAbility().addTarget(new TargetOpponent());
         this.getSpellAbility().addEffect(new BloodTributeLoseLifeEffect());
+        this.getSpellAbility().addTarget(new TargetOpponent());
 
         // If Blood Tribute was kicked, you gain life equal to the life lost this way.
         Effect effect = new ConditionalOneShotEffect(
diff --git a/Mage.Sets/src/mage/cards/b/BloodbornScoundrels.java b/Mage.Sets/src/mage/cards/b/BloodbornScoundrels.java
index 87837760ce..b55b53e9ba 100644
--- a/Mage.Sets/src/mage/cards/b/BloodbornScoundrels.java
+++ b/Mage.Sets/src/mage/cards/b/BloodbornScoundrels.java
@@ -1,21 +1,20 @@
-
 package mage.cards.b;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.effects.common.GainLifeEffect;
 import mage.abilities.effects.common.LoseLifeTargetEffect;
-import mage.constants.SubType;
 import mage.abilities.keyword.AssistAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
+import mage.constants.SubType;
 import mage.target.common.TargetOpponent;
 
+import java.util.UUID;
+
 /**
- *
  * @author TheElk801
  */
 public final class BloodbornScoundrels extends CardImpl {
@@ -33,7 +32,7 @@ public final class BloodbornScoundrels extends CardImpl {
 
         // When Bloodborn Scoundrels enters the battlefield, target opponent loses 2 life and you gain 2 life.
         Ability ability = new EntersBattlefieldTriggeredAbility(new LoseLifeTargetEffect(2));
-        ability.addEffect(new GainLifeEffect(2).setText("and you gain 2 life"));
+        ability.addEffect(new GainLifeEffect(2).concatBy("and"));
         ability.addTarget(new TargetOpponent());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/b/BrushWithDeath.java b/Mage.Sets/src/mage/cards/b/BrushWithDeath.java
index f3a9b2da3e..78df808785 100644
--- a/Mage.Sets/src/mage/cards/b/BrushWithDeath.java
+++ b/Mage.Sets/src/mage/cards/b/BrushWithDeath.java
@@ -1,7 +1,5 @@
-
 package mage.cards.b;
 
-import java.util.UUID;
 import mage.abilities.effects.common.GainLifeEffect;
 import mage.abilities.effects.common.LoseLifeTargetEffect;
 import mage.abilities.keyword.BuybackAbility;
@@ -10,17 +8,19 @@ import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.target.common.TargetOpponent;
 
+import java.util.UUID;
+
 /**
- *
  * @author LoneFox
  */
 public final class BrushWithDeath extends CardImpl {
 
     public BrushWithDeath(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{B}");
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}");
 
         // Buyback {2}{B}{B}
         this.addAbility(new BuybackAbility("{2}{B}{B}"));
+
         // Target opponent loses 2 life. You gain 2 life.
         this.getSpellAbility().addEffect(new LoseLifeTargetEffect(2));
         this.getSpellAbility().addEffect(new GainLifeEffect(2));
diff --git a/Mage.Sets/src/mage/cards/b/BumpInTheNight.java b/Mage.Sets/src/mage/cards/b/BumpInTheNight.java
index 9dc048b4cf..024b2619e4 100644
--- a/Mage.Sets/src/mage/cards/b/BumpInTheNight.java
+++ b/Mage.Sets/src/mage/cards/b/BumpInTheNight.java
@@ -1,7 +1,5 @@
-
 package mage.cards.b;
 
-import java.util.UUID;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.common.LoseLifeTargetEffect;
 import mage.abilities.keyword.FlashbackAbility;
@@ -11,14 +9,15 @@ import mage.constants.CardType;
 import mage.constants.TimingRule;
 import mage.target.common.TargetOpponent;
 
+import java.util.UUID;
+
 /**
  * @author nantuko
  */
 public final class BumpInTheNight extends CardImpl {
 
     public BumpInTheNight(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{B}");
-
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{B}");
 
         // Target opponent loses 3 life.
         this.getSpellAbility().addEffect(new LoseLifeTargetEffect(3));
diff --git a/Mage.Sets/src/mage/cards/c/CollectiveBrutality.java b/Mage.Sets/src/mage/cards/c/CollectiveBrutality.java
index 9d60e693ab..985fbac516 100644
--- a/Mage.Sets/src/mage/cards/c/CollectiveBrutality.java
+++ b/Mage.Sets/src/mage/cards/c/CollectiveBrutality.java
@@ -3,7 +3,6 @@ package mage.cards.c;
 import mage.abilities.Mode;
 import mage.abilities.costs.Cost;
 import mage.abilities.costs.common.DiscardCardCost;
-import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.GainLifeEffect;
 import mage.abilities.effects.common.LoseLifeTargetEffect;
 import mage.abilities.effects.common.continuous.BoostTargetEffect;
@@ -15,11 +14,9 @@ import mage.constants.CardType;
 import mage.constants.Duration;
 import mage.constants.TargetController;
 import mage.filter.FilterCard;
-import mage.filter.FilterPlayer;
-import mage.filter.common.FilterCreaturePermanent;
 import mage.filter.predicate.Predicates;
-import mage.target.TargetPlayer;
 import mage.target.common.TargetCreaturePermanent;
+import mage.target.common.TargetOpponent;
 
 import java.util.UUID;
 
@@ -28,16 +25,11 @@ import java.util.UUID;
  */
 public final class CollectiveBrutality extends CardImpl {
 
-    private static final FilterCard filter = new FilterCard("instant or sorcery card");
-    private static final FilterPlayer filterDiscard = new FilterPlayer("opponent to discard");
-    private static final FilterCreaturePermanent filterCreatureMinus = new FilterCreaturePermanent("creature to get -2/-2");
-    private static final FilterPlayer filterLoseLife = new FilterPlayer("opponent to lose life");
+    private static final FilterCard filter = new FilterCard("an instant or sorcery card");
 
     static {
         filter.add(Predicates.or(CardType.INSTANT.getPredicate(),
                 CardType.SORCERY.getPredicate()));
-        filterDiscard.add(TargetController.OPPONENT.getPlayerPredicate());
-        filterLoseLife.add(TargetController.OPPONENT.getPlayerPredicate());
     }
 
     public CollectiveBrutality(UUID ownerId, CardSetInfo setInfo) {
@@ -52,28 +44,21 @@ public final class CollectiveBrutality extends CardImpl {
         this.getSpellAbility().getModes().setMinModes(1);
         this.getSpellAbility().getModes().setMaxModes(3);
 
-        // Target opponent reveals their hand. You choose an instant or sorcery card from it. That player discards that card.;
-        Effect effect = new DiscardCardYouChooseTargetEffect(filter, TargetController.ANY);
-        effect.setText("Target opponent reveals their hand. You choose an instant or sorcery card from it. That player discards that card");
-        this.getSpellAbility().addEffect(effect);
-        this.getSpellAbility().addTarget(new TargetPlayer(1, 1, false, filterDiscard).withChooseHint("reveals hand, you choose to discard"));
+        // Target opponent reveals their hand. You choose an instant or sorcery card from it. That player discards that card.
+        this.getSpellAbility().addEffect(new DiscardCardYouChooseTargetEffect(filter, TargetController.OPPONENT));
+        this.getSpellAbility().addTarget(new TargetOpponent().withChooseHint("reveals hand, you choose to discard"));
 
-        // Target creature gets -2/-2 until end of turn.;
+        // Target creature gets -2/-2 until end of turn.
         Mode mode = new Mode();
-        effect = new BoostTargetEffect(-2, -2, Duration.EndOfTurn);
-        effect.setText("Target creature gets -2/-2 until end of turn");
-        mode.addEffect(effect);
-        mode.addTarget(new TargetCreaturePermanent(filterCreatureMinus).withChooseHint("gets -2/-2 until end of turn"));
+        mode.addEffect(new BoostTargetEffect(-2, -2, Duration.EndOfTurn));
+        mode.addTarget(new TargetCreaturePermanent().withChooseHint("gets -2/-2 until end of turn"));
         this.getSpellAbility().addMode(mode);
 
         // Target opponent loses 2 life and you gain 2 life.
         mode = new Mode();
-        effect = new LoseLifeTargetEffect(2);
-        effect.setText("Target opponent loses 2 life");
-        mode.addEffect(effect);
-        mode.addTarget(new TargetPlayer(1, 1, false, filterLoseLife).withChooseHint("loses 2 life"));
-        effect = new GainLifeEffect(2);
-        mode.addEffect(effect.concatBy("and"));
+        mode.addEffect(new LoseLifeTargetEffect(2));
+        mode.addEffect(new GainLifeEffect(2).concatBy("and"));
+        mode.addTarget(new TargetOpponent().withChooseHint("loses 2 life"));
         this.getSpellAbility().addMode(mode);
     }
 
diff --git a/Mage.Sets/src/mage/cards/d/DakmorGhoul.java b/Mage.Sets/src/mage/cards/d/DakmorGhoul.java
index 8afa10f66a..34e559e459 100644
--- a/Mage.Sets/src/mage/cards/d/DakmorGhoul.java
+++ b/Mage.Sets/src/mage/cards/d/DakmorGhoul.java
@@ -1,11 +1,8 @@
-
 package mage.cards.d;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
-import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.GainLifeEffect;
 import mage.abilities.effects.common.LoseLifeTargetEffect;
 import mage.cards.CardImpl;
@@ -14,24 +11,23 @@ import mage.constants.CardType;
 import mage.constants.SubType;
 import mage.target.common.TargetOpponent;
 
+import java.util.UUID;
+
 /**
- *
  * @author fireshoes
  */
 public final class DakmorGhoul extends CardImpl {
 
     public DakmorGhoul(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{B}{B}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{B}");
         this.subtype.add(SubType.ZOMBIE);
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
 
         // When Dakmor Ghoul enters the battlefield, target opponent loses 2 life and you gain 2 life.
-        Effect effect = new GainLifeEffect(2);
-        effect.setText("and you gain 2 life");
         Ability ability = new EntersBattlefieldTriggeredAbility(new LoseLifeTargetEffect(2), false);
+        ability.addEffect(new GainLifeEffect(2).concatBy("and"));
         ability.addTarget(new TargetOpponent());
-        ability.addEffect(effect);
         this.addAbility(ability);
     }
 
diff --git a/Mage.Sets/src/mage/cards/d/DesiccatedNaga.java b/Mage.Sets/src/mage/cards/d/DesiccatedNaga.java
index a9fbe8b25f..b15a00889d 100644
--- a/Mage.Sets/src/mage/cards/d/DesiccatedNaga.java
+++ b/Mage.Sets/src/mage/cards/d/DesiccatedNaga.java
@@ -1,4 +1,3 @@
-
 package mage.cards.d;
 
 import mage.MageInt;
@@ -6,7 +5,6 @@ import mage.abilities.Ability;
 import mage.abilities.common.ActivateIfConditionActivatedAbility;
 import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
 import mage.abilities.costs.mana.ManaCostsImpl;
-import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.GainLifeEffect;
 import mage.abilities.effects.common.LoseLifeTargetEffect;
 import mage.cards.CardImpl;
@@ -20,7 +18,6 @@ import mage.target.common.TargetOpponent;
 import java.util.UUID;
 
 /**
- *
  * @author fireshoes
  */
 public final class DesiccatedNaga extends CardImpl {
@@ -44,10 +41,8 @@ public final class DesiccatedNaga extends CardImpl {
                 new LoseLifeTargetEffect(2),
                 new ManaCostsImpl("{3}{B}"),
                 new PermanentsOnTheBattlefieldCondition(filter));
+        ability.addEffect(new GainLifeEffect(2).concatBy("and"));
         ability.addTarget(new TargetOpponent());
-        Effect effect = new GainLifeEffect(2);
-        effect.setText("and you gain 2 life");
-        ability.addEffect(effect);
         this.addAbility(ability);
     }
 
diff --git a/Mage.Sets/src/mage/cards/d/DiregrafCaptain.java b/Mage.Sets/src/mage/cards/d/DiregrafCaptain.java
index 845221f6d8..52ee1b1f96 100644
--- a/Mage.Sets/src/mage/cards/d/DiregrafCaptain.java
+++ b/Mage.Sets/src/mage/cards/d/DiregrafCaptain.java
@@ -1,7 +1,5 @@
-
 package mage.cards.d;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.TriggeredAbilityImpl;
 import mage.abilities.common.SimpleStaticAbility;
@@ -22,8 +20,9 @@ import mage.game.events.ZoneChangeEvent;
 import mage.game.permanent.Permanent;
 import mage.target.common.TargetOpponent;
 
+import java.util.UUID;
+
 /**
- *
  * @author Loki
  */
 public final class DiregrafCaptain extends CardImpl {
@@ -35,16 +34,18 @@ public final class DiregrafCaptain extends CardImpl {
     }
 
     public DiregrafCaptain(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}{B}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{B}");
         this.subtype.add(SubType.ZOMBIE);
         this.subtype.add(SubType.SOLDIER);
-
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
 
+        // Deathtouch
         this.addAbility(DeathtouchAbility.getInstance());
+
         // Other Zombie creatures you control get +1/+1.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield, filter, true)));
+
         // Whenever another Zombie you control dies, target opponent loses 1 life.
         this.addAbility(new DiregrafCaptainTriggeredAbility());
     }
@@ -84,12 +85,10 @@ class DiregrafCaptainTriggeredAbility extends TriggeredAbilityImpl {
     @Override
     public boolean checkTrigger(GameEvent event, Game game) {
         if (!event.getTargetId().equals(this.getSourceId())) {
-            ZoneChangeEvent zEvent = (ZoneChangeEvent)event;
+            ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
             if (zEvent.isDiesEvent()) {
                 Permanent p = (Permanent) game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD);
-                if (p != null && p.isControlledBy(this.controllerId) && filter.match(p, game)) {
-                    return true;
-                }
+                return p != null && p.isControlledBy(this.controllerId) && filter.match(p, game);
             }
         }
         return false;
diff --git a/Mage.Sets/src/mage/cards/e/EbonyCharm.java b/Mage.Sets/src/mage/cards/e/EbonyCharm.java
index f3ed1de7c0..b6f9245471 100644
--- a/Mage.Sets/src/mage/cards/e/EbonyCharm.java
+++ b/Mage.Sets/src/mage/cards/e/EbonyCharm.java
@@ -1,26 +1,23 @@
 package mage.cards.e;
 
-import java.util.UUID;
-import mage.abilities.Ability;
 import mage.abilities.Mode;
-import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.ExileTargetEffect;
+import mage.abilities.effects.common.GainLifeEffect;
+import mage.abilities.effects.common.LoseLifeTargetEffect;
 import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
 import mage.abilities.keyword.FearAbility;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.Duration;
-import mage.constants.Outcome;
-import mage.filter.FilterCard;
-import mage.game.Game;
-import mage.players.Player;
+import mage.filter.StaticFilters;
 import mage.target.common.TargetCardInASingleGraveyard;
 import mage.target.common.TargetCreaturePermanent;
 import mage.target.common.TargetOpponent;
 
+import java.util.UUID;
+
 /**
- *
  * @author fireshoes
  */
 public final class EbonyCharm extends CardImpl {
@@ -29,13 +26,14 @@ public final class EbonyCharm extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}");
 
         // Choose one - Target opponent loses 1 life and you gain 1 life;
-        this.getSpellAbility().addEffect(new EbonyCharmDrainEffect());
+        this.getSpellAbility().addEffect(new LoseLifeTargetEffect(1));
+        this.getSpellAbility().addEffect(new GainLifeEffect(1).concatBy("and"));
         this.getSpellAbility().addTarget(new TargetOpponent());
 
         // or exile up to three target cards from a single graveyard; 
         Mode mode = new Mode();
-        mode.addEffect(new EbonyCharmExileEffect());
-        mode.addTarget((new TargetCardInASingleGraveyard(0, 3, new FilterCard("up to three target cards from a single graveyard"))));
+        mode.addEffect(new ExileTargetEffect());
+        mode.addTarget((new TargetCardInASingleGraveyard(0, 3, StaticFilters.FILTER_CARD_CARDS)));
         this.getSpellAbility().addMode(mode);
 
         // or target creature gains fear until end of turn.
@@ -54,62 +52,3 @@ public final class EbonyCharm extends CardImpl {
         return new EbonyCharm(this);
     }
 }
-
-class EbonyCharmDrainEffect extends OneShotEffect {
-
-    EbonyCharmDrainEffect() {
-        super(Outcome.Damage);
-        staticText = "target opponent loses 1 life and you gain 1 life";
-    }
-
-    EbonyCharmDrainEffect(final EbonyCharmDrainEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        Player targetOpponent = game.getPlayer(source.getFirstTarget());
-        Player controller = game.getPlayer(source.getControllerId());
-        if (targetOpponent != null
-                && controller != null) {
-            targetOpponent.loseLife(1, game, false);
-            controller.gainLife(1, game, source);
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    public EbonyCharmDrainEffect copy() {
-        return new EbonyCharmDrainEffect(this);
-    }
-
-}
-
-class EbonyCharmExileEffect extends OneShotEffect {
-
-    public EbonyCharmExileEffect() {
-        super(Outcome.Exile);
-        this.staticText = "Exile up to three target cards from a single graveyard";
-    }
-
-    public EbonyCharmExileEffect(final EbonyCharmExileEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public EbonyCharmExileEffect copy() {
-        return new EbonyCharmExileEffect(this);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        for (UUID targetID : source.getTargets().get(0).getTargets()) {
-            Card card = game.getCard(targetID);
-            if (card != null) {
-                card.moveToExile(null, "", source.getSourceId(), game);
-            }
-        }
-        return true;
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/e/EssenceDepleter.java b/Mage.Sets/src/mage/cards/e/EssenceDepleter.java
index 6f54e14200..185570dfd4 100644
--- a/Mage.Sets/src/mage/cards/e/EssenceDepleter.java
+++ b/Mage.Sets/src/mage/cards/e/EssenceDepleter.java
@@ -1,12 +1,9 @@
-
 package mage.cards.e;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
-import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.GainLifeEffect;
 import mage.abilities.effects.common.LoseLifeTargetEffect;
 import mage.abilities.keyword.DevoidAbility;
@@ -17,14 +14,15 @@ import mage.constants.SubType;
 import mage.constants.Zone;
 import mage.target.common.TargetOpponent;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class EssenceDepleter extends CardImpl {
 
     public EssenceDepleter(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{B}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}");
         this.subtype.add(SubType.ELDRAZI);
         this.subtype.add(SubType.DRONE);
         this.power = new MageInt(2);
@@ -35,9 +33,7 @@ public final class EssenceDepleter extends CardImpl {
 
         // {1}{C}: Target opponent loses 1 life and you gain 1 life.
         Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new LoseLifeTargetEffect(1), new ManaCostsImpl("{1}{C}"));
-        Effect effect = new GainLifeEffect(1);
-        effect.setText("and you gain 1 life");
-        ability.addEffect(effect);
+        ability.addEffect(new GainLifeEffect(1).concatBy("and"));
         ability.addTarget(new TargetOpponent());
         this.addAbility(ability);
 
diff --git a/Mage.Sets/src/mage/cards/g/GeralfsMessenger.java b/Mage.Sets/src/mage/cards/g/GeralfsMessenger.java
index f2445f84dd..347c139b77 100644
--- a/Mage.Sets/src/mage/cards/g/GeralfsMessenger.java
+++ b/Mage.Sets/src/mage/cards/g/GeralfsMessenger.java
@@ -1,7 +1,5 @@
-
 package mage.cards.g;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldTappedAbility;
@@ -14,14 +12,15 @@ import mage.constants.CardType;
 import mage.constants.SubType;
 import mage.target.common.TargetOpponent;
 
+import java.util.UUID;
+
 /**
- *
  * @author Loki
  */
 public final class GeralfsMessenger extends CardImpl {
 
     public GeralfsMessenger(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{B}{B}{B}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}{B}{B}");
         this.subtype.add(SubType.ZOMBIE);
 
         this.power = new MageInt(3);
@@ -29,10 +28,12 @@ public final class GeralfsMessenger extends CardImpl {
 
         // Geralf's Messenger enters the battlefield tapped.
         this.addAbility(new EntersBattlefieldTappedAbility());
+
         // When Geralf's Messenger enters the battlefield, target opponent loses 2 life.
         Ability ability = new EntersBattlefieldTriggeredAbility(new LoseLifeTargetEffect(2));
         ability.addTarget(new TargetOpponent());
         this.addAbility(ability);
+
         // Undying
         this.addAbility(new UndyingAbility());
     }
diff --git a/Mage.Sets/src/mage/cards/g/GhostCouncilOfOrzhova.java b/Mage.Sets/src/mage/cards/g/GhostCouncilOfOrzhova.java
index e9181831f2..d4b7ec441a 100644
--- a/Mage.Sets/src/mage/cards/g/GhostCouncilOfOrzhova.java
+++ b/Mage.Sets/src/mage/cards/g/GhostCouncilOfOrzhova.java
@@ -1,29 +1,28 @@
 package mage.cards.g;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.common.SacrificeTargetCost;
 import mage.abilities.costs.mana.GenericManaCost;
-import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.ExileReturnBattlefieldOwnerNextEndStepSourceEffect;
+import mage.abilities.effects.common.GainLifeEffect;
+import mage.abilities.effects.common.LoseLifeTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.Outcome;
 import mage.constants.SubType;
 import mage.constants.SuperType;
 import mage.constants.Zone;
-import static mage.filter.StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT;
-import mage.game.Game;
-import mage.players.Player;
 import mage.target.common.TargetControlledCreaturePermanent;
 import mage.target.common.TargetOpponent;
 
+import java.util.UUID;
+
+import static mage.filter.StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT;
+
 /**
- *
  * @author Loki
  */
 public final class GhostCouncilOfOrzhova extends CardImpl {
@@ -37,7 +36,8 @@ public final class GhostCouncilOfOrzhova extends CardImpl {
         this.toughness = new MageInt(4);
 
         // When Ghost Council of Orzhova enters the battlefield, target opponent loses 1 life and you gain 1 life.
-        Ability ability = new EntersBattlefieldTriggeredAbility(new GhostCouncilOfOrzhovaEffect());
+        Ability ability = new EntersBattlefieldTriggeredAbility(new LoseLifeTargetEffect(1));
+        ability.addEffect(new GainLifeEffect(1).concatBy("and"));
         ability.addTarget(new TargetOpponent());
         this.addAbility(ability);
 
@@ -62,34 +62,3 @@ public final class GhostCouncilOfOrzhova extends CardImpl {
     }
 
 }
-
-class GhostCouncilOfOrzhovaEffect extends OneShotEffect {
-
-    GhostCouncilOfOrzhovaEffect() {
-        super(Outcome.GainLife);
-        staticText = "target opponent loses 1 life and you gain 1 life";
-    }
-
-    GhostCouncilOfOrzhovaEffect(final GhostCouncilOfOrzhovaEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        Player targetPlayer = game.getPlayer(source.getFirstTarget());
-        Player controller = game.getPlayer(source.getControllerId());
-        if (targetPlayer != null 
-                && controller != null) {
-            targetPlayer.loseLife(1, game, false);
-            controller.gainLife(1, game, source);
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    public GhostCouncilOfOrzhovaEffect copy() {
-        return new GhostCouncilOfOrzhovaEffect(this);
-    }
-
-}
diff --git a/Mage.Sets/src/mage/cards/h/HierophantsChalice.java b/Mage.Sets/src/mage/cards/h/HierophantsChalice.java
index d803d89a9b..8fa3f4a8b8 100644
--- a/Mage.Sets/src/mage/cards/h/HierophantsChalice.java
+++ b/Mage.Sets/src/mage/cards/h/HierophantsChalice.java
@@ -1,7 +1,5 @@
-
 package mage.cards.h;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.effects.common.GainLifeEffect;
@@ -13,8 +11,9 @@ import mage.constants.CardType;
 import mage.target.Target;
 import mage.target.common.TargetOpponent;
 
+import java.util.UUID;
+
 /**
- *
  * @author TheElk801
  */
 public final class HierophantsChalice extends CardImpl {
@@ -24,7 +23,7 @@ public final class HierophantsChalice extends CardImpl {
 
         // When Hierophant's Chalice enters the battlefield, target opponent loses 1 life and you gain 1 life.
         Ability ability = new EntersBattlefieldTriggeredAbility(new LoseLifeTargetEffect(1), false);
-        ability.addEffect(new GainLifeEffect(1).setText("and you gain one life."));
+        ability.addEffect(new GainLifeEffect(1).concatBy("and"));
         Target target = new TargetOpponent();
         ability.addTarget(target);
         this.addAbility(ability);
diff --git a/Mage.Sets/src/mage/cards/n/NihilisticGlee.java b/Mage.Sets/src/mage/cards/n/NihilisticGlee.java
index 5590c72316..5cd67d38c8 100644
--- a/Mage.Sets/src/mage/cards/n/NihilisticGlee.java
+++ b/Mage.Sets/src/mage/cards/n/NihilisticGlee.java
@@ -1,7 +1,5 @@
-
 package mage.cards.n;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.condition.common.HellbentCondition;
@@ -20,8 +18,9 @@ import mage.constants.CardType;
 import mage.constants.Zone;
 import mage.target.common.TargetOpponent;
 
+import java.util.UUID;
+
 /**
- *
  * @author TheElk801
  */
 public final class NihilisticGlee extends CardImpl {
@@ -34,7 +33,7 @@ public final class NihilisticGlee extends CardImpl {
                 new LoseLifeTargetEffect(1),
                 new ManaCostsImpl("{2}{B}")
         );
-        ability.addEffect(new GainLifeEffect(1).setText("and you gain 1 life"));
+        ability.addEffect(new GainLifeEffect(1).concatBy("and"));
         ability.addCost(new DiscardCardCost());
         ability.addTarget(new TargetOpponent());
         this.addAbility(ability);
diff --git a/Mage.Sets/src/mage/cards/o/ObzedatGhostCouncil.java b/Mage.Sets/src/mage/cards/o/ObzedatGhostCouncil.java
index 8600415009..f1700a55cf 100644
--- a/Mage.Sets/src/mage/cards/o/ObzedatGhostCouncil.java
+++ b/Mage.Sets/src/mage/cards/o/ObzedatGhostCouncil.java
@@ -42,6 +42,7 @@ public final class ObzedatGhostCouncil extends CardImpl {
         ability.addEffect(new GainLifeEffect(2).concatBy("and"));
         ability.addTarget(new TargetOpponent());
         this.addAbility(ability);
+
         //At the beginning of your end step you may exile Obzedat. If you do, return it to the battlefield under its owner's 
         //control at the beginning of your next upkeep. It gains haste.
         Ability ability2 = new BeginningOfYourEndStepTriggeredAbility(new ObzedatGhostCouncilExileSourceEffect(), true);
diff --git a/Mage.Sets/src/mage/cards/p/PryingQuestions.java b/Mage.Sets/src/mage/cards/p/PryingQuestions.java
index fe21705c4c..b1f314c267 100644
--- a/Mage.Sets/src/mage/cards/p/PryingQuestions.java
+++ b/Mage.Sets/src/mage/cards/p/PryingQuestions.java
@@ -1,7 +1,5 @@
-
 package mage.cards.p;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.LoseLifeTargetEffect;
@@ -16,20 +14,20 @@ import mage.players.Player;
 import mage.target.common.TargetCardInHand;
 import mage.target.common.TargetOpponent;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class PryingQuestions extends CardImpl {
 
     public PryingQuestions(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{B}");
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}");
 
         // Target opponent loses 3 life and puts a card from their hand on top of their library.
-        this.getSpellAbility().addTarget(new TargetOpponent());
         this.getSpellAbility().addEffect(new LoseLifeTargetEffect(3));
-        this.getSpellAbility().addEffect(new PryingQuestionsEffect());
-
+        this.getSpellAbility().addEffect(new PryingQuestionsEffect().concatBy("and"));
+        this.getSpellAbility().addTarget(new TargetOpponent());
     }
 
     public PryingQuestions(final PryingQuestions card) {
@@ -46,7 +44,7 @@ class PryingQuestionsEffect extends OneShotEffect {
 
     public PryingQuestionsEffect() {
         super(Outcome.Detriment);
-        this.staticText = "and puts a card from their hand on top of their library";
+        this.staticText = "puts a card from their hand on top of their library";
     }
 
     public PryingQuestionsEffect(final PryingQuestionsEffect effect) {
diff --git a/Mage.Sets/src/mage/cards/q/QarsiSadist.java b/Mage.Sets/src/mage/cards/q/QarsiSadist.java
index bc18b2e299..7e05641809 100644
--- a/Mage.Sets/src/mage/cards/q/QarsiSadist.java
+++ b/Mage.Sets/src/mage/cards/q/QarsiSadist.java
@@ -1,11 +1,8 @@
-
 package mage.cards.q;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.ExploitCreatureTriggeredAbility;
-import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.GainLifeEffect;
 import mage.abilities.effects.common.LoseLifeTargetEffect;
 import mage.abilities.keyword.ExploitAbility;
@@ -15,14 +12,15 @@ import mage.constants.CardType;
 import mage.constants.SubType;
 import mage.target.common.TargetOpponent;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class QarsiSadist extends CardImpl {
 
     public QarsiSadist(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}");
         this.subtype.add(SubType.HUMAN);
         this.subtype.add(SubType.CLERIC);
         this.power = new MageInt(1);
@@ -30,12 +28,11 @@ public final class QarsiSadist extends CardImpl {
 
         // Exploit
         this.addAbility(new ExploitAbility());
+
         // When Qarsi Sadist exploits a creature, target opponent loses 2 life and you gain 2 life.
         Ability ability = new ExploitCreatureTriggeredAbility(new LoseLifeTargetEffect(2), false);
+        ability.addEffect(new GainLifeEffect(2).concatBy("and"));
         ability.addTarget(new TargetOpponent());
-        Effect effect = new GainLifeEffect(2);
-        effect.setText("and you gain 2 life");
-        ability.addEffect(effect);
         this.addAbility(ability);
     }
 
diff --git a/Mage.Sets/src/mage/cards/s/SanguineBond.java b/Mage.Sets/src/mage/cards/s/SanguineBond.java
index 9a25f2c7ae..fa12709d1e 100644
--- a/Mage.Sets/src/mage/cards/s/SanguineBond.java
+++ b/Mage.Sets/src/mage/cards/s/SanguineBond.java
@@ -1,7 +1,5 @@
-
 package mage.cards.s;
 
-import java.util.UUID;
 import mage.abilities.TriggeredAbilityImpl;
 import mage.abilities.effects.common.LoseLifeTargetEffect;
 import mage.cards.CardImpl;
@@ -13,15 +11,15 @@ import mage.game.events.GameEvent;
 import mage.game.events.GameEvent.EventType;
 import mage.target.common.TargetOpponent;
 
+import java.util.UUID;
+
 /**
- *
  * @author North
  */
 public final class SanguineBond extends CardImpl {
 
     public SanguineBond(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{B}{B}");
-
+        super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{B}{B}");
 
         // Whenever you gain life, target opponent loses that much life.
         SanguineBondTriggeredAbility ability = new SanguineBondTriggeredAbility();
diff --git a/Mage.Sets/src/mage/cards/s/ShadowSlice.java b/Mage.Sets/src/mage/cards/s/ShadowSlice.java
index a3ea77c054..7a25b54a7a 100644
--- a/Mage.Sets/src/mage/cards/s/ShadowSlice.java
+++ b/Mage.Sets/src/mage/cards/s/ShadowSlice.java
@@ -1,7 +1,5 @@
-
 package mage.cards.s;
 
-import java.util.UUID;
 import mage.abilities.effects.common.CipherEffect;
 import mage.abilities.effects.common.LoseLifeTargetEffect;
 import mage.cards.CardImpl;
@@ -9,15 +7,15 @@ import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.target.common.TargetOpponent;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class ShadowSlice extends CardImpl {
 
-    public ShadowSlice (UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{4}{B}");
-
+    public ShadowSlice(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{B}");
 
         // Target Opponent loses 3 life.
         this.getSpellAbility().addEffect(new LoseLifeTargetEffect(3));
@@ -33,7 +31,7 @@ public final class ShadowSlice extends CardImpl {
     }
 
     @Override
-    public ShadowSlice  copy() {
+    public ShadowSlice copy() {
         return new ShadowSlice(this);
     }
 }
diff --git a/Mage.Sets/src/mage/cards/s/SkymarchBloodletter.java b/Mage.Sets/src/mage/cards/s/SkymarchBloodletter.java
index e7207e3f96..904f841ac9 100644
--- a/Mage.Sets/src/mage/cards/s/SkymarchBloodletter.java
+++ b/Mage.Sets/src/mage/cards/s/SkymarchBloodletter.java
@@ -1,7 +1,5 @@
-
 package mage.cards.s;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
@@ -15,8 +13,9 @@ import mage.constants.SubType;
 import mage.target.Target;
 import mage.target.common.TargetOpponent;
 
+import java.util.UUID;
+
 /**
- *
  * @author TheElk801
  */
 public final class SkymarchBloodletter extends CardImpl {
@@ -34,7 +33,7 @@ public final class SkymarchBloodletter extends CardImpl {
 
         // When Skymarch Bloodletters enters the battlefield, target opponent loses 1 life and you gain 1 life.
         Ability ability = new EntersBattlefieldTriggeredAbility(new LoseLifeTargetEffect(1), false);
-        ability.addEffect(new GainLifeEffect(1).setText("and you gain 1 life"));
+        ability.addEffect(new GainLifeEffect(1).concatBy("and"));
         Target target = new TargetOpponent();
         ability.addTarget(target);
         this.addAbility(ability);
diff --git a/Mage.Sets/src/mage/cards/v/VampireSovereign.java b/Mage.Sets/src/mage/cards/v/VampireSovereign.java
index 66b5549d08..1eb0bd6b42 100644
--- a/Mage.Sets/src/mage/cards/v/VampireSovereign.java
+++ b/Mage.Sets/src/mage/cards/v/VampireSovereign.java
@@ -31,7 +31,7 @@ public final class VampireSovereign extends CardImpl {
 
         // When Vampire Sovereign enters the battlefield, target opponent loses 3 life and you gain 3 life.
         Ability ability = new EntersBattlefieldTriggeredAbility(new LoseLifeTargetEffect(3));
-        ability.addEffect(new GainLifeEffect(3).setText("and you gain 3 life"));
+        ability.addEffect(new GainLifeEffect(3).concatBy("and"));
         ability.addTarget(new TargetOpponent());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/w/WaywardDisciple.java b/Mage.Sets/src/mage/cards/w/WaywardDisciple.java
index 04e8904255..a0d2ca3990 100644
--- a/Mage.Sets/src/mage/cards/w/WaywardDisciple.java
+++ b/Mage.Sets/src/mage/cards/w/WaywardDisciple.java
@@ -1,11 +1,8 @@
-
 package mage.cards.w;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.DiesThisOrAnotherCreatureTriggeredAbility;
-import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.GainLifeEffect;
 import mage.abilities.effects.common.LoseLifeTargetEffect;
 import mage.cards.CardImpl;
@@ -16,8 +13,9 @@ import mage.constants.TargetController;
 import mage.filter.common.FilterCreaturePermanent;
 import mage.target.common.TargetOpponent;
 
+import java.util.UUID;
+
 /**
- *
  * @author fireshoes
  */
 public final class WaywardDisciple extends CardImpl {
@@ -29,7 +27,7 @@ public final class WaywardDisciple extends CardImpl {
     }
 
     public WaywardDisciple(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "");
         this.subtype.add(SubType.HUMAN);
         this.subtype.add(SubType.CLERIC);
         this.power = new MageInt(2);
@@ -41,10 +39,8 @@ public final class WaywardDisciple extends CardImpl {
 
         // Whenever Wayward Disciple or another creature you control dies, target opponent loses 1 life and you gain 1 life.
         Ability ability = new DiesThisOrAnotherCreatureTriggeredAbility(new LoseLifeTargetEffect(1), false, filter);
+        ability.addEffect(new GainLifeEffect(1).concatBy("and"));
         ability.addTarget(new TargetOpponent());
-        Effect effect = new GainLifeEffect(1);
-        effect.setText("and you gain 1 life");
-        ability.addEffect(effect);
         this.addAbility(ability);
     }
 
diff --git a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java
index f352a25b8b..90be9b1bb7 100644
--- a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java
+++ b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java
@@ -985,13 +985,18 @@ public class VerifyCardDataTest {
             System.out.println();
             System.out.println(card.getName() + " " + card.getManaCost().getText());
             if (card instanceof SplitCard) {
-                card.getAbilities().getRules(card.getName()).forEach(System.out::println);
+                card.getAbilities().getRules(card.getName()).forEach(this::printAbilityText);
             } else {
-                card.getRules().forEach(System.out::println);
+                card.getRules().forEach(this::printAbilityText);
             }
         });
     }
 
+    private void printAbilityText(String text) {
+        text = text.replace("<br>", "\n");
+        System.out.println(text);
+    }
+
     private void checkWrongAbilitiesText(Card card, JsonCard ref, int cardIndex) {
         // checks missing or wrong text
         if (!card.getExpansionSetCode().equals(FULL_ABILITIES_CHECK_SET_CODE)) {

From be08aedc5d7f60c901ea950134ed303b7598d6d6 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Sat, 20 Jun 2020 03:05:43 +0400
Subject: [PATCH 422/586] Added hints for some cards

---
 .../mage/cards/t/TezzeretMasterOfMetal.java   | 22 ++++++++-----------
 .../src/mage/cards/t/TezzeretsSimulacrum.java | 21 ++++++++++++------
 .../src/mage/cards/v/VraskasConquistador.java | 13 ++++++-----
 3 files changed, 31 insertions(+), 25 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/t/TezzeretMasterOfMetal.java b/Mage.Sets/src/mage/cards/t/TezzeretMasterOfMetal.java
index 368382bb53..f26c2ce66c 100644
--- a/Mage.Sets/src/mage/cards/t/TezzeretMasterOfMetal.java
+++ b/Mage.Sets/src/mage/cards/t/TezzeretMasterOfMetal.java
@@ -1,27 +1,19 @@
-
 package mage.cards.t;
 
-import java.util.List;
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.LoyaltyAbility;
 import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
+import mage.abilities.dynamicvalue.DynamicValue;
 import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
 import mage.abilities.effects.ContinuousEffect;
 import mage.abilities.effects.ContinuousEffectImpl;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.LoseLifeTargetEffect;
 import mage.abilities.effects.common.RevealCardsFromLibraryUntilEffect;
+import mage.abilities.hint.ValueHint;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.CardType;
-import mage.constants.SubType;
-import mage.constants.Duration;
-import mage.constants.Layer;
-import mage.constants.Outcome;
-import mage.constants.SubLayer;
-import mage.constants.SuperType;
-import mage.constants.Zone;
+import mage.constants.*;
 import mage.filter.FilterPermanent;
 import mage.filter.common.FilterArtifactCard;
 import mage.filter.common.FilterControlledArtifactPermanent;
@@ -31,8 +23,10 @@ import mage.game.permanent.Permanent;
 import mage.target.common.TargetOpponent;
 import mage.target.targetpointer.FixedTarget;
 
+import java.util.List;
+import java.util.UUID;
+
 /**
- *
  * @author Styxo
  */
 public final class TezzeretMasterOfMetal extends CardImpl {
@@ -48,8 +42,10 @@ public final class TezzeretMasterOfMetal extends CardImpl {
         this.addAbility(new LoyaltyAbility(new RevealCardsFromLibraryUntilEffect(new FilterArtifactCard(), Zone.HAND, Zone.LIBRARY), 1));
 
         // -3: Target opponent loses life equal to the number of artifacts you control.
-        Ability ability = new LoyaltyAbility(new LoseLifeTargetEffect(new PermanentsOnBattlefieldCount(new FilterControlledArtifactPermanent())), -3);
+        DynamicValue xValue = new PermanentsOnBattlefieldCount(new FilterControlledArtifactPermanent());
+        Ability ability = new LoyaltyAbility(new LoseLifeTargetEffect(xValue), -3);
         ability.addTarget(new TargetOpponent());
+        ability.addHint(new ValueHint("Artifacts you control", xValue));
         this.addAbility(ability);
 
         // -8: Gain control of all artifacts and creatures target opponent controls.
diff --git a/Mage.Sets/src/mage/cards/t/TezzeretsSimulacrum.java b/Mage.Sets/src/mage/cards/t/TezzeretsSimulacrum.java
index 0749fb4e45..3c9e33e2fc 100644
--- a/Mage.Sets/src/mage/cards/t/TezzeretsSimulacrum.java
+++ b/Mage.Sets/src/mage/cards/t/TezzeretsSimulacrum.java
@@ -1,28 +1,35 @@
-
 package mage.cards.t;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.condition.Condition;
 import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
 import mage.abilities.costs.common.TapSourceCost;
 import mage.abilities.decorator.ConditionalOneShotEffect;
 import mage.abilities.effects.common.LoseLifeTargetEffect;
+import mage.abilities.hint.ConditionHint;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.SubType;
 import mage.constants.Zone;
-import mage.filter.common.FilterPlaneswalkerPermanent;
+import mage.filter.common.FilterControlledPlaneswalkerPermanent;
 import mage.target.common.TargetOpponent;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class TezzeretsSimulacrum extends CardImpl {
 
+    private static final FilterControlledPlaneswalkerPermanent filter = new FilterControlledPlaneswalkerPermanent();
+
+    static {
+        filter.add(SubType.TEZZERET.getPredicate());
+    }
+
     public TezzeretsSimulacrum(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}");
 
@@ -31,13 +38,13 @@ public final class TezzeretsSimulacrum extends CardImpl {
         this.toughness = new MageInt(3);
 
         // {T}: Target opponent loses 1 life. If you control a Tezzeret planeswalker, that player loses 3 life instead.
-        FilterPlaneswalkerPermanent filter = new FilterPlaneswalkerPermanent();
-        filter.add(SubType.TEZZERET.getPredicate());
+        Condition condition = new PermanentsOnTheBattlefieldCondition(filter);
         Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD,
                 new ConditionalOneShotEffect(new LoseLifeTargetEffect(3), new LoseLifeTargetEffect(1),
-                        new PermanentsOnTheBattlefieldCondition(filter),
+                        condition,
                         "Target opponent loses 1 life. If you control a Tezzeret planeswalker, that player loses 3 life instead"), new TapSourceCost());
         ability.addTarget(new TargetOpponent());
+        ability.addHint(new ConditionHint(condition, "You control a Tezzeret planeswalker"));
         this.addAbility(ability);
     }
 
diff --git a/Mage.Sets/src/mage/cards/v/VraskasConquistador.java b/Mage.Sets/src/mage/cards/v/VraskasConquistador.java
index 43ec42ffa0..f7caaae0c3 100644
--- a/Mage.Sets/src/mage/cards/v/VraskasConquistador.java
+++ b/Mage.Sets/src/mage/cards/v/VraskasConquistador.java
@@ -1,14 +1,14 @@
-
 package mage.cards.v;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.TriggeredAbility;
 import mage.abilities.common.AttacksOrBlocksTriggeredAbility;
+import mage.abilities.condition.Condition;
 import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
 import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
 import mage.abilities.effects.common.GainLifeEffect;
 import mage.abilities.effects.common.LoseLifeTargetEffect;
+import mage.abilities.hint.ConditionHint;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
@@ -16,8 +16,9 @@ import mage.constants.SubType;
 import mage.filter.common.FilterControlledPermanent;
 import mage.target.common.TargetOpponent;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class VraskasConquistador extends CardImpl {
@@ -38,11 +39,13 @@ public final class VraskasConquistador extends CardImpl {
         this.toughness = new MageInt(1);
 
         // Whenever Vraska's Conquistador attacks or blocks, if you control a Vraska planeswalker, target opponent loses 2 life and you gain 2 life.
+        Condition condition = new PermanentsOnTheBattlefieldCondition(filter);
         TriggeredAbility ability = new AttacksOrBlocksTriggeredAbility(new LoseLifeTargetEffect(2), false);
-        ability.addEffect(new GainLifeEffect(2).setText("and you gain 2 life"));
+        ability.addEffect(new GainLifeEffect(2).concatBy("and"));
         ability.addTarget(new TargetOpponent());
+        ability.addHint(new ConditionHint(condition, "You control a Vraska planeswalker"));
         this.addAbility(new ConditionalInterveningIfTriggeredAbility(
-                ability, new PermanentsOnTheBattlefieldCondition(filter),
+                ability, condition,
                 "Whenever {this} attacks or blocks, if you control a Vraska planeswalker, target opponent loses 2 life and you gain 2 life."));
     }
 

From 271b8d06f4f92c59af928c528654a45abace3525 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Sat, 20 Jun 2020 03:11:58 +0400
Subject: [PATCH 423/586] Reverted wrong changes and missing card hint

---
 .../src/mage/cards/s/ShamanOfThePack.java     |  9 +++--
 .../src/mage/cards/s/SpellweaverHelix.java    | 40 ++++++++++++++++++-
 2 files changed, 43 insertions(+), 6 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/s/ShamanOfThePack.java b/Mage.Sets/src/mage/cards/s/ShamanOfThePack.java
index 167160264f..929b0d6e90 100644
--- a/Mage.Sets/src/mage/cards/s/ShamanOfThePack.java
+++ b/Mage.Sets/src/mage/cards/s/ShamanOfThePack.java
@@ -1,13 +1,12 @@
-
 package mage.cards.s;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.LoseLifeTargetEffect;
+import mage.abilities.hint.ValueHint;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
@@ -16,8 +15,9 @@ import mage.constants.TargetController;
 import mage.filter.FilterPermanent;
 import mage.target.common.TargetOpponent;
 
+import java.util.UUID;
+
 /**
- *
  * @author fireshoes
  */
 public final class ShamanOfThePack extends CardImpl {
@@ -30,7 +30,7 @@ public final class ShamanOfThePack extends CardImpl {
     }
 
     public ShamanOfThePack(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}{G}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{G}");
         this.subtype.add(SubType.ELF);
         this.subtype.add(SubType.SHAMAN);
         this.power = new MageInt(3);
@@ -41,6 +41,7 @@ public final class ShamanOfThePack extends CardImpl {
         effect.setText("target opponent loses life equal to the number of Elves you control");
         Ability ability = new EntersBattlefieldTriggeredAbility(effect, false);
         ability.addTarget(new TargetOpponent());
+        ability.addHint(new ValueHint("Elves you control", new PermanentsOnBattlefieldCount(filter)));
         this.addAbility(ability);
     }
 
diff --git a/Mage.Sets/src/mage/cards/s/SpellweaverHelix.java b/Mage.Sets/src/mage/cards/s/SpellweaverHelix.java
index cc3f0bbd38..5eeed09130 100644
--- a/Mage.Sets/src/mage/cards/s/SpellweaverHelix.java
+++ b/Mage.Sets/src/mage/cards/s/SpellweaverHelix.java
@@ -6,7 +6,6 @@ import mage.abilities.TriggeredAbilityImpl;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.OneShotEffect;
-import mage.abilities.effects.common.ExileTargetForSourceEffect;
 import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -21,6 +20,7 @@ import mage.game.stack.Spell;
 import mage.players.Player;
 import mage.target.common.TargetCardInASingleGraveyard;
 import mage.target.targetpointer.FixedTarget;
+import mage.util.CardUtil;
 
 import java.util.UUID;
 
@@ -39,7 +39,7 @@ public final class SpellweaverHelix extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}");
 
         // Imprint - When Spellweaver Helix enters the battlefield, you may exile two target sorcery cards from a single graveyard.
-        Ability ability = new EntersBattlefieldTriggeredAbility(new ExileTargetForSourceEffect(), true, "Imprint &mdash; ");
+        Ability ability = new EntersBattlefieldTriggeredAbility(new SpellweaverHelixImprintEffect(), true, "Imprint &mdash; ");
         ability.addTarget(new TargetCardInASingleGraveyard(2, 2, filter));
         this.addAbility(ability);
 
@@ -57,6 +57,42 @@ public final class SpellweaverHelix extends CardImpl {
     }
 }
 
+class SpellweaverHelixImprintEffect extends OneShotEffect {
+
+    SpellweaverHelixImprintEffect() {
+        super(Outcome.Exile);
+        this.staticText = "you may exile two target sorcery cards from a single graveyard";
+    }
+
+    SpellweaverHelixImprintEffect(final SpellweaverHelixImprintEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public SpellweaverHelixImprintEffect copy() {
+        return new SpellweaverHelixImprintEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Player controller = game.getPlayer(source.getControllerId());
+        if (controller != null) {
+            Permanent sourcePermanent = game.getPermanent(source.getSourceId());
+            for (UUID targetId : this.getTargetPointer().getTargets(game, source)) {
+                Card card = game.getCard(targetId);
+                if (card != null) {
+                    controller.moveCardsToExile(card, source, game, true, CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()), source.getSourceObject(game).getIdName());
+                    if (sourcePermanent != null) {
+                        sourcePermanent.imprint(targetId, game);
+                    }
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+}
+
 class SpellweaverHelixTriggeredAbility extends TriggeredAbilityImpl {
 
     SpellweaverHelixTriggeredAbility() {

From 16f7e401f77e8ae3a40dce1bfde0ceebf6ef5c17 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Fri, 19 Jun 2020 19:13:06 -0400
Subject: [PATCH 424/586] fixed some test failures

---
 Mage.Sets/src/mage/cards/t/TomeAnima.java | 2 +-
 Mage.Sets/src/mage/cards/t/TrackDown.java | 6 +++---
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/t/TomeAnima.java b/Mage.Sets/src/mage/cards/t/TomeAnima.java
index 8b4351c2a5..28a29266ea 100644
--- a/Mage.Sets/src/mage/cards/t/TomeAnima.java
+++ b/Mage.Sets/src/mage/cards/t/TomeAnima.java
@@ -33,7 +33,7 @@ public final class TomeAnima extends CardImpl {
         this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect(
                 new GainAbilitySourceEffect(new CantBeBlockedSourceAbility(), Duration.WhileOnBattlefield),
                 TomeAnimaCondition.instance,
-                "{this} can’t be blocked as long as you’ve drawn two or more cards this turn"
+                "{this} can't be blocked as long as you've drawn two or more cards this turn"
         )), new CardsDrawnThisTurnWatcher());
     }
 
diff --git a/Mage.Sets/src/mage/cards/t/TrackDown.java b/Mage.Sets/src/mage/cards/t/TrackDown.java
index 986845a13d..468de32576 100644
--- a/Mage.Sets/src/mage/cards/t/TrackDown.java
+++ b/Mage.Sets/src/mage/cards/t/TrackDown.java
@@ -1,7 +1,5 @@
 package mage.cards.t;
 
-import java.util.UUID;
-
 import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
@@ -15,6 +13,8 @@ import mage.constants.Outcome;
 import mage.game.Game;
 import mage.players.Player;
 
+import java.util.UUID;
+
 /**
  * @author arcox
  */
@@ -42,7 +42,7 @@ class TrackDownEffect extends OneShotEffect {
 
     public TrackDownEffect() {
         super(Outcome.DrawCard);
-        this.staticText = "then reveal the top card of your library. If it’s a creature or land card, draw a card";
+        this.staticText = "then reveal the top card of your library. If it's a creature or land card, draw a card";
     }
 
     public TrackDownEffect(final TrackDownEffect effect) {

From 52579fd1f322282e5ade10c0b49d7830d264a0b6 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Sat, 20 Jun 2020 07:06:34 +0400
Subject: [PATCH 425/586] Fixed tests

---
 .../abilities/effects/mana/AddManaChosenColorEffect.java   | 1 +
 .../main/java/mage/game/permanent/token/WeirdToken2.java   | 7 ++++---
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/Mage/src/main/java/mage/abilities/effects/mana/AddManaChosenColorEffect.java b/Mage/src/main/java/mage/abilities/effects/mana/AddManaChosenColorEffect.java
index 554d8463df..1b856a1d7f 100644
--- a/Mage/src/main/java/mage/abilities/effects/mana/AddManaChosenColorEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/mana/AddManaChosenColorEffect.java
@@ -44,6 +44,7 @@ public class AddManaChosenColorEffect extends ManaEffect {
 
     @Override
     public String getText(Mode mode) {
+        // buggy, but no other way, see comments https://github.com/magefree/mage/commit/cd8d12365f8119dcfe19176a7142e77f80f423b
         return super.getText(mode) + (chosenColorInfo == null ? "" : " {" + chosenColorInfo.toString() + "}");
     }
 }
diff --git a/Mage/src/main/java/mage/game/permanent/token/WeirdToken2.java b/Mage/src/main/java/mage/game/permanent/token/WeirdToken2.java
index 837479df7f..31daad3b06 100644
--- a/Mage/src/main/java/mage/game/permanent/token/WeirdToken2.java
+++ b/Mage/src/main/java/mage/game/permanent/token/WeirdToken2.java
@@ -1,17 +1,18 @@
 package mage.game.permanent.token;
 
 import mage.MageInt;
-import mage.abilities.keyword.DefenderAbility;
-import mage.abilities.keyword.FlyingAbility;
 import mage.constants.CardType;
 import mage.constants.SubType;
 
 /**
- *
  * @author TheElk801
  */
 public final class WeirdToken2 extends TokenImpl {
 
+    public WeirdToken2() {
+        this(0);
+    }
+
     public WeirdToken2(int xValue) {
         super("Weird", "X/X blue and red Weird creature token");
         cardType.add(CardType.CREATURE);

From c54c100b4dbd9d401c7ac8f00e614c299ff4e12a Mon Sep 17 00:00:00 2001
From: John Hitchings <hitch17@gmail.com>
Date: Fri, 19 Jun 2020 22:10:09 -0700
Subject: [PATCH 426/586] add docs/links for jumpstart, tweak the jumpstart
 packs file.

---
 Mage.Server/release/config/config.xml         |  2 +
 .../jumpstart/JumpstartPoolGenerator.java     | 40 +++++++++++-
 .../mage/game/tournament/TournamentImpl.java  |  2 +-
 .../main/resources/jumpstart/jumpstart.txt    | 63 ++++++-------------
 4 files changed, 58 insertions(+), 49 deletions(-)

diff --git a/Mage.Server/release/config/config.xml b/Mage.Server/release/config/config.xml
index aac80217ba..3a9810f386 100644
--- a/Mage.Server/release/config/config.xml
+++ b/Mage.Server/release/config/config.xml
@@ -98,6 +98,8 @@
         <tournamentType name="Sealed Elimination (Cube)" jar="mage-tournament-sealed-${project.version}.jar" className="mage.tournament.SealedEliminationTournament" typeName="mage.tournament.SealedEliminationCubeTournamentType"/>
         <tournamentType name="Sealed Swiss" jar="mage-tournament-sealed-${project.version}.jar" className="mage.tournament.SealedSwissTournament" typeName="mage.tournament.SealedSwissTournamentType"/>
         <tournamentType name="Sealed Swiss (Cube)" jar="mage-tournament-sealed-${project.version}.jar" className="mage.tournament.SealedSwissTournament" typeName="mage.tournament.SealedSwissCubeTournamentType"/>
+        <tournamentType name="Jumpstart Elimination" jar="mage-tournament-sealed.jar" className="mage.tournament.JumpstartEliminationTournament" typeName="mage.tournament.JumpstartEliminationTournamentType"/>
+        <tournamentType name="Jumpstart Swiss" jar="mage-tournament-sealed.jar" className="mage.tournament.JumpstartSwissTournament" typeName="mage.tournament.JumpstartSwissTournamentType"/>
     </tournamentTypes>
     <draftCubes>
         <draftCube name="Adam Styborski's Pauper Cube" jar="mage-tournament-booster-draft-${project.version}.jar" className="mage.tournament.cubes.AdamStyborskisPauperCube"/>
diff --git a/Mage/src/main/java/mage/game/jumpstart/JumpstartPoolGenerator.java b/Mage/src/main/java/mage/game/jumpstart/JumpstartPoolGenerator.java
index b9425d2c3a..9039e28e07 100644
--- a/Mage/src/main/java/mage/game/jumpstart/JumpstartPoolGenerator.java
+++ b/Mage/src/main/java/mage/game/jumpstart/JumpstartPoolGenerator.java
@@ -20,6 +20,36 @@ import mage.game.GameException;
 
 public class JumpstartPoolGenerator {
 
+  /**
+   * "Jumpstart is a new way to play Magic that mashes together themes from throughout the history of the game and
+   * lets you skip the deckbuilding part. Grab two boosters, shuffle them into a 40-card deck, and start playing.
+   * The set contains almost 500 reprints but also introduces 37 new cards that were designed to help fill out some
+   * of the themes. These are not going to be Standard-, Pioneer-, or Modern-legal cards, but are legal in Eternal
+   * formats (Legacy, Vintage, Pauper and Commander). There are no foil cards in the set. 120 cards are from Core
+   * Set 2021[7], and more than 400 reprints are from sets before Core Set 2021. Cards from M21 have the M21 expansion
+   * symbol, while new cards and other reprints will have the Jumpstart symbol. Reprints have a Post-M15 card frame.
+   * The new cards and cards with new art are numbered #1/78 to #78/78 (40 basic lands, TBA common, TBA uncommon,
+   * TBA rare, and TBA mythic rare). The other reprints with the Jumpstart expansion symbol are numbered #79 to 495."
+   *
+   * "Jumpstart is sold in 20-card boosters, and booster boxes contain 24 boosters.[9] All 20 cards in a booster fit a
+   * theme, and most themes are mono-color and have multiple variations of cards included in the pack, totaling 121
+   * different possible pack contents. Special "Mythic Rare" packs don't have variations at all, but just one possible
+   * card list and can feature multiple colors. What theme each booster contains is randomized. Most packs are
+   * singletons, but there are some instances of having two copies of a card in the pack. All boosters contain at
+   * least one rare, and one in three boosters includes an extra rare. Of the 20 cards in a pack, seven or eight
+   * are lands. One basic land (or in the case of Rainbow, Terramorphic Expanse) features art that matches the pack's
+   * theme. The planeswalker themed packs use the respective Showcase lands from M21, but the other packs use brand-new
+   * themed land art created for the set. The booster pack is wrapped like a regular pack, but the set of cards is
+   * packed in an additional plastic wrap, with a face card (the "Pack Summary card") that indicates the theme and
+   * the color of the half-deck."
+   *
+   * Source:  https://mtg.gamepedia.com/Jumpstart
+   *
+   * Announcement: https://magic.wizards.com/en/articles/archive/news/introducing-jumpstart-new-way-play-magic-2020-02-20
+   * Card Pool: https://magic.wizards.com/en/articles/archive/card-image-gallery/jumpstart
+   * Deck Lists: https://magic.wizards.com/en/articles/archive/feature/jumpstart-decklists-2020-06-18
+   */
+
   private static final String RESOURCE_NAME = "jumpstart/jumpstart.txt";
   private static final List<JumpstartPack> JUMPSTART_PACKS;
 
@@ -28,17 +58,21 @@ public class JumpstartPoolGenerator {
       CharSource source = Resources.asCharSource(Resources.getResource(RESOURCE_NAME), Charsets.UTF_8);
       List<JumpstartPack> packs = new ArrayList<>();
       JumpstartPack pack = new JumpstartPack();
+      packs.add(pack);
       for (String line : source.readLines()) {
         if (line.isEmpty()) {
           if (!pack.isEmpty()) {
-            packs.add(pack);
             pack = new JumpstartPack();
+            packs.add(pack);
           }
         } else if (line.startsWith("#")) {
           // skip comment
         } else {
-          String[] ls = line.split(" ", 3);
-          pack.add(new DeckCardInfo(ls[2], ls[1], ls[0]));
+          String[] ls = line.split(" ", 4);
+          int quantity = Integer.parseInt(ls[0]);
+          for (int i = 0; i < quantity; i++) {
+            pack.add(new DeckCardInfo(ls[3], ls[2], ls[1]));
+          }
         }
       }
       JUMPSTART_PACKS = Collections.unmodifiableList(packs);
diff --git a/Mage/src/main/java/mage/game/tournament/TournamentImpl.java b/Mage/src/main/java/mage/game/tournament/TournamentImpl.java
index cd3729b844..001cf9dc6e 100644
--- a/Mage/src/main/java/mage/game/tournament/TournamentImpl.java
+++ b/Mage/src/main/java/mage/game/tournament/TournamentImpl.java
@@ -411,7 +411,7 @@ public abstract class TournamentImpl implements Tournament {
                     player.getDeck().getSideboard().addAll(cube.createBooster());
                 }
             } else if (options.getLimitedOptions().getIsJumpstart()) {
-                player.getDeck().getSideboard().addAll(JumpstartPoolGenerator.generatePool());
+                player.getDeck().getCards().addAll(JumpstartPoolGenerator.generatePool());
             } else {
                 for (ExpansionSet set : sets) {
                     player.getDeck().getSideboard().addAll(set.createBooster());
diff --git a/Mage/src/main/resources/jumpstart/jumpstart.txt b/Mage/src/main/resources/jumpstart/jumpstart.txt
index f030253772..e9e6ddb0d6 100644
--- a/Mage/src/main/resources/jumpstart/jumpstart.txt
+++ b/Mage/src/main/resources/jumpstart/jumpstart.txt
@@ -1,50 +1,23 @@
-# setCode cardNum cardName
+# Jumpstart Decklists
+# https://magic.wizards.com/en/articles/archive/feature/jumpstart-decklists-2020-06-18
+# quantity setCode cardNum cardName
+
 # Red Cards
-ELD 137 Rimrock Knight
-ELD 137 Rimrock Knight
-ELD 137 Rimrock Knight
-ELD 137 Rimrock Knight
-ELD 291 Bonecrusher Giant
-ELD 291 Bonecrusher Giant
-ELD 291 Bonecrusher Giant
-ELD 291 Bonecrusher Giant
-ELD 120 Embercleave
-ELD 120 Embercleave
-ELD 120 Embercleave
-ELD 120 Embercleave
+3 ELD 137 Rimrock Knight
+4 ELD 115 Bonecrusher Giant
+2 ELD 120 Embercleave
+8 ELD 262 Mountain
 
 # Green Cards
-GRN 141 Pelt Collector
-GRN 141 Pelt Collector
-GRN 141 Pelt Collector
-GRN 141 Pelt Collector
-WAR 171 Paradise Druid
-WAR 171 Paradise Druid
-WAR 171 Paradise Druid
-WAR 171 Paradise Druid
-ELD 165 Lovestruck Beast
-ELD 165 Lovestruck Beast
-ELD 165 Lovestruck Beast
-ELD 165 Lovestruck Beast
-ELD 171 Questing Beast
-ELD 171 Questing Beast
-ELD 171 Questing Beast
-ELD 171 Questing Beast
+3 GRN 141 Pelt Collector
+3 WAR 171 Paradise Druid
+3 ELD 165 Lovestruck Beast
+3 ELD 171 Questing Beast
+8 ELD 266 Forest
 
 # Black Cards
-THB 123 Woe Strider
-THB 123 Woe Strider
-THB 123 Woe Strider
-THB 123 Woe Strider
-ELD 81 Cauldron Familiar
-ELD 81 Cauldron Familiar
-ELD 81 Cauldron Familiar
-ELD 81 Cauldron Familiar
-ELD 237 Witch's Oven
-ELD 237 Witch's Oven
-ELD 237 Witch's Oven
-ELD 237 Witch's Oven
-IKO 220 Fiend Artisan
-IKO 220 Fiend Artisan
-IKO 220 Fiend Artisan
-IKO 220 Fiend Artisan
\ No newline at end of file
+2 THB 123 Woe Strider
+4 ELD 81 Cauldron Familiar
+3 ELD 237 Witch's Oven
+3 IKO 220 Fiend Artisan
+8 ELD 258 Swamp

From 305dab90b5f66e96187715af0321dc1d5b56cbde Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Sat, 20 Jun 2020 08:58:29 +0200
Subject: [PATCH 427/586] * Replaced some card.putOntoBattlefield by
 player.moveCard... methods (#4866). Added new player.shuffleCardsToLibrary
 method.

---
 Mage.Sets/src/mage/cards/a/Aethertow.java     |   6 +-
 .../src/mage/cards/a/AmassTheComponents.java  |   3 +-
 .../src/mage/cards/b/BrutalizerExarch.java    |  10 +-
 .../src/mage/cards/c/ConsignToDream.java      |  11 +-
 Mage.Sets/src/mage/cards/d/DarkRevenant.java  |   7 +-
 Mage.Sets/src/mage/cards/d/DeadReckoning.java |  13 +--
 .../src/mage/cards/d/DiminishingReturns.java  |  31 ++----
 Mage.Sets/src/mage/cards/d/Doomsday.java      |  43 ++------
 .../src/mage/cards/d/DwellOnThePast.java      |  21 +---
 Mage.Sets/src/mage/cards/e/EpitaphGolem.java  |  11 +-
 Mage.Sets/src/mage/cards/g/GaeasBlessing.java |  31 +-----
 .../src/mage/cards/g/GravebaneZombie.java     |  11 +-
 .../src/mage/cards/h/HarmonicConvergence.java |  50 +++------
 .../src/mage/cards/k/KrosanReclamation.java   |  22 +---
 Mage.Sets/src/mage/cards/l/LichsMirror.java   |  40 +++----
 .../src/mage/cards/m/MemorysJourney.java      |  21 +---
 .../src/mage/cards/m/MirrorMadPhantasm.java   |   5 +-
 Mage.Sets/src/mage/cards/m/MirrorOfFate.java  |  40 ++-----
 .../java/org/mage/test/player/TestPlayer.java |  15 +++
 .../java/org/mage/test/stub/PlayerStub.java   |  15 +++
 Mage/src/main/java/mage/players/Player.java   |  15 ++-
 .../main/java/mage/players/PlayerImpl.java    | 100 +++++++++++-------
 .../main/java/mage/players/StubPlayer.java    |   2 +
 23 files changed, 217 insertions(+), 306 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/a/Aethertow.java b/Mage.Sets/src/mage/cards/a/Aethertow.java
index d2c060a9bb..4922cb7142 100644
--- a/Mage.Sets/src/mage/cards/a/Aethertow.java
+++ b/Mage.Sets/src/mage/cards/a/Aethertow.java
@@ -7,12 +7,14 @@ import mage.abilities.effects.OneShotEffect;
 import mage.abilities.keyword.ConspireAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
+import mage.cards.CardsImpl;
 import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.constants.Zone;
 import mage.filter.common.FilterAttackingOrBlockingCreature;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
+import mage.players.Player;
 import mage.target.common.TargetCreaturePermanent;
 
 /**
@@ -58,9 +60,9 @@ class AethertowEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Permanent targetCreature = game.getPermanent(targetPointer.getFirst(game, source));
+        Player controller = game.getPlayer(source.getControllerId());
         if (targetCreature != null) {
-            targetCreature.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true);
-            return true;
+            return controller.putCardsOnTopOfLibrary(targetCreature, game, source, true);
         }
         return false;
     }
diff --git a/Mage.Sets/src/mage/cards/a/AmassTheComponents.java b/Mage.Sets/src/mage/cards/a/AmassTheComponents.java
index 5d7c24dbad..c3cdc37ece 100644
--- a/Mage.Sets/src/mage/cards/a/AmassTheComponents.java
+++ b/Mage.Sets/src/mage/cards/a/AmassTheComponents.java
@@ -71,8 +71,7 @@ class AmassTheComponentsEffect extends OneShotEffect {
             if (player.choose(Outcome.Detriment, player.getHand(), target, game)) {
                 Card card = player.getHand().get(target.getFirstTarget(), game);
                 if (card != null) {
-                    player.removeFromHand(card, game);
-                    card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, false);
+                    return player.putCardsOnBottomOfLibrary(card, game, source, true);
                 }
             }
         }
diff --git a/Mage.Sets/src/mage/cards/b/BrutalizerExarch.java b/Mage.Sets/src/mage/cards/b/BrutalizerExarch.java
index de15f1581d..308ddc69c1 100644
--- a/Mage.Sets/src/mage/cards/b/BrutalizerExarch.java
+++ b/Mage.Sets/src/mage/cards/b/BrutalizerExarch.java
@@ -83,13 +83,9 @@ class BrutalizerExarchEffect2 extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Permanent permanent = game.getPermanent(source.getFirstTarget());
-        if (permanent != null) {
-            Player player = game.getPlayer(permanent.getOwnerId());
-            if (player == null) {
-                return false;
-            }
-
-            return permanent.moveToZone(Zone.LIBRARY, source.getSourceId(), game, false);
+        Player controller = game.getPlayer(source.getControllerId());
+        if (permanent != null && controller != null) {
+            return controller.putCardsOnBottomOfLibrary(permanent, game, source, true);
         }
         return false;
     }
diff --git a/Mage.Sets/src/mage/cards/c/ConsignToDream.java b/Mage.Sets/src/mage/cards/c/ConsignToDream.java
index 451c0819ea..32946ea4eb 100644
--- a/Mage.Sets/src/mage/cards/c/ConsignToDream.java
+++ b/Mage.Sets/src/mage/cards/c/ConsignToDream.java
@@ -11,6 +11,7 @@ import mage.constants.Outcome;
 import mage.constants.Zone;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
+import mage.players.Player;
 import mage.target.TargetPermanent;
 
 /**
@@ -58,15 +59,15 @@ class ConsignToDreamEffect extends OneShotEffect {
 
     @Override
     public boolean apply(Game game, Ability source) {
-        boolean applied = false;
         Permanent target = game.getPermanent(source.getFirstTarget());
-        if (target != null) {
+        Player controller = game.getPlayer(source.getControllerId());
+        if (target != null && controller != null) {
             if (target.getColor(game).isRed() || target.getColor(game).isGreen()) {
-                applied = target.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true);
+                return controller.putCardsOnTopOfLibrary(target, game, source, true);
             } else {
-                applied = target.moveToZone(Zone.HAND, source.getSourceId(), game, false);
+                return controller.moveCards(target, Zone.HAND, source, game);
             }
         }
-        return applied;
+        return false;
     }
 }
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/d/DarkRevenant.java b/Mage.Sets/src/mage/cards/d/DarkRevenant.java
index 8f42634f7e..8e404392ec 100644
--- a/Mage.Sets/src/mage/cards/d/DarkRevenant.java
+++ b/Mage.Sets/src/mage/cards/d/DarkRevenant.java
@@ -10,6 +10,7 @@ import mage.abilities.keyword.FlyingAbility;
 import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
+import mage.cards.CardsImpl;
 import mage.constants.CardType;
 import mage.constants.SubType;
 import mage.constants.Outcome;
@@ -67,9 +68,9 @@ class DarkRevenantEffect extends OneShotEffect {
     public boolean apply(Game game, Ability source) {
         Card card = game.getCard(source.getSourceId());
         if (card != null && game.getState().getZone(source.getSourceId()) == Zone.GRAVEYARD) {
-            Player owner = game.getPlayer(card.getOwnerId());
-            if(owner != null) {
-                return card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true);
+            Player controller = game.getPlayer(source.getControllerId());
+            if(controller != null) {
+                return controller.putCardsOnTopOfLibrary(card, game, source, true);
             }
         }
         return true;
diff --git a/Mage.Sets/src/mage/cards/d/DeadReckoning.java b/Mage.Sets/src/mage/cards/d/DeadReckoning.java
index 1e3843ae00..a12b0bd82f 100644
--- a/Mage.Sets/src/mage/cards/d/DeadReckoning.java
+++ b/Mage.Sets/src/mage/cards/d/DeadReckoning.java
@@ -7,6 +7,7 @@ import mage.abilities.effects.OneShotEffect;
 import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
+import mage.cards.CardsImpl;
 import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.constants.Zone;
@@ -59,22 +60,22 @@ class DeadReckoningEffect extends OneShotEffect {
 
     @Override
     public boolean apply(Game game, Ability source) {
-        Player you = game.getPlayer(source.getControllerId());
+        Player controller = game.getPlayer(source.getControllerId());
         TargetCardInYourGraveyard target1 = new TargetCardInYourGraveyard(new FilterCreatureCard("creature card in your graveyard"));
         TargetCreaturePermanent target2 = new TargetCreaturePermanent();
 
-        if (you != null) {
+        if (controller != null) {
             if (target1.canChoose(source.getControllerId(), game)
-                    && you.choose(Outcome.Benefit, target1, source.getSourceId(), game)
+                    && controller.choose(Outcome.Benefit, target1, source.getSourceId(), game)
                     && target2.canChoose(source.getControllerId(), game)
-                    && you.choose(Outcome.Damage, target2, source.getSourceId(), game)) {
+                    && controller.choose(Outcome.Damage, target2, source.getSourceId(), game)) {
                 Card creatureInGraveyard = game.getCard(target1.getFirstTarget());
                 if (creatureInGraveyard != null) {
-                    if (creatureInGraveyard.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true)) {
+                    if (controller.putCardsOnTopOfLibrary(creatureInGraveyard, game, source, true)) {
                         int power = creatureInGraveyard.getPower().getValue();
                         Permanent creature = game.getPermanent(target2.getFirstTarget());
                         if (creature != null) {
-                            creature.damage(power, source.getSourceId(), game, true, true);
+                            creature.damage(power, source.getSourceId(), game, false, true);
                             return true;
                         }
                     }
diff --git a/Mage.Sets/src/mage/cards/d/DiminishingReturns.java b/Mage.Sets/src/mage/cards/d/DiminishingReturns.java
index cee47ba767..f1c69c6f8c 100644
--- a/Mage.Sets/src/mage/cards/d/DiminishingReturns.java
+++ b/Mage.Sets/src/mage/cards/d/DiminishingReturns.java
@@ -4,7 +4,8 @@ package mage.cards.d;
 import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
-import mage.cards.Card;
+import mage.abilities.effects.common.DrawCardAllEffect;
+import mage.abilities.effects.common.ShuffleHandGraveyardAllEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
@@ -23,7 +24,8 @@ public final class DiminishingReturns extends CardImpl {
         super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{U}{U}");
 
         // Each player shuffles their hand and graveyard into their library. You exile the top ten cards of your library. Then each player draws up to seven cards.
-        this.getSpellAbility().addEffect(new DiminishingReturnsEffect());
+        this.getSpellAbility().addEffect(new ShuffleHandGraveyardAllEffect());
+        this.getSpellAbility().addEffect(new DiminishingReturnsEffect());        
     }
 
     public DiminishingReturns(final DiminishingReturns card) {
@@ -40,7 +42,7 @@ class DiminishingReturnsEffect extends OneShotEffect {
 
     public DiminishingReturnsEffect() {
         super(Outcome.Neutral);
-        staticText = "Each player shuffles their hand and graveyard into their library. You exile the top ten cards of your library. Then each player draws up to seven cards.";
+        staticText = "You exile the top ten cards of your library. Then each player draws up to seven cards.";
     }
 
     public DiminishingReturnsEffect(final DiminishingReturnsEffect effect) {
@@ -51,28 +53,13 @@ class DiminishingReturnsEffect extends OneShotEffect {
     public boolean apply(Game game, Ability source) {
         Player controller = game.getPlayer(source.getControllerId());
         if (controller != null) {
+            controller.moveCards(controller.getLibrary().getTopCards(game, 10), Zone.EXILED, source, game);
+            game.getState().processAction(game);
             for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
                 Player player = game.getPlayer(playerId);
                 if (player != null) {
-                    for (Card card: player.getHand().getCards(game)) {
-                        card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true);
-                    }                    
-                    for (Card card: player.getGraveyard().getCards(game)) {
-                        card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true);
-                    }                    
-                    player.shuffleLibrary(source, game);
-                }
-            }
-
-            for (Card card: controller.getLibrary().getTopCards(game, 10)) {
-                controller.moveCardToExileWithInfo(card, null, "", source.getSourceId(), game, Zone.LIBRARY, true);
-            }
-
-            for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
-                Player player = game.getPlayer(playerId);
-                if (player != null) {
-                    int cardsToDrawCount = player.getAmount(0, 7, "How many cards to draw (up to 7)?", game);
-                    player.drawCards(cardsToDrawCount, source.getSourceId(), game);
+                    player.drawCards(player.getAmount(0, 7, "How many cards to draw (up to 7)?", game),
+                            source.getSourceId(), game);
                 }
             }
         }
diff --git a/Mage.Sets/src/mage/cards/d/Doomsday.java b/Mage.Sets/src/mage/cards/d/Doomsday.java
index 59f04c1df2..15ed6ab58a 100644
--- a/Mage.Sets/src/mage/cards/d/Doomsday.java
+++ b/Mage.Sets/src/mage/cards/d/Doomsday.java
@@ -58,47 +58,24 @@ class DoomsdayEffect extends OneShotEffect {
 
     @Override
     public boolean apply(Game game, Ability source) {
-        Player player = game.getPlayer(source.getControllerId());
+        Player controller = game.getPlayer(source.getControllerId());
 
-        if (player != null) {
+        if (controller != null) {
             //Search your library and graveyard for five cards
             Cards allCards = new CardsImpl();
-            Cards cards = new CardsImpl();
-            allCards.addAll(player.getLibrary().getCardList());
-            allCards.addAll(player.getGraveyard());
+            allCards.addAll(controller.getLibrary().getCardList());
+            allCards.addAll(controller.getGraveyard());
             int number = Math.min(5, allCards.size());
             TargetCard target = new TargetCard(number, number, Zone.ALL, new FilterCard());
 
-            if (player.choose(Outcome.Benefit, allCards, target, game)) {
-                // exile the rest
-                for (UUID uuid : allCards) {
-                    if (!target.getTargets().contains(uuid)) {
-                        Card card = game.getCard(uuid);
-                        if (card != null) {
-                            card.moveToExile(null, "Doomsday", source.getSourceId(), game);
-                        }
-                    } else {
-                        cards.add(uuid);
-                    }
-
-                }
+            if (controller.choose(Outcome.Benefit, allCards, target, game)) {
+                Cards toLibrary = new CardsImpl(target.getTargets());
+                allCards.removeAll(toLibrary);
+                // Exile the rest
+                controller.moveCards(allCards, Zone.EXILED, source, game);
                 //Put the chosen cards on top of your library in any order
-                target = new TargetCard(Zone.ALL, new FilterCard("Card to put on top"));
-                while (cards.size() > 1 && player.canRespond()) {
-                    player.choose(Outcome.Neutral, cards, target, game);
-                    Card card = cards.get(target.getFirstTarget(), game);
-                    if (card != null) {
-                        cards.remove(card);
-                        card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true);
-                    }
-                    target.clearChosen();
-                }
-                if (cards.size() == 1) {
-                    Card card = cards.get(cards.iterator().next(), game);
-                    card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true);
-                }
+                controller.putCardsOnTopOfLibrary(toLibrary, game, source, true);
             }
-
             return true;
         }
         return false;
diff --git a/Mage.Sets/src/mage/cards/d/DwellOnThePast.java b/Mage.Sets/src/mage/cards/d/DwellOnThePast.java
index 57164964d9..f57594e87a 100644
--- a/Mage.Sets/src/mage/cards/d/DwellOnThePast.java
+++ b/Mage.Sets/src/mage/cards/d/DwellOnThePast.java
@@ -8,6 +8,7 @@ import mage.abilities.effects.OneShotEffect;
 import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
+import mage.cards.CardsImpl;
 import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.constants.Zone;
@@ -61,23 +62,9 @@ class DwellOnThePastEffect extends OneShotEffect {
 
     @Override
     public boolean apply(Game game, Ability source) {
-        Player player = game.getPlayer(source.getFirstTarget());
-        if (player != null) {
-            List<UUID> targets = source.getTargets().get(1).getTargets();
-            boolean shuffle = false;
-            for (UUID targetId : targets) {
-                Card card = game.getCard(targetId);
-                if (card != null) {
-                    if (player.getGraveyard().contains(card.getId())) {
-                        card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true);
-                        shuffle = true;
-                    }
-                }
-            }
-            if (shuffle) {
-                player.shuffleLibrary(source, game);
-            }
-            return true;
+        Player controller = game.getPlayer(source.getFirstTarget());
+        if (controller != null) {
+            return controller.shuffleCardsToLibrary(new CardsImpl(source.getTargets().get(1).getTargets()), game, source);
         }
         return false;
     }
diff --git a/Mage.Sets/src/mage/cards/e/EpitaphGolem.java b/Mage.Sets/src/mage/cards/e/EpitaphGolem.java
index 9751cffa5b..19b60d1e56 100644
--- a/Mage.Sets/src/mage/cards/e/EpitaphGolem.java
+++ b/Mage.Sets/src/mage/cards/e/EpitaphGolem.java
@@ -7,7 +7,7 @@ import mage.abilities.Ability;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.OneShotEffect;
-import mage.cards.Card;
+import mage.abilities.effects.common.PutOnLibraryTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
@@ -15,6 +15,7 @@ import mage.constants.SubType;
 import mage.constants.Outcome;
 import mage.constants.Zone;
 import mage.game.Game;
+import mage.players.Player;
 import mage.target.common.TargetCardInYourGraveyard;
 
 /**
@@ -32,7 +33,7 @@ public final class EpitaphGolem extends CardImpl {
         // {2}: Put target card from your graveyard on the bottom of your library.
         Ability ability = new SimpleActivatedAbility(
                 Zone.BATTLEFIELD,
-                new EpitaphGolemGraveyardToLibraryEffect(),
+                new PutOnLibraryTargetEffect(true),
                 new ManaCostsImpl("{2}"));
         ability.addTarget(new TargetCardInYourGraveyard());
         this.addAbility(ability);
@@ -66,9 +67,9 @@ class EpitaphGolemGraveyardToLibraryEffect extends OneShotEffect {
 
     @Override
     public boolean apply(Game game, Ability source) {
-        Card card = game.getCard(getTargetPointer().getFirst(game, source));
-        if (card != null && game.getState().getZone(card.getId()) == Zone.GRAVEYARD) {
-            return card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, false);
+        Player controller = game.getPlayer(source.getControllerId());
+        if (controller != null) {
+            return controller.putCardsOnBottomOfLibrary(game.getCard(getTargetPointer().getFirst(game, source)), game, source, true);
         }
         return false;
     }
diff --git a/Mage.Sets/src/mage/cards/g/GaeasBlessing.java b/Mage.Sets/src/mage/cards/g/GaeasBlessing.java
index 8f617b4bc2..e3de06d4a5 100644
--- a/Mage.Sets/src/mage/cards/g/GaeasBlessing.java
+++ b/Mage.Sets/src/mage/cards/g/GaeasBlessing.java
@@ -1,7 +1,6 @@
 
 package mage.cards.g;
 
-import java.util.List;
 import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.common.ZoneChangeTriggeredAbility;
@@ -10,6 +9,7 @@ import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
+import mage.cards.CardsImpl;
 import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.constants.Zone;
@@ -68,23 +68,9 @@ class GaeasBlessingEffect extends OneShotEffect {
 
     @Override
     public boolean apply(Game game, Ability source) {
-        Player player = game.getPlayer(source.getFirstTarget());
-        if (player != null) {
-            List<UUID> targets = source.getTargets().get(1).getTargets();
-            boolean shuffle = false;
-            for (UUID targetId : targets) {
-                Card card = game.getCard(targetId);
-                if (card != null) {
-                    if (player.getGraveyard().contains(card.getId())) {
-                        card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true);
-                        shuffle = true;
-                    }
-                }
-            }
-            if (shuffle) {
-                player.shuffleLibrary(source, game);
-            }
-            return true;
+        Player targetPlayer = game.getPlayer(source.getFirstTarget());
+        if (targetPlayer != null) {
+            return targetPlayer.shuffleCardsToLibrary(new CardsImpl(source.getTargets().get(1).getTargets()), game, source);
         }
         return false;
     }
@@ -154,14 +140,7 @@ class GaeasBlessingGraveToLibraryEffect extends OneShotEffect {
     public boolean apply(Game game, Ability source) {
         Player controller = game.getPlayer(source.getControllerId());
         if (controller != null) {
-            game.informPlayers(controller.getLogName() + " shuffle their graveyard into their library");
-            for (Card card : controller.getGraveyard().getCards(game)) {
-                controller.moveCardToLibraryWithInfo(card, source.getSourceId(), game, Zone.GRAVEYARD, true, true);
-            }
-            controller.getLibrary().addAll(controller.getGraveyard().getCards(game), game);
-            controller.getGraveyard().clear();
-            controller.shuffleLibrary(source, game);
-            return true;
+            return controller.shuffleCardsToLibrary(controller.getGraveyard(), game, source);
         }
         return false;
     }
diff --git a/Mage.Sets/src/mage/cards/g/GravebaneZombie.java b/Mage.Sets/src/mage/cards/g/GravebaneZombie.java
index 43d6a30779..6717c31377 100644
--- a/Mage.Sets/src/mage/cards/g/GravebaneZombie.java
+++ b/Mage.Sets/src/mage/cards/g/GravebaneZombie.java
@@ -18,6 +18,7 @@ import mage.game.events.GameEvent;
 import mage.game.events.GameEvent.EventType;
 import mage.game.events.ZoneChangeEvent;
 import mage.game.permanent.Permanent;
+import mage.players.Player;
 
 /**
  *
@@ -50,7 +51,7 @@ class GravebaneZombieEffect extends ReplacementEffectImpl {
 
     GravebaneZombieEffect() {
         super(Duration.WhileOnBattlefield, Outcome.Neutral);
-        staticText = "If {this} would die, put Gravebane Zombie on top of its owner's library instead";
+        staticText = "If {this} would die, put {this} on top of its owner's library instead";
     }
 
     GravebaneZombieEffect(final GravebaneZombieEffect effect) {
@@ -60,11 +61,9 @@ class GravebaneZombieEffect extends ReplacementEffectImpl {
     @Override
     public boolean replaceEvent(GameEvent event, Ability source, Game game) {
         Permanent permanent = ((ZoneChangeEvent) event).getTarget();
-        if (permanent != null) {
-            if (permanent.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true)) {
-                game.informPlayers(permanent.getName() + " was put on the top of its owner's library");
-                return true;
-            }
+        Player controller = game.getPlayer(source.getControllerId());
+        if (permanent != null && controller !=null) {
+            return controller.putCardsOnTopOfLibrary(permanent, game, source, true);
         }
         return false;
     }
diff --git a/Mage.Sets/src/mage/cards/h/HarmonicConvergence.java b/Mage.Sets/src/mage/cards/h/HarmonicConvergence.java
index 95dc56ddf9..5c33eb781a 100644
--- a/Mage.Sets/src/mage/cards/h/HarmonicConvergence.java
+++ b/Mage.Sets/src/mage/cards/h/HarmonicConvergence.java
@@ -17,6 +17,9 @@ import mage.players.Player;
 import mage.target.TargetCard;
 
 import java.util.*;
+import mage.cards.Cards;
+import mage.filter.common.FilterEnchantmentPermanent;
+import mage.filter.predicate.other.OwnerIdPredicate;
 
 /**
  *
@@ -27,7 +30,6 @@ public final class HarmonicConvergence extends CardImpl {
     public HarmonicConvergence(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{G}");
 
-
         // Put all enchantments on top of their owners' libraries.
         this.getSpellAbility().addEffect(new HarmonicConvergenceEffect());
     }
@@ -60,44 +62,18 @@ class HarmonicConvergenceEffect extends OneShotEffect {
 
     @Override
     public boolean apply(Game game, Ability source) {
-        List<Permanent> enchantments = game.getBattlefield().getActivePermanents(StaticFilters.FILTER_ENCHANTMENT_PERMANENT,
-                source.getControllerId(),
-                source.getSourceId(),
-                game);
-
-        Map<UUID, List<Permanent>> moveList = new HashMap<>();
-        for (Permanent permanent : enchantments) {
-            List<Permanent> list = moveList.computeIfAbsent(permanent.getControllerId(), k -> new ArrayList<>());
-            list.add(permanent);
-        }
-
-        TargetCard target = new TargetCard(Zone.BATTLEFIELD, new FilterCard("card to put on top of your library"));
-        for (UUID playerId : moveList.keySet()) {
+        for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) {
             Player player = game.getPlayer(playerId);
-            List<Permanent> list = moveList.get(playerId);
-            if (player == null) {
-                continue;
-            }
-
-            CardsImpl cards = new CardsImpl();
-            for (Permanent permanent : list) {
-                cards.add(permanent);
-            }
-            while (player.canRespond() && cards.size() > 1) {
-                player.choose(Outcome.Neutral, cards, target, game);
-                Permanent permanent = game.getPermanent(target.getFirstTarget());
-                if (permanent != null) {
-                    cards.remove(permanent);
-                    permanent.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true);
+            if (player != null) {
+                FilterEnchantmentPermanent filter = new FilterEnchantmentPermanent();
+                filter.add(new OwnerIdPredicate(player.getId()));
+                Cards toLib = new CardsImpl();
+                for(Permanent enchantment: game.getBattlefield().getActivePermanents(filter, playerId, source.getSourceId(), game)) {
+                    toLib.add(enchantment);
                 }
-                target.clearChosen();
-            }
-            if (cards.size() == 1) {
-                Permanent permanent = game.getPermanent(cards.iterator().next());
-                permanent.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true);
-            }
-        }
-
+                player.putCardsOnTopOfLibrary(toLib, game, source, true);
+            }            
+        }        
         return true;
     }
 }
diff --git a/Mage.Sets/src/mage/cards/k/KrosanReclamation.java b/Mage.Sets/src/mage/cards/k/KrosanReclamation.java
index 2ae901a246..671b114408 100644
--- a/Mage.Sets/src/mage/cards/k/KrosanReclamation.java
+++ b/Mage.Sets/src/mage/cards/k/KrosanReclamation.java
@@ -1,7 +1,6 @@
 
 package mage.cards.k;
 
-import java.util.List;
 import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.costs.mana.ManaCostsImpl;
@@ -10,6 +9,7 @@ import mage.abilities.keyword.FlashbackAbility;
 import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
+import mage.cards.CardsImpl;
 import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.constants.TimingRule;
@@ -66,23 +66,9 @@ class KrosanReclamationEffect extends OneShotEffect {
 
     @Override
     public boolean apply(Game game, Ability source) {
-        Player player = game.getPlayer(source.getFirstTarget());
-        if (player != null) {
-            List<UUID> targets = source.getTargets().get(1).getTargets();
-            boolean shuffle = false;
-            for (UUID targetId : targets) {
-                Card card = game.getCard(targetId);
-                if (card != null) {
-                    if (player.getGraveyard().contains(card.getId())) {
-                        card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true);
-                        shuffle = true;
-                    }
-                }
-            }
-            if (shuffle) {
-                player.shuffleLibrary(source, game);
-            }
-            return true;
+        Player targetPlayer = game.getPlayer(source.getFirstTarget());
+        if (targetPlayer != null) {
+            return targetPlayer.shuffleCardsToLibrary(new CardsImpl(source.getTargets().get(1).getTargets()), game, source);
         }
         return false;
     }
diff --git a/Mage.Sets/src/mage/cards/l/LichsMirror.java b/Mage.Sets/src/mage/cards/l/LichsMirror.java
index 3bff7b7d4e..d12065242e 100644
--- a/Mage.Sets/src/mage/cards/l/LichsMirror.java
+++ b/Mage.Sets/src/mage/cards/l/LichsMirror.java
@@ -8,7 +8,10 @@ import mage.abilities.effects.ReplacementEffectImpl;
 import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
+import mage.cards.Cards;
+import mage.cards.CardsImpl;
 import mage.constants.*;
+import mage.filter.StaticFilters;
 import mage.filter.common.FilterControlledPermanent;
 import mage.filter.predicate.other.OwnerIdPredicate;
 import mage.game.Game;
@@ -64,32 +67,20 @@ class LichsMirrorEffect extends ReplacementEffectImpl {
     public boolean replaceEvent(GameEvent event, Ability source, Game game) {
         Player player = game.getPlayer(event.getPlayerId());
         if (player != null) {
+            Cards toLib = new CardsImpl();
             FilterControlledPermanent filter = new FilterControlledPermanent();
             filter.add(new OwnerIdPredicate(player.getId()));
-            for (UUID uuid : player.getHand().copy()) {
-                Card card = game.getCard(uuid);
-                if (card != null) {
-                    card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true);
-                }
-            }
-        
-            for (UUID uuid : player.getGraveyard().copy()) {
-                Card card = game.getCard(uuid);
-                if (card != null) {
-                    card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true);
-                }
-            }
-            
+            toLib.addAll(player.getHand());
+            toLib.addAll(player.getGraveyard());
             for(Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)){
-                permanent.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true);
-            }
-            player.shuffleLibrary(source, game);
-            
-            player.drawCards(7, source.getSourceId(), game);
-            
-            player.setLife(20, game, source);
+                toLib.add(permanent);
+            }            
+            player.shuffleCardsToLibrary(toLib, game, source);
+            game.getState().processAction(game);
+            player.drawCards(7, source.getSourceId(), game);            
+            player.setLife(20, game, source);            
         }
-        return true;
+        return true; // replace the loses event
     }
 
     @Override
@@ -99,10 +90,7 @@ class LichsMirrorEffect extends ReplacementEffectImpl {
 
     @Override
     public boolean applies(GameEvent event, Ability source, Game game) {
-        if (event.getPlayerId().equals(source.getControllerId())) {
-            return true;
-        }
-        return false;
+        return event.getPlayerId().equals(source.getControllerId());
     }
 
 }
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/m/MemorysJourney.java b/Mage.Sets/src/mage/cards/m/MemorysJourney.java
index 71b1f58fc3..4893700f1b 100644
--- a/Mage.Sets/src/mage/cards/m/MemorysJourney.java
+++ b/Mage.Sets/src/mage/cards/m/MemorysJourney.java
@@ -10,6 +10,7 @@ import mage.abilities.keyword.FlashbackAbility;
 import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
+import mage.cards.CardsImpl;
 import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.constants.TimingRule;
@@ -65,23 +66,9 @@ class MemorysJourneyEffect extends OneShotEffect {
 
     @Override
     public boolean apply(Game game, Ability source) {
-        Player player = game.getPlayer(source.getFirstTarget());
-        if (player != null) {
-            List<UUID> targets = source.getTargets().get(1).getTargets();
-            boolean shuffle = false;
-            for (UUID targetId : targets) {
-                Card card = game.getCard(targetId);
-                if (card != null) {
-                    if (player.getGraveyard().contains(card.getId())) {
-                        card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true);
-                        shuffle = true;
-                    }
-                }
-            }
-            if (shuffle) {
-                player.shuffleLibrary(source, game);
-            }
-            return true;
+        Player targetPlayer = game.getPlayer(source.getFirstTarget());
+        if (targetPlayer != null) {
+            return targetPlayer.shuffleCardsToLibrary(new CardsImpl(source.getTargets().get(1).getTargets()), game, source);
         }
         return false;
     }
diff --git a/Mage.Sets/src/mage/cards/m/MirrorMadPhantasm.java b/Mage.Sets/src/mage/cards/m/MirrorMadPhantasm.java
index 34511c3480..5d8a5207f3 100644
--- a/Mage.Sets/src/mage/cards/m/MirrorMadPhantasm.java
+++ b/Mage.Sets/src/mage/cards/m/MirrorMadPhantasm.java
@@ -69,10 +69,7 @@ class MirrorMadPhantasmEffect extends OneShotEffect {
             if (owner == null) {
                 return false;
             }
-            if (owner.moveCards(perm, Zone.LIBRARY, source, game)) {
-                owner.shuffleLibrary(source, game);
-                perm.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true);
-                owner.shuffleLibrary(source, game);
+            if (owner.shuffleCardsToLibrary(perm, game, source)) {
                 Cards cards = new CardsImpl();
                 Card phantasmCard = null;
                 for (Card card : owner.getLibrary().getCards(game)) {
diff --git a/Mage.Sets/src/mage/cards/m/MirrorOfFate.java b/Mage.Sets/src/mage/cards/m/MirrorOfFate.java
index bc8bd0e12f..5eb29e5592 100644
--- a/Mage.Sets/src/mage/cards/m/MirrorOfFate.java
+++ b/Mage.Sets/src/mage/cards/m/MirrorOfFate.java
@@ -67,42 +67,20 @@ class MirrorOfFateEffect extends OneShotEffect {
 
     @Override
     public boolean apply(Game game, Ability source) {
-        Player player = game.getPlayer(source.getControllerId());
-        if (player == null) {
+        Player controller = game.getPlayer(source.getControllerId());
+        if (controller == null) {
             return false;
         }
-
+        // Choose up to seven face-up exiled cards you own
         CardsImpl cards = new CardsImpl();
         MirrorOfFateTarget targetExile = new MirrorOfFateTarget();
-        if (player.choose(outcome, targetExile, source.getSourceId(), game)) {
-            for (UUID cardId : targetExile.getTargets()) {
-                Card card = game.getCard(cardId);
-                if (card != null) {
-                    cards.add(card);
-                }
-            }
+        if (controller.choose(outcome, targetExile, source.getSourceId(), game)) {
+            cards.addAll(targetExile.getTargets());
         }
-
-        for (Card card : player.getLibrary().getCards(game)) {
-            card.moveToExile(null, null, source.getSourceId(), game);
-        }
-
-        TargetCard target = new TargetCard(Zone.EXILED, new FilterCard("card to put on top of your library"));
-        while (player.canRespond() && cards.size() > 1) {
-            player.choose(Outcome.Neutral, cards, target, game);
-            Card card = cards.get(target.getFirstTarget(), game);
-            if (card != null) {
-                cards.remove(card);
-                card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true);
-            }
-            target.clearChosen();
-        }
-        if (cards.size() == 1) {
-            Card card = cards.get(cards.iterator().next(), game);
-            card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true);
-        }
-
-        return true;
+        // Exile all the cards from your library
+        controller.moveCards(new CardsImpl(controller.getLibrary().getCardList()), Zone.EXILED, source, game);
+        // put the chosen cards on top of your library"
+        return controller.putCardsOnTopOfLibrary(cards, game, source, true);
     }
 }
 
diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
index c937ce70d6..171038de3a 100644
--- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
+++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
@@ -2659,6 +2659,21 @@ public class TestPlayer implements Player {
         return computerPlayer.putCardsOnTopOfLibrary(cards, game, source, anyOrder);
     }
 
+    @Override
+    public boolean putCardsOnTopOfLibrary(Card card, Game game, Ability source, boolean anyOrder) {
+        return computerPlayer.putCardsOnTopOfLibrary(card, game, source, anyOrder);
+    }
+    
+    @Override
+    public boolean shuffleCardsToLibrary(Cards cards, Game game, Ability source) {
+        return computerPlayer.shuffleCardsToLibrary(cards, game, source);
+    }
+    
+    @Override
+    public boolean shuffleCardsToLibrary(Card card, Game game, Ability source) {
+        return computerPlayer.shuffleCardsToLibrary(card, game, source);
+    }
+    
     @Override
     public void setCastSourceIdWithAlternateMana(UUID sourceId, ManaCosts manaCosts, Costs costs) {
         computerPlayer.setCastSourceIdWithAlternateMana(sourceId, manaCosts, costs);
diff --git a/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java b/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java
index 7093c32259..652a654636 100644
--- a/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java
+++ b/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java
@@ -892,6 +892,21 @@ public class PlayerStub implements Player {
         return false;
     }
 
+    @Override
+    public boolean putCardsOnTopOfLibrary(Card card, Game game, Ability source, boolean anyOrder) {
+        return false;
+    }
+    
+    @Override
+    public boolean shuffleCardsToLibrary(Cards cards, Game game, Ability source) {
+        return false;
+    }
+
+    @Override
+    public boolean shuffleCardsToLibrary(Card card, Game game, Ability source) {
+        return false;
+    }
+    
     @Override
     public boolean putCardOnTopXOfLibrary(Card card, Game game, Ability source, int xFromTheTop) {
         return true;
diff --git a/Mage/src/main/java/mage/players/Player.java b/Mage/src/main/java/mage/players/Player.java
index 61e04700c6..cbf313bc28 100644
--- a/Mage/src/main/java/mage/players/Player.java
+++ b/Mage/src/main/java/mage/players/Player.java
@@ -530,8 +530,11 @@ public interface Player extends MageItem, Copyable<Player> {
      *
      * @param cards    - list of cards that have to be moved
      * @param game     - game
-     * @param anyOrder - true if player can determine the order of the cards
-     *                 else random order
+     * @param anyOrder - true = if player can determine the order of the cards
+     *                     else false = random order
+     *                  401.4. If an effect puts two or more cards in a specific position in a library
+     *                  at the same time, the owner of those cards may arrange them in any order. 
+     *                  That library’s owner doesn’t reveal the order in which the cards go into the library.
      * @param source   - source ability
      * @return
      */
@@ -560,7 +563,13 @@ public interface Player extends MageItem, Copyable<Player> {
      * @return
      */
     boolean putCardsOnTopOfLibrary(Cards cards, Game game, Ability source, boolean anyOrder);
+    
+    boolean putCardsOnTopOfLibrary(Card card, Game game, Ability source, boolean anyOrder);
 
+    boolean shuffleCardsToLibrary(Cards cards, Game game, Ability source);
+    
+    boolean shuffleCardsToLibrary(Card card, Game game, Ability source);
+    
     // set the value for X mana spells and abilities
     default int announceXMana(int min, int max, String message, Game game, Ability ability) {
         return announceXMana(min, max, 1, message, game, ability);
@@ -725,7 +734,7 @@ public interface Player extends MageItem, Copyable<Player> {
 
     /**
      * Universal method to move cards from one zone to another. Do not mix
-     * objects from different from zones to move.
+     * objects from different zones to move.
      *
      * @param cards
      * @param toZone
diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java
index bbc47b1193..cdd42ea188 100644
--- a/Mage/src/main/java/mage/players/PlayerImpl.java
+++ b/Mage/src/main/java/mage/players/PlayerImpl.java
@@ -613,9 +613,9 @@ public abstract class PlayerImpl implements Player, Serializable {
                     && this.hasOpponent(sourceControllerId, game)
                     && game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, null, sourceControllerId, game) == null
                     && abilities.stream()
-                    .filter(HexproofBaseAbility.class::isInstance)
-                    .map(HexproofBaseAbility.class::cast)
-                    .anyMatch(ability -> ability.checkObject(source, game))) {
+                            .filter(HexproofBaseAbility.class::isInstance)
+                            .map(HexproofBaseAbility.class::cast)
+                            .anyMatch(ability -> ability.checkObject(source, game))) {
                 return false;
             }
 
@@ -655,7 +655,7 @@ public abstract class PlayerImpl implements Player, Serializable {
                 game.informPlayers(getLogName() + " discards down to "
                         + this.maxHandSize
                         + (this.maxHandSize == 1
-                        ? " hand card" : " hand cards"));
+                                ? " hand card" : " hand cards"));
             }
             discard(hand.size() - this.maxHandSize, false, null, game);
         }
@@ -804,7 +804,7 @@ public abstract class PlayerImpl implements Player, Serializable {
         }
         GameEvent gameEvent = GameEvent.getEvent(GameEvent.EventType.DISCARD_CARD,
                 card.getId(), source == null
-                        ? null : source.getSourceId(), playerId);
+                ? null : source.getSourceId(), playerId);
         gameEvent.setFlag(source != null); // event from effect or from cost (source == null)
         if (game.replaceEvent(gameEvent, source)) {
             return false;
@@ -966,6 +966,27 @@ public abstract class PlayerImpl implements Player, Serializable {
         return true;
     }
 
+    @Override
+    public boolean shuffleCardsToLibrary(Cards cards, Game game, Ability source) {
+        if (cards.isEmpty()) {
+            return true;
+        }
+        game.informPlayers(getName() + " shuffels " + CardUtil.numberToText(cards.size(), "a")
+                + " card" + (cards.size() == 1 ? "" : "s")
+                + " into their library.");
+        boolean status = moveCards(cards, Zone.LIBRARY, source, game);
+        shuffleLibrary(source, game);
+        return status;
+    }
+    
+    @Override
+    public boolean shuffleCardsToLibrary(Card card, Game game, Ability source) {
+        if (card == null) {
+            return true;
+        }
+        return shuffleCardsToLibrary(new CardsImpl(card), game, source);
+    }
+    
     @Override
     public boolean putCardOnTopXOfLibrary(Card card, Game game, Ability source, int xFromTheTop) {
         if (card.isOwnedBy(getId())) {
@@ -1005,7 +1026,6 @@ public abstract class PlayerImpl implements Player, Serializable {
     public boolean putCardsOnTopOfLibrary(Cards cardsToLibrary, Game game, Ability source, boolean anyOrder) {
         if (cardsToLibrary != null && !cardsToLibrary.isEmpty()) {
             Cards cards = new CardsImpl(cardsToLibrary); // prevent possible ConcurrentModificationException
-            UUID sourceId = (source == null ? null : source.getSourceId());
             if (!anyOrder) {
                 // random order
                 List<UUID> ids = new ArrayList<>(cards);
@@ -1039,6 +1059,14 @@ public abstract class PlayerImpl implements Player, Serializable {
         return true;
     }
 
+    @Override
+    public boolean putCardsOnTopOfLibrary(Card cardToLibrary, Game game, Ability source, boolean anyOrder) {
+        if (cardToLibrary != null) {
+            return putCardsOnTopOfLibrary(new CardsImpl(cardToLibrary), game, source, anyOrder);
+        }
+        return true;
+    }
+
     private boolean moveObjectToLibrary(UUID objectId, UUID sourceId, Game game, boolean toTop, boolean withName) {
         MageObject mageObject = game.getObject(objectId);
         if (mageObject != null) {
@@ -1813,9 +1841,9 @@ public abstract class PlayerImpl implements Player, Serializable {
     }
 
     private List<Permanent> getPermanentsThatCanBeUntapped(Game game,
-                                                           List<Permanent> canBeUntapped,
-                                                           RestrictionUntapNotMoreThanEffect handledEffect,
-                                                           Map<Entry<RestrictionUntapNotMoreThanEffect, Set<Ability>>, Integer> notMoreThanEffectsUsage) {
+            List<Permanent> canBeUntapped,
+            RestrictionUntapNotMoreThanEffect handledEffect,
+            Map<Entry<RestrictionUntapNotMoreThanEffect, Set<Ability>>, Integer> notMoreThanEffectsUsage) {
         List<Permanent> leftForUntap = new ArrayList<>();
         // select permanents that can still be untapped
         for (Permanent permanent : canBeUntapped) {
@@ -2524,7 +2552,7 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean searchLibrary(TargetCardInLibrary target, Ability source, Game game, UUID targetPlayerId,
-                                 boolean triggerEvents) {
+            boolean triggerEvents) {
         //20091005 - 701.14c
         Library searchedLibrary = null;
         String searchInfo = null;
@@ -2726,7 +2754,7 @@ public abstract class PlayerImpl implements Player, Serializable {
     /**
      * @param game
      * @param appliedEffects
-     * @param numSides       Number of sides the dice has
+     * @param numSides Number of sides the dice has
      * @return the number that the player rolled
      */
     @Override
@@ -2763,16 +2791,16 @@ public abstract class PlayerImpl implements Player, Serializable {
     /**
      * @param game
      * @param appliedEffects
-     * @param numberChaosSides  The number of chaos sides the planar die
-     *                          currently has (normally 1 but can be 5)
+     * @param numberChaosSides The number of chaos sides the planar die
+     * currently has (normally 1 but can be 5)
      * @param numberPlanarSides The number of chaos sides the planar die
-     *                          currently has (normally 1)
+     * currently has (normally 1)
      * @return the outcome that the player rolled. Either ChaosRoll, PlanarRoll
      * or NilRoll
      */
     @Override
     public PlanarDieRoll rollPlanarDie(Game game, List<UUID> appliedEffects, int numberChaosSides,
-                                       int numberPlanarSides) {
+            int numberPlanarSides) {
         int result = RandomUtil.nextInt(9) + 1;
         PlanarDieRoll roll = PlanarDieRoll.NIL_ROLL;
         if (numberChaosSides + numberPlanarSides > 9) {
@@ -2929,7 +2957,7 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     /**
      * @param ability
-     * @param available    if null, it won't be checked if enough mana is available
+     * @param available if null, it won't be checked if enough mana is available
      * @param sourceObject
      * @param game
      * @return
@@ -3652,7 +3680,7 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean canPaySacrificeCost(Permanent permanent, UUID sourceId,
-                                       UUID controllerId, Game game
+            UUID controllerId, Game game
     ) {
         return sacrificeCostFilter == null || !sacrificeCostFilter.match(permanent, sourceId, controllerId, game);
     }
@@ -3805,8 +3833,8 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCards(Card card, Zone toZone,
-                             Ability source, Game game,
-                             boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
+            Ability source, Game game,
+            boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
     ) {
         Set<Card> cardList = new HashSet<>();
         if (card != null) {
@@ -3817,22 +3845,22 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCards(Cards cards, Zone toZone,
-                             Ability source, Game game
+            Ability source, Game game
     ) {
         return moveCards(cards.getCards(game), toZone, source, game);
     }
 
     @Override
     public boolean moveCards(Set<Card> cards, Zone toZone,
-                             Ability source, Game game
+            Ability source, Game game
     ) {
         return moveCards(cards, toZone, source, game, false, false, false, null);
     }
 
     @Override
     public boolean moveCards(Set<Card> cards, Zone toZone,
-                             Ability source, Game game,
-                             boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
+            Ability source, Game game,
+            boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
     ) {
         if (cards.isEmpty()) {
             return true;
@@ -3934,8 +3962,8 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCardsToExile(Card card, Ability source,
-                                    Game game, boolean withName, UUID exileId,
-                                    String exileZoneName
+            Game game, boolean withName, UUID exileId,
+            String exileZoneName
     ) {
         Set<Card> cards = new HashSet<>();
         cards.add(card);
@@ -3944,8 +3972,8 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCardsToExile(Set<Card> cards, Ability source,
-                                    Game game, boolean withName, UUID exileId,
-                                    String exileZoneName
+            Game game, boolean withName, UUID exileId,
+            String exileZoneName
     ) {
         if (cards.isEmpty()) {
             return true;
@@ -3961,14 +3989,14 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCardToHandWithInfo(Card card, UUID sourceId,
-                                          Game game
+            Game game
     ) {
         return this.moveCardToHandWithInfo(card, sourceId, game, true);
     }
 
     @Override
     public boolean moveCardToHandWithInfo(Card card, UUID sourceId,
-                                          Game game, boolean withName
+            Game game, boolean withName
     ) {
         boolean result = false;
         Zone fromZone = game.getState().getZone(card.getId());
@@ -3993,7 +4021,7 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public Set<Card> moveCardsToGraveyardWithInfo(Set<Card> allCards, Ability source,
-                                                  Game game, Zone fromZone
+            Game game, Zone fromZone
     ) {
         UUID sourceId = source == null ? null : source.getSourceId();
         Set<Card> movedCards = new LinkedHashSet<>();
@@ -4001,7 +4029,7 @@ public abstract class PlayerImpl implements Player, Serializable {
             // identify cards from one owner
             Cards cards = new CardsImpl();
             UUID ownerId = null;
-            for (Iterator<Card> it = allCards.iterator(); it.hasNext(); ) {
+            for (Iterator<Card> it = allCards.iterator(); it.hasNext();) {
                 Card card = it.next();
                 if (cards.isEmpty()) {
                     ownerId = card.getOwnerId();
@@ -4064,7 +4092,7 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCardToGraveyardWithInfo(Card card, UUID sourceId,
-                                               Game game, Zone fromZone
+            Game game, Zone fromZone
     ) {
         if (card == null) {
             return false;
@@ -4093,8 +4121,8 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCardToLibraryWithInfo(Card card, UUID sourceId,
-                                             Game game, Zone fromZone,
-                                             boolean toTop, boolean withName
+            Game game, Zone fromZone,
+            boolean toTop, boolean withName
     ) {
         if (card == null) {
             return false;
@@ -4159,7 +4187,7 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCardToExileWithInfo(Card card, UUID exileId, String exileName, UUID sourceId,
-                                           Game game, Zone fromZone, boolean withName) {
+            Game game, Zone fromZone, boolean withName) {
         if (card == null) {
             return false;
         }
@@ -4182,7 +4210,7 @@ public abstract class PlayerImpl implements Player, Serializable {
                 game.informPlayers(this.getLogName() + " moves " + (withName ? card.getLogName()
                         + (card.isCopy() ? " (Copy)" : "") : "a card face down") + ' '
                         + (fromZone != null ? "from " + fromZone.toString().toLowerCase(Locale.ENGLISH)
-                        + ' ' : "") + "to the exile zone");
+                                + ' ' : "") + "to the exile zone");
 
             }
             result = true;
diff --git a/Mage/src/main/java/mage/players/StubPlayer.java b/Mage/src/main/java/mage/players/StubPlayer.java
index 6b1fe798d3..c9d959e34a 100644
--- a/Mage/src/main/java/mage/players/StubPlayer.java
+++ b/Mage/src/main/java/mage/players/StubPlayer.java
@@ -35,6 +35,7 @@ import static com.google.common.collect.Iterables.getOnlyElement;
 
 public class StubPlayer extends PlayerImpl implements Player {
 
+    @Override
     public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game) {
         if (target instanceof TargetPlayer) {
             for (Player player : game.getPlayers().values()) {
@@ -47,6 +48,7 @@ public class StubPlayer extends PlayerImpl implements Player {
         return false;
     }
 
+    @Override
     public boolean choose(Outcome outcome, Cards cards, TargetCard target, Game game) {
         cards.getCards(game).stream().map(MageItem::getId).forEach(cardId -> target.add(cardId, game));
         return true;

From 1dde600cadfb638e413b770a6365d85295fef469 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Sat, 20 Jun 2020 09:31:10 +0200
Subject: [PATCH 428/586] * Added test for "Rollback doesn't unflip a newly
 flipped Jace." The problem is not reproducible (closes #1973).

---
 .../test/cards/planeswalker/JaceTest.java     | 59 +++++++++++++++----
 1 file changed, 49 insertions(+), 10 deletions(-)

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/JaceTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/JaceTest.java
index 598ed67bdc..874026b6ed 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/JaceTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/JaceTest.java
@@ -1,9 +1,9 @@
-
 package org.mage.test.cards.planeswalker;
 
 import mage.constants.PhaseStep;
 import mage.constants.Zone;
 import mage.counters.CounterType;
+import org.junit.Assert;
 import org.junit.Test;
 import org.mage.test.serverside.base.CardTestPlayerBase;
 
@@ -72,35 +72,74 @@ public class JaceTest extends CardTestPlayerBase {
         assertExileCount("Jace, Vryn's Prodigy", 0);
         assertPermanentCount(playerA, "Jace, Telepath Unbound", 1);
     }
-    
+
+    /**
+     * Rollback doesn't unflip a newly flipped Jace #1973 Reporting on behalf of
+     * someone else (but he plays a lot on xmage) so I don't have any further
+     * information, but a rollback didn't reset a jace, vryn's prodigy. Haven't
+     * tried to recreate it.
+     */
+    @Test
+    public void rollbackDoesntUnflipJaceTest() {
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 4);
+        addCard(Zone.GRAVEYARD, playerA, "Mountain", 4);
+
+        // {T}: Draw a card, then discard a card. If there are five or more cards in your graveyard,
+        // exile Jace, Vryn's Prodigy, then return him to the battefield transformed under his owner's control.
+        addCard(Zone.BATTLEFIELD, playerA, "Jace, Vryn's Prodigy", 1); // {U}{1} - 0/2
+        addCard(Zone.HAND, playerA, "Pillarfield Ox", 1);
+
+        // Flash
+        // If a nontoken creature would enter the battlefield and it wasn't cast, exile it instead.
+        addCard(Zone.BATTLEFIELD, playerB, "Containment Priest", 1); // {2}{U}{U}
+
+        activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Draw a card");
+        setChoice(playerA, "Pillarfield Ox");
+                     
+        setStopAt(3, PhaseStep.BEGIN_COMBAT);
+        execute();
+        
+        currentGame.rollbackTurns(0); // Start of turn 3
+
+        assertGraveyardCount(playerA, "Pillarfield Ox", 0); // Goes back to hand
+        assertHandCount(playerA, "Pillarfield Ox", 1);
+        
+        assertExileCount("Jace, Vryn's Prodigy", 0);
+        
+        assertPermanentCount(playerA, "Jace, Telepath Unbound", 0);
+        assertPermanentCount(playerA, "Jace, Vryn's Prodigy", 1);
+        
+        Assert.assertFalse("Jace, Vryn's Prodigy may not be flipped", getPermanent("Jace, Vryn's Prodigy").isFlipped());
+    }
+
     @Test
     public void vrynCannotCastAncestralVisions() {
-                
+
         // {T}: Draw a card, then discard a card. If there are five or more cards in your graveyard,
         // exile Jace, Vryn's Prodigy, then return him to the battefield transformed under his owner's control.
         String jVryn = "Jace, Vryn's Prodigy"; // {U}{1} 0/2
-        
+
         //−3: You may cast target instant or sorcery card from your graveyard this turn. If that card would be put into your graveyard this turn, exile it instead.        
         String jTelepath = "Jace, Telepath Unbound"; // 5 loyalty
-        
+
         // Sorcery, Suspend 4 {U}. Target player draws three cards.
-        String ancestralVision = "Ancestral Vision"; 
-        
+        String ancestralVision = "Ancestral Vision";
+
         addCard(Zone.BATTLEFIELD, playerA, "Jace, Vryn's Prodigy", 1); // {U}{1} - 0/2
         addCard(Zone.BATTLEFIELD, playerA, "Island");
         addCard(Zone.GRAVEYARD, playerA, "Island", 4);
         addCard(Zone.GRAVEYARD, playerA, ancestralVision);
         addCard(Zone.HAND, playerA, "Swamp", 1);
-        
+
         activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Draw a card, then discard a card. If there are five or more cards in your graveyard");
         setChoice(playerA, "Swamp");
         activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "-3:");
         addTarget(playerA, ancestralVision);
         castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, ancestralVision);
-        
+
         setStopAt(3, PhaseStep.BEGIN_COMBAT);
         execute();
-        
+
         assertPermanentCount(playerA, jTelepath, 1);
         assertGraveyardCount(playerA, "Swamp", 1);
         assertGraveyardCount(playerA, ancestralVision, 1);

From 3f2a5fc3625f19c2d135338c289730dab8ef941b Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Sat, 20 Jun 2020 11:36:13 +0400
Subject: [PATCH 429/586] Prepare new release

---
 Mage.Client/pom.xml                                         | 2 +-
 Mage.Common/pom.xml                                         | 2 +-
 Mage.Common/src/main/java/mage/utils/MageVersion.java       | 6 +++---
 Mage.Plugins/Mage.Counter.Plugin/pom.xml                    | 2 +-
 Mage.Plugins/pom.xml                                        | 2 +-
 Mage.Server.Console/pom.xml                                 | 2 +-
 Mage.Server.Plugins/Mage.Deck.Constructed/pom.xml           | 2 +-
 Mage.Server.Plugins/Mage.Deck.Limited/pom.xml               | 2 +-
 Mage.Server.Plugins/Mage.Game.BrawlDuel/pom.xml             | 2 +-
 Mage.Server.Plugins/Mage.Game.BrawlFreeForAll/pom.xml       | 2 +-
 .../Mage.Game.CanadianHighlanderDuel/pom.xml                | 2 +-
 Mage.Server.Plugins/Mage.Game.CommanderDuel/pom.xml         | 2 +-
 Mage.Server.Plugins/Mage.Game.CommanderFreeForAll/pom.xml   | 2 +-
 Mage.Server.Plugins/Mage.Game.FreeForAll/pom.xml            | 2 +-
 Mage.Server.Plugins/Mage.Game.FreeformCommanderDuel/pom.xml | 2 +-
 .../Mage.Game.FreeformCommanderFreeForAll/pom.xml           | 2 +-
 .../Mage.Game.FreeformUnlimitedCommander/pom.xml            | 4 ++--
 Mage.Server.Plugins/Mage.Game.MomirDuel/pom.xml             | 2 +-
 Mage.Server.Plugins/Mage.Game.MomirGame/pom.xml             | 2 +-
 Mage.Server.Plugins/Mage.Game.OathbreakerDuel/pom.xml       | 4 ++--
 Mage.Server.Plugins/Mage.Game.OathbreakerFreeForAll/pom.xml | 2 +-
 .../Mage.Game.PennyDreadfulCommanderFreeForAll/pom.xml      | 2 +-
 Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/pom.xml       | 2 +-
 Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/pom.xml         | 2 +-
 Mage.Server.Plugins/Mage.Player.AI.DraftBot/pom.xml         | 2 +-
 Mage.Server.Plugins/Mage.Player.AI.MA/pom.xml               | 2 +-
 Mage.Server.Plugins/Mage.Player.AI/pom.xml                  | 2 +-
 Mage.Server.Plugins/Mage.Player.AIMCTS/pom.xml              | 2 +-
 Mage.Server.Plugins/Mage.Player.AIMinimax/pom.xml           | 2 +-
 Mage.Server.Plugins/Mage.Player.Human/pom.xml               | 2 +-
 Mage.Server.Plugins/Mage.Tournament.BoosterDraft/pom.xml    | 2 +-
 Mage.Server.Plugins/Mage.Tournament.Constructed/pom.xml     | 2 +-
 Mage.Server.Plugins/Mage.Tournament.Sealed/pom.xml          | 2 +-
 Mage.Server.Plugins/pom.xml                                 | 2 +-
 Mage.Server/pom.xml                                         | 2 +-
 Mage.Sets/pom.xml                                           | 2 +-
 Mage.Tests/pom.xml                                          | 2 +-
 Mage.Verify/pom.xml                                         | 4 ++--
 Mage/pom.xml                                                | 2 +-
 .../src/main/java/mage/cards/repository/CardRepository.java | 2 +-
 pom.xml                                                     | 4 ++--
 41 files changed, 47 insertions(+), 47 deletions(-)

diff --git a/Mage.Client/pom.xml b/Mage.Client/pom.xml
index 05f3a8cc69..c42d5d1e48 100644
--- a/Mage.Client/pom.xml
+++ b/Mage.Client/pom.xml
@@ -6,7 +6,7 @@
     <parent>
         <groupId>org.mage</groupId>
         <artifactId>mage-root</artifactId>
-        <version>1.4.42</version>
+        <version>1.4.43</version>
     </parent>
 
     <artifactId>mage-client</artifactId>
diff --git a/Mage.Common/pom.xml b/Mage.Common/pom.xml
index 79575ccf25..23a14eed63 100644
--- a/Mage.Common/pom.xml
+++ b/Mage.Common/pom.xml
@@ -7,7 +7,7 @@
     <parent>
         <groupId>org.mage</groupId>
         <artifactId>mage-root</artifactId>
-        <version>1.4.42</version>
+        <version>1.4.43</version>
     </parent>
 
     <artifactId>mage-common</artifactId>
diff --git a/Mage.Common/src/main/java/mage/utils/MageVersion.java b/Mage.Common/src/main/java/mage/utils/MageVersion.java
index 6aebdad1fb..a2322ca010 100644
--- a/Mage.Common/src/main/java/mage/utils/MageVersion.java
+++ b/Mage.Common/src/main/java/mage/utils/MageVersion.java
@@ -11,11 +11,11 @@ public class MageVersion implements Serializable, Comparable<MageVersion> {
 
     public static final int MAGE_VERSION_MAJOR = 1;
     public static final int MAGE_VERSION_MINOR = 4;
-    public static final int MAGE_VERSION_PATCH = 42;
+    public static final int MAGE_VERSION_PATCH = 43;
     public static final String MAGE_EDITION_INFO = ""; // set "-beta2" for 1.4.32V1-beta2
-    public static final String MAGE_VERSION_MINOR_PATCH = "V7"; // default
+    public static final String MAGE_VERSION_MINOR_PATCH = "V0"; // default
     // strict mode
-    private static final boolean MAGE_VERSION_MINOR_PATCH_MUST_BE_SAME = true; // set true on uncompatible github changes, set false after new major release (after MAGE_VERSION_PATCH changes)
+    private static final boolean MAGE_VERSION_MINOR_PATCH_MUST_BE_SAME = false; // set true on uncompatible github changes, set false after new major release (after MAGE_VERSION_PATCH changes)
 
     public static final boolean MAGE_VERSION_SHOW_BUILD_TIME = true;
     private final int major;
diff --git a/Mage.Plugins/Mage.Counter.Plugin/pom.xml b/Mage.Plugins/Mage.Counter.Plugin/pom.xml
index 813af4df7e..cc8dfb0075 100644
--- a/Mage.Plugins/Mage.Counter.Plugin/pom.xml
+++ b/Mage.Plugins/Mage.Counter.Plugin/pom.xml
@@ -7,7 +7,7 @@
     <parent>
         <groupId>org.mage</groupId>
         <artifactId>mage-plugins</artifactId>
-        <version>1.4.42</version>
+        <version>1.4.43</version>
     </parent>
 
     <artifactId>mage-counter-plugin</artifactId>
diff --git a/Mage.Plugins/pom.xml b/Mage.Plugins/pom.xml
index 7ec674a862..41f63af798 100644
--- a/Mage.Plugins/pom.xml
+++ b/Mage.Plugins/pom.xml
@@ -7,7 +7,7 @@
     <parent>
         <groupId>org.mage</groupId>
         <artifactId>mage-root</artifactId>
-        <version>1.4.42</version>
+        <version>1.4.43</version>
     </parent>
 
     <artifactId>mage-plugins</artifactId>
diff --git a/Mage.Server.Console/pom.xml b/Mage.Server.Console/pom.xml
index 4102763af4..0861e00116 100644
--- a/Mage.Server.Console/pom.xml
+++ b/Mage.Server.Console/pom.xml
@@ -6,7 +6,7 @@
     <parent>
         <groupId>org.mage</groupId>
         <artifactId>mage-root</artifactId>
-        <version>1.4.42</version>
+        <version>1.4.43</version>
     </parent>
 
     <artifactId>mage.server.console</artifactId>
diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/pom.xml b/Mage.Server.Plugins/Mage.Deck.Constructed/pom.xml
index 05f4f1e9be..aade95e9b6 100644
--- a/Mage.Server.Plugins/Mage.Deck.Constructed/pom.xml
+++ b/Mage.Server.Plugins/Mage.Deck.Constructed/pom.xml
@@ -7,7 +7,7 @@
     <parent>
         <groupId>org.mage</groupId>
         <artifactId>mage-server-plugins</artifactId>
-        <version>1.4.42</version>
+        <version>1.4.43</version>
     </parent>
 
     <artifactId>mage-deck-constructed</artifactId>
diff --git a/Mage.Server.Plugins/Mage.Deck.Limited/pom.xml b/Mage.Server.Plugins/Mage.Deck.Limited/pom.xml
index 2c925c27b6..f9dd5c8877 100644
--- a/Mage.Server.Plugins/Mage.Deck.Limited/pom.xml
+++ b/Mage.Server.Plugins/Mage.Deck.Limited/pom.xml
@@ -7,7 +7,7 @@
     <parent>
         <groupId>org.mage</groupId>
         <artifactId>mage-server-plugins</artifactId>
-        <version>1.4.42</version>
+        <version>1.4.43</version>
     </parent>
 
     <artifactId>mage-deck-limited</artifactId>
diff --git a/Mage.Server.Plugins/Mage.Game.BrawlDuel/pom.xml b/Mage.Server.Plugins/Mage.Game.BrawlDuel/pom.xml
index 7c67eda5fc..84886ebf37 100644
--- a/Mage.Server.Plugins/Mage.Game.BrawlDuel/pom.xml
+++ b/Mage.Server.Plugins/Mage.Game.BrawlDuel/pom.xml
@@ -7,7 +7,7 @@
     <parent>
         <groupId>org.mage</groupId>
         <artifactId>mage-server-plugins</artifactId>
-        <version>1.4.42</version>
+        <version>1.4.43</version>
     </parent>
 
     <artifactId>mage-game-brawlduel</artifactId>
diff --git a/Mage.Server.Plugins/Mage.Game.BrawlFreeForAll/pom.xml b/Mage.Server.Plugins/Mage.Game.BrawlFreeForAll/pom.xml
index 82682897fb..18e0a90e94 100644
--- a/Mage.Server.Plugins/Mage.Game.BrawlFreeForAll/pom.xml
+++ b/Mage.Server.Plugins/Mage.Game.BrawlFreeForAll/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>org.mage</groupId>
     <artifactId>mage-server-plugins</artifactId>
-    <version>1.4.42</version>
+    <version>1.4.43</version>
   </parent>
 
     <artifactId>mage-game-brawlfreeforall</artifactId>
diff --git a/Mage.Server.Plugins/Mage.Game.CanadianHighlanderDuel/pom.xml b/Mage.Server.Plugins/Mage.Game.CanadianHighlanderDuel/pom.xml
index 374960f567..4659a5482c 100644
--- a/Mage.Server.Plugins/Mage.Game.CanadianHighlanderDuel/pom.xml
+++ b/Mage.Server.Plugins/Mage.Game.CanadianHighlanderDuel/pom.xml
@@ -7,7 +7,7 @@
     <parent>
         <groupId>org.mage</groupId>
         <artifactId>mage-server-plugins</artifactId>
-        <version>1.4.42</version>
+        <version>1.4.43</version>
     </parent>
 
     <artifactId>mage-game-canadianhighlanderduel</artifactId>
diff --git a/Mage.Server.Plugins/Mage.Game.CommanderDuel/pom.xml b/Mage.Server.Plugins/Mage.Game.CommanderDuel/pom.xml
index cc12a371a1..2afdd233c1 100644
--- a/Mage.Server.Plugins/Mage.Game.CommanderDuel/pom.xml
+++ b/Mage.Server.Plugins/Mage.Game.CommanderDuel/pom.xml
@@ -7,7 +7,7 @@
     <parent>
         <groupId>org.mage</groupId>
         <artifactId>mage-server-plugins</artifactId>
-        <version>1.4.42</version>
+        <version>1.4.43</version>
     </parent>
 
     <artifactId>mage-game-commanderduel</artifactId>
diff --git a/Mage.Server.Plugins/Mage.Game.CommanderFreeForAll/pom.xml b/Mage.Server.Plugins/Mage.Game.CommanderFreeForAll/pom.xml
index d7433f0e55..1f1e952ee9 100644
--- a/Mage.Server.Plugins/Mage.Game.CommanderFreeForAll/pom.xml
+++ b/Mage.Server.Plugins/Mage.Game.CommanderFreeForAll/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>org.mage</groupId>
     <artifactId>mage-server-plugins</artifactId>
-    <version>1.4.42</version>
+    <version>1.4.43</version>
   </parent>
 
     <artifactId>mage-game-commanderfreeforall</artifactId>
diff --git a/Mage.Server.Plugins/Mage.Game.FreeForAll/pom.xml b/Mage.Server.Plugins/Mage.Game.FreeForAll/pom.xml
index c515dde4d9..0dc77912dc 100644
--- a/Mage.Server.Plugins/Mage.Game.FreeForAll/pom.xml
+++ b/Mage.Server.Plugins/Mage.Game.FreeForAll/pom.xml
@@ -7,7 +7,7 @@
     <parent>
         <groupId>org.mage</groupId>
         <artifactId>mage-server-plugins</artifactId>
-        <version>1.4.42</version>
+        <version>1.4.43</version>
     </parent>
 
     <artifactId>mage-game-freeforall</artifactId>
diff --git a/Mage.Server.Plugins/Mage.Game.FreeformCommanderDuel/pom.xml b/Mage.Server.Plugins/Mage.Game.FreeformCommanderDuel/pom.xml
index 8e37dc5115..b6aee93e90 100644
--- a/Mage.Server.Plugins/Mage.Game.FreeformCommanderDuel/pom.xml
+++ b/Mage.Server.Plugins/Mage.Game.FreeformCommanderDuel/pom.xml
@@ -7,7 +7,7 @@
     <parent>
         <groupId>org.mage</groupId>
         <artifactId>mage-server-plugins</artifactId>
-        <version>1.4.42</version>
+        <version>1.4.43</version>
     </parent>
 
     <artifactId>mage-game-freeformcommanderduel</artifactId>
diff --git a/Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/pom.xml b/Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/pom.xml
index d2fd9950d9..ec78b087ef 100644
--- a/Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/pom.xml
+++ b/Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>org.mage</groupId>
     <artifactId>mage-server-plugins</artifactId>
-    <version>1.4.42</version>
+    <version>1.4.43</version>
   </parent>
 
     <artifactId>mage-game-freeformcommanderfreeforall</artifactId>
diff --git a/Mage.Server.Plugins/Mage.Game.FreeformUnlimitedCommander/pom.xml b/Mage.Server.Plugins/Mage.Game.FreeformUnlimitedCommander/pom.xml
index 249c124fa2..d9994d430a 100644
--- a/Mage.Server.Plugins/Mage.Game.FreeformUnlimitedCommander/pom.xml
+++ b/Mage.Server.Plugins/Mage.Game.FreeformUnlimitedCommander/pom.xml
@@ -7,7 +7,7 @@
     <parent>
         <groupId>org.mage</groupId>
         <artifactId>mage-server-plugins</artifactId>
-        <version>1.4.42</version>
+        <version>1.4.43</version>
     </parent>
 
     <artifactId>mage-game-freeformunlimitedcommander</artifactId>
@@ -23,7 +23,7 @@
         <dependency>
             <groupId>org.mage</groupId>
             <artifactId>mage-game-freeformcommanderfreeforall</artifactId>
-            <version>1.4.42</version>
+            <version>1.4.43</version>
             <scope>compile</scope>
         </dependency>
     </dependencies>
diff --git a/Mage.Server.Plugins/Mage.Game.MomirDuel/pom.xml b/Mage.Server.Plugins/Mage.Game.MomirDuel/pom.xml
index f82f22af9d..2d77de5a0c 100644
--- a/Mage.Server.Plugins/Mage.Game.MomirDuel/pom.xml
+++ b/Mage.Server.Plugins/Mage.Game.MomirDuel/pom.xml
@@ -7,7 +7,7 @@
     <parent>
         <groupId>org.mage</groupId>
         <artifactId>mage-server-plugins</artifactId>
-        <version>1.4.42</version>
+        <version>1.4.43</version>
     </parent>
     
     <artifactId>mage-game-momirduel</artifactId>
diff --git a/Mage.Server.Plugins/Mage.Game.MomirGame/pom.xml b/Mage.Server.Plugins/Mage.Game.MomirGame/pom.xml
index 845d640990..e5badb6ddd 100644
--- a/Mage.Server.Plugins/Mage.Game.MomirGame/pom.xml
+++ b/Mage.Server.Plugins/Mage.Game.MomirGame/pom.xml
@@ -7,7 +7,7 @@
     <parent>
         <groupId>org.mage</groupId>
         <artifactId>mage-server-plugins</artifactId>
-        <version>1.4.42</version>
+        <version>1.4.43</version>
     </parent>
     
     <artifactId>mage-game-momirfreeforall</artifactId>
diff --git a/Mage.Server.Plugins/Mage.Game.OathbreakerDuel/pom.xml b/Mage.Server.Plugins/Mage.Game.OathbreakerDuel/pom.xml
index 269f85d71b..24e0fe00b0 100644
--- a/Mage.Server.Plugins/Mage.Game.OathbreakerDuel/pom.xml
+++ b/Mage.Server.Plugins/Mage.Game.OathbreakerDuel/pom.xml
@@ -6,7 +6,7 @@
     <parent>
         <groupId>org.mage</groupId>
         <artifactId>mage-server-plugins</artifactId>
-        <version>1.4.42</version>
+        <version>1.4.43</version>
     </parent>
 
     <artifactId>mage-game-oathbreakerduel</artifactId>
@@ -22,7 +22,7 @@
         <dependency>
             <groupId>org.mage</groupId>
             <artifactId>mage-game-oathbreakerfreeforall</artifactId>
-            <version>1.4.42</version>
+            <version>1.4.43</version>
             <scope>compile</scope>
         </dependency>
     </dependencies>
diff --git a/Mage.Server.Plugins/Mage.Game.OathbreakerFreeForAll/pom.xml b/Mage.Server.Plugins/Mage.Game.OathbreakerFreeForAll/pom.xml
index 30bcec9cba..73bbd699bf 100644
--- a/Mage.Server.Plugins/Mage.Game.OathbreakerFreeForAll/pom.xml
+++ b/Mage.Server.Plugins/Mage.Game.OathbreakerFreeForAll/pom.xml
@@ -7,7 +7,7 @@
     <parent>
         <groupId>org.mage</groupId>
         <artifactId>mage-server-plugins</artifactId>
-        <version>1.4.42</version>
+        <version>1.4.43</version>
     </parent>
 
     <artifactId>mage-game-oathbreakerfreeforall</artifactId>
diff --git a/Mage.Server.Plugins/Mage.Game.PennyDreadfulCommanderFreeForAll/pom.xml b/Mage.Server.Plugins/Mage.Game.PennyDreadfulCommanderFreeForAll/pom.xml
index 4790c78bea..de80e55dea 100644
--- a/Mage.Server.Plugins/Mage.Game.PennyDreadfulCommanderFreeForAll/pom.xml
+++ b/Mage.Server.Plugins/Mage.Game.PennyDreadfulCommanderFreeForAll/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>org.mage</groupId>
     <artifactId>mage-server-plugins</artifactId>
-    <version>1.4.42</version>
+    <version>1.4.43</version>
   </parent>
 
     <artifactId>mage-game-pennydreadfulcommanderfreeforall</artifactId>
diff --git a/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/pom.xml b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/pom.xml
index 1ae9001fe4..81774dffc7 100644
--- a/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/pom.xml
+++ b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/pom.xml
@@ -7,7 +7,7 @@
     <parent>
         <groupId>org.mage</groupId>
         <artifactId>mage-server-plugins</artifactId>
-        <version>1.4.42</version>
+        <version>1.4.43</version>
     </parent>
     
     <artifactId>mage-game-tinyleadersduel</artifactId>
diff --git a/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/pom.xml b/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/pom.xml
index c8e14f9687..3a6ed3b173 100644
--- a/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/pom.xml
+++ b/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/pom.xml
@@ -7,7 +7,7 @@
     <parent>
         <groupId>org.mage</groupId>
         <artifactId>mage-server-plugins</artifactId>
-        <version>1.4.42</version>
+        <version>1.4.43</version>
     </parent>
 
     <artifactId>mage-game-twoplayerduel</artifactId>
diff --git a/Mage.Server.Plugins/Mage.Player.AI.DraftBot/pom.xml b/Mage.Server.Plugins/Mage.Player.AI.DraftBot/pom.xml
index b46e30e798..c2d00d5150 100644
--- a/Mage.Server.Plugins/Mage.Player.AI.DraftBot/pom.xml
+++ b/Mage.Server.Plugins/Mage.Player.AI.DraftBot/pom.xml
@@ -7,7 +7,7 @@
     <parent>
         <groupId>org.mage</groupId>
         <artifactId>mage-server-plugins</artifactId>
-        <version>1.4.42</version>
+        <version>1.4.43</version>
     </parent>
 
     <artifactId>mage-player-ai-draftbot</artifactId>
diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/pom.xml b/Mage.Server.Plugins/Mage.Player.AI.MA/pom.xml
index b25b4bbeab..0c278dc99d 100644
--- a/Mage.Server.Plugins/Mage.Player.AI.MA/pom.xml
+++ b/Mage.Server.Plugins/Mage.Player.AI.MA/pom.xml
@@ -7,7 +7,7 @@
     <parent>
         <groupId>org.mage</groupId>
         <artifactId>mage-server-plugins</artifactId>
-        <version>1.4.42</version>
+        <version>1.4.43</version>
     </parent>
 
     <artifactId>mage-player-ai-ma</artifactId>
diff --git a/Mage.Server.Plugins/Mage.Player.AI/pom.xml b/Mage.Server.Plugins/Mage.Player.AI/pom.xml
index aa2e2c2d44..1a32f8a06b 100644
--- a/Mage.Server.Plugins/Mage.Player.AI/pom.xml
+++ b/Mage.Server.Plugins/Mage.Player.AI/pom.xml
@@ -7,7 +7,7 @@
     <parent>
         <groupId>org.mage</groupId>
         <artifactId>mage-server-plugins</artifactId>
-        <version>1.4.42</version>
+        <version>1.4.43</version>
     </parent>
 
     <artifactId>mage-player-ai</artifactId>
diff --git a/Mage.Server.Plugins/Mage.Player.AIMCTS/pom.xml b/Mage.Server.Plugins/Mage.Player.AIMCTS/pom.xml
index cbaf2a5aad..c7fccdb2f2 100644
--- a/Mage.Server.Plugins/Mage.Player.AIMCTS/pom.xml
+++ b/Mage.Server.Plugins/Mage.Player.AIMCTS/pom.xml
@@ -7,7 +7,7 @@
     <parent>
         <groupId>org.mage</groupId>
         <artifactId>mage-server-plugins</artifactId>
-        <version>1.4.42</version>
+        <version>1.4.43</version>
     </parent>
 
     <artifactId>mage-player-ai-mcts</artifactId>
diff --git a/Mage.Server.Plugins/Mage.Player.AIMinimax/pom.xml b/Mage.Server.Plugins/Mage.Player.AIMinimax/pom.xml
index 619b2d17ad..029acd82d3 100644
--- a/Mage.Server.Plugins/Mage.Player.AIMinimax/pom.xml
+++ b/Mage.Server.Plugins/Mage.Player.AIMinimax/pom.xml
@@ -7,7 +7,7 @@
     <parent>
         <groupId>org.mage</groupId>
         <artifactId>mage-server-plugins</artifactId>
-        <version>1.4.42</version>
+        <version>1.4.43</version>
     </parent>
 
     <artifactId>mage-player-aiminimax</artifactId>
diff --git a/Mage.Server.Plugins/Mage.Player.Human/pom.xml b/Mage.Server.Plugins/Mage.Player.Human/pom.xml
index ab624dec8c..ff5d7e2649 100644
--- a/Mage.Server.Plugins/Mage.Player.Human/pom.xml
+++ b/Mage.Server.Plugins/Mage.Player.Human/pom.xml
@@ -7,7 +7,7 @@
     <parent>
         <groupId>org.mage</groupId>
         <artifactId>mage-server-plugins</artifactId>
-        <version>1.4.42</version>
+        <version>1.4.43</version>
     </parent>
 
     <artifactId>mage-player-human</artifactId>
diff --git a/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/pom.xml b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/pom.xml
index 32f5eadeb1..ee04e76d77 100644
--- a/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/pom.xml
+++ b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/pom.xml
@@ -7,7 +7,7 @@
     <parent>
         <groupId>org.mage</groupId>
         <artifactId>mage-server-plugins</artifactId>
-        <version>1.4.42</version>
+        <version>1.4.43</version>
     </parent>
 
     <artifactId>mage-tournament-boosterdraft</artifactId>
diff --git a/Mage.Server.Plugins/Mage.Tournament.Constructed/pom.xml b/Mage.Server.Plugins/Mage.Tournament.Constructed/pom.xml
index 8fa3ceae29..826e1af68d 100644
--- a/Mage.Server.Plugins/Mage.Tournament.Constructed/pom.xml
+++ b/Mage.Server.Plugins/Mage.Tournament.Constructed/pom.xml
@@ -7,7 +7,7 @@
   <parent>
     <groupId>org.mage</groupId>
     <artifactId>mage-server-plugins</artifactId>
-    <version>1.4.42</version>
+    <version>1.4.43</version>
   </parent>
 
   <artifactId>mage-tournament-constructed</artifactId>
diff --git a/Mage.Server.Plugins/Mage.Tournament.Sealed/pom.xml b/Mage.Server.Plugins/Mage.Tournament.Sealed/pom.xml
index 3530951a0d..aa062a8a45 100644
--- a/Mage.Server.Plugins/Mage.Tournament.Sealed/pom.xml
+++ b/Mage.Server.Plugins/Mage.Tournament.Sealed/pom.xml
@@ -7,7 +7,7 @@
     <parent>
         <groupId>org.mage</groupId>
         <artifactId>mage-server-plugins</artifactId>
-        <version>1.4.42</version>
+        <version>1.4.43</version>
     </parent>
 
     <artifactId>mage-tournament-sealed</artifactId>
diff --git a/Mage.Server.Plugins/pom.xml b/Mage.Server.Plugins/pom.xml
index c449b61d49..bc32dd3724 100644
--- a/Mage.Server.Plugins/pom.xml
+++ b/Mage.Server.Plugins/pom.xml
@@ -7,7 +7,7 @@
     <parent>
         <groupId>org.mage</groupId>
         <artifactId>mage-root</artifactId>
-        <version>1.4.42</version>
+        <version>1.4.43</version>
     </parent>
 
     <artifactId>mage-server-plugins</artifactId>
diff --git a/Mage.Server/pom.xml b/Mage.Server/pom.xml
index 318db625ad..dfb4fb958a 100644
--- a/Mage.Server/pom.xml
+++ b/Mage.Server/pom.xml
@@ -6,7 +6,7 @@
     <parent>
         <groupId>org.mage</groupId>
         <artifactId>mage-root</artifactId>
-        <version>1.4.42</version>
+        <version>1.4.43</version>
     </parent>
 
     <artifactId>mage-server</artifactId>
diff --git a/Mage.Sets/pom.xml b/Mage.Sets/pom.xml
index e6bb3dc467..e80b1051b4 100644
--- a/Mage.Sets/pom.xml
+++ b/Mage.Sets/pom.xml
@@ -7,7 +7,7 @@
     <parent>
         <groupId>org.mage</groupId>
         <artifactId>mage-root</artifactId>
-        <version>1.4.42</version>
+        <version>1.4.43</version>
     </parent>
 
     <artifactId>mage-sets</artifactId>
diff --git a/Mage.Tests/pom.xml b/Mage.Tests/pom.xml
index 7b61c3be4c..9677c152e6 100644
--- a/Mage.Tests/pom.xml
+++ b/Mage.Tests/pom.xml
@@ -6,7 +6,7 @@
     <parent>
         <groupId>org.mage</groupId>
         <artifactId>mage-root</artifactId>
-        <version>1.4.42</version>
+        <version>1.4.43</version>
     </parent> 
 
     <artifactId>mage-tests</artifactId>
diff --git a/Mage.Verify/pom.xml b/Mage.Verify/pom.xml
index 43b3ce0a3b..a00c7026e7 100644
--- a/Mage.Verify/pom.xml
+++ b/Mage.Verify/pom.xml
@@ -6,7 +6,7 @@
     <parent>
         <groupId>org.mage</groupId>
         <artifactId>mage-root</artifactId>
-        <version>1.4.42</version>
+        <version>1.4.43</version>
     </parent>
 
     <artifactId>mage-verify</artifactId>
@@ -49,7 +49,7 @@
         <dependency>
             <groupId>org.mage</groupId>
             <artifactId>mage-client</artifactId>
-            <version>1.4.42</version>
+            <version>1.4.43</version>
         </dependency>
 
     </dependencies>
diff --git a/Mage/pom.xml b/Mage/pom.xml
index 3a5b142e25..4ea9bb926d 100644
--- a/Mage/pom.xml
+++ b/Mage/pom.xml
@@ -6,7 +6,7 @@
     <parent>
         <groupId>org.mage</groupId>
         <artifactId>mage-root</artifactId>
-        <version>1.4.42</version>
+        <version>1.4.43</version>
     </parent>
 
     <artifactId>mage</artifactId>
diff --git a/Mage/src/main/java/mage/cards/repository/CardRepository.java b/Mage/src/main/java/mage/cards/repository/CardRepository.java
index d77c31bd1a..86302bd990 100644
--- a/Mage/src/main/java/mage/cards/repository/CardRepository.java
+++ b/Mage/src/main/java/mage/cards/repository/CardRepository.java
@@ -35,7 +35,7 @@ public enum CardRepository {
     // raise this if db structure was changed
     private static final long CARD_DB_VERSION = 52;
     // raise this if new cards were added to the server
-    private static final long CARD_CONTENT_VERSION = 229;
+    private static final long CARD_CONTENT_VERSION = 230;
     private Dao<CardInfo, Object> cardDao;
     private Set<String> classNames;
     private RepositoryEventSource eventSource = new RepositoryEventSource();
diff --git a/pom.xml b/pom.xml
index cee6748623..1f95f7fa8b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
 
     <groupId>org.mage</groupId>
     <artifactId>mage-root</artifactId>
-    <version>1.4.42</version>
+    <version>1.4.43</version>
     <packaging>pom</packaging>
     <name>Mage Root</name>
     <description>Mage Root POM</description>
@@ -86,7 +86,7 @@
     </repositories>
 
     <properties>
-        <mage-version>1.4.42</mage-version>
+        <mage-version>1.4.43</mage-version>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <maven.build.timestamp.format>yyyy-MM-dd'T'HH:mm:ss'Z'</maven.build.timestamp.format>
     </properties>

From a06dae2270698a8e5500ea683bfe3c01f8fffae1 Mon Sep 17 00:00:00 2001
From: gp66 <garyprince66@hotmail.com>
Date: Sat, 20 Jun 2020 13:58:13 -0500
Subject: [PATCH 430/586] cub warden lifelink tokens

---
 Mage.Sets/src/mage/cards/c/Cubwarden.java | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/c/Cubwarden.java b/Mage.Sets/src/mage/cards/c/Cubwarden.java
index 91897e4f14..0d5daa788d 100644
--- a/Mage.Sets/src/mage/cards/c/Cubwarden.java
+++ b/Mage.Sets/src/mage/cards/c/Cubwarden.java
@@ -9,7 +9,7 @@ import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.SubType;
-import mage.game.permanent.token.CatToken;
+import mage.game.permanent.token.CatToken2;
 
 import java.util.UUID;
 
@@ -32,7 +32,7 @@ public final class Cubwarden extends CardImpl {
         this.addAbility(LifelinkAbility.getInstance());
 
         // Whenever this creature mutates, create two 1/1 white Cat creature tokens with lifelink.
-        this.addAbility(new MutatesSourceTriggeredAbility(new CreateTokenEffect(new CatToken(), 2)));
+        this.addAbility(new MutatesSourceTriggeredAbility(new CreateTokenEffect(new CatToken2(), 2)));
     }
 
     private Cubwarden(final Cubwarden card) {

From 23cb5274f54ddbb53c64c1cdf20228687c4315d1 Mon Sep 17 00:00:00 2001
From: gp66 <garyprince66@hotmail.com>
Date: Sat, 20 Jun 2020 14:11:31 -0500
Subject: [PATCH 431/586] cat lifelink token

---
 Mage.Client/src/main/resources/card-pictures-tok.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Mage.Client/src/main/resources/card-pictures-tok.txt b/Mage.Client/src/main/resources/card-pictures-tok.txt
index d259a41e0a..1a6c7b6ebe 100644
--- a/Mage.Client/src/main/resources/card-pictures-tok.txt
+++ b/Mage.Client/src/main/resources/card-pictures-tok.txt
@@ -1372,7 +1372,7 @@
 # IKO
 |Generate|TOK:IKO|Beast|||BeastToken|
 |Generate|TOK:IKO|Cat Bird|||CatBirdToken|
-|Generate|TOK:IKO|Cat|||CatToken|
+|Generate|TOK:IKO|Cat|||CatToken2|
 |Generate|TOK:IKO|Dinosaur Beast|||DinosaurBeastToken|
 |Generate|TOK:IKO|Dinosaur|||DinosaurHasteToken|
 |Generate|TOK:IKO|Feather|||FeatherToken|

From b538aea84801787ae241dc8c2c1c104a47d99302 Mon Sep 17 00:00:00 2001
From: gp66 <garyprince66@hotmail.com>
Date: Sat, 20 Jun 2020 14:26:01 -0500
Subject: [PATCH 432/586] beast token iko

---
 Mage/src/main/java/mage/game/permanent/token/BeastToken.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Mage/src/main/java/mage/game/permanent/token/BeastToken.java b/Mage/src/main/java/mage/game/permanent/token/BeastToken.java
index 50727e81da..8e3a85a3bb 100644
--- a/Mage/src/main/java/mage/game/permanent/token/BeastToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/BeastToken.java
@@ -16,7 +16,7 @@ public final class BeastToken extends TokenImpl {
     static final private List<String> tokenImageSets = new ArrayList<>();
 
     static {
-        tokenImageSets.addAll(Arrays.asList("C14", "LRW", "M15", "M14", "DDL", "M13", "M12", "DD3GVL", "NPH", "M11", "M10", "EVE", "MM3", "CMA", "E01", "C19"));
+        tokenImageSets.addAll(Arrays.asList("C14", "LRW", "M15", "M14", "DDL", "M13", "M12", "DD3GVL", "NPH", "M11", "M10", "EVE", "MM3", "CMA", "E01", "C19", "IKO"));
     }
 
     public BeastToken() {

From 313829c82623105630eae55487806bd58cda4194 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Sun, 21 Jun 2020 09:10:33 +0200
Subject: [PATCH 433/586] * Elite Scaleguard - Fixed not working triggered
 ability (fixes #6689).

---
 .../src/mage/cards/e/EliteScaleguard.java     | 50 +++++-----
 .../cards/abilities/keywords/BolsterTest.java | 94 +++++++++++++++++++
 2 files changed, 115 insertions(+), 29 deletions(-)
 create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BolsterTest.java

diff --git a/Mage.Sets/src/mage/cards/e/EliteScaleguard.java b/Mage.Sets/src/mage/cards/e/EliteScaleguard.java
index b2c5911d85..02a7c48ef0 100644
--- a/Mage.Sets/src/mage/cards/e/EliteScaleguard.java
+++ b/Mage.Sets/src/mage/cards/e/EliteScaleguard.java
@@ -1,10 +1,11 @@
-
 package mage.cards.e;
 
+import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.AttacksCreatureYouControlTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.TapTargetEffect;
 import mage.abilities.effects.keyword.BolsterEffect;
 import mage.cards.CardImpl;
@@ -14,13 +15,12 @@ import mage.constants.SubType;
 import mage.counters.CounterType;
 import mage.filter.common.FilterControlledCreaturePermanent;
 import mage.filter.common.FilterCreaturePermanent;
+import mage.filter.predicate.permanent.ControllerIdPredicate;
 import mage.filter.predicate.permanent.CounterPredicate;
-import mage.filter.predicate.permanent.DefendingPlayerControlsPredicate;
 import mage.game.Game;
-import mage.game.permanent.Permanent;
 import mage.target.common.TargetCreaturePermanent;
-
-import java.util.UUID;
+import mage.target.targetadjustment.TargetAdjuster;
+import mage.target.targetpointer.FirstTargetPointer;
 
 /**
  * @author emerald000
@@ -28,11 +28,9 @@ import java.util.UUID;
 public final class EliteScaleguard extends CardImpl {
 
     private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("creature you control with a +1/+1 counter on it");
-    private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent("creature defending player controls");
 
     static {
         filter.add(new CounterPredicate(CounterType.P1P1));
-        filter2.add(DefendingPlayerControlsPredicate.instance);
     }
 
     public EliteScaleguard(UUID ownerId, CardSetInfo setInfo) {
@@ -46,8 +44,9 @@ public final class EliteScaleguard extends CardImpl {
         this.addAbility(new EntersBattlefieldTriggeredAbility(new BolsterEffect(2)));
 
         // Whenever a creature you control with a +1/+1 counter on it attacks, tap target creature defending player controls.
-        Ability ability = new AttacksCreatureYouControlTriggeredAbility(new EliteScaleguardTapEffect(), false, filter, true);
-        ability.addTarget(new TargetCreaturePermanent(filter2));
+        Ability ability = new AttacksCreatureYouControlTriggeredAbility(new TapTargetEffect(), false, filter, true);
+        ability.addTarget(new TargetCreaturePermanent(new FilterCreaturePermanent("creature defending player controls")));
+        ability.setTargetAdjuster(EliteScaleguardTargetAdjuster.instance);
         this.addAbility(ability);
     }
 
@@ -61,28 +60,21 @@ public final class EliteScaleguard extends CardImpl {
     }
 }
 
-class EliteScaleguardTapEffect extends TapTargetEffect {
-
-    EliteScaleguardTapEffect() {
-        super();
-    }
-
-    EliteScaleguardTapEffect(final EliteScaleguardTapEffect effect) {
-        super(effect);
-    }
+enum EliteScaleguardTargetAdjuster implements TargetAdjuster {
+    instance;
 
     @Override
-    public EliteScaleguardTapEffect copy() {
-        return new EliteScaleguardTapEffect(this);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        Permanent permanent = game.getPermanent(source.getFirstTarget());
-        if (permanent != null) {
-            permanent.tap(game);
-            return true;
+    public void adjustTargets(Ability ability, Game game) {
+        FilterCreaturePermanent filterDefender = new FilterCreaturePermanent("creature defending player controls");
+        for (Effect effect : ability.getEffects()) {
+            if (effect instanceof TapTargetEffect) {
+                filterDefender.add(new ControllerIdPredicate(game.getCombat().getDefendingPlayerId(effect.getTargetPointer().getFirst(game, ability), game)));
+                effect.setTargetPointer(new FirstTargetPointer());// reset target pointer to first target to tap correct target
+                break;
+            }
         }
-        return false;
+        ability.getTargets().clear();
+        TargetCreaturePermanent target = new TargetCreaturePermanent(filterDefender);
+        ability.addTarget(target);
     }
 }
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BolsterTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BolsterTest.java
new file mode 100644
index 0000000000..c9eb93a740
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BolsterTest.java
@@ -0,0 +1,94 @@
+package org.mage.test.cards.abilities.keywords;
+
+import mage.constants.PhaseStep;
+import mage.constants.Zone;
+import mage.counters.CounterType;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestPlayerBase;
+
+/**
+ *
+ * @author LevelX2
+ */
+public class BolsterTest extends CardTestPlayerBase {
+
+    /**
+     * Tests keyword bolster
+     *
+     * 701.32. Bolster <br>
+     * 701.32a "Bolster N" means “Choose a creature you control with the least
+     * toughness or tied for least toughness among creatures you control. Put N
+     * +1/+1 counters on that creature.”
+     */
+    @Test
+    public void Basic1Test() {
+        // When Elite Scaleguard enters the battlefield, bolster 2.
+        // Whenever a creature you control with a +1/+1 counter on it attacks, tap target creature defending player controls.
+        addCard(Zone.HAND, playerA, "Elite Scaleguard"); // Creature 2/3 {4}{W}
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Elite Scaleguard");
+
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+
+        setStrictChooseMode(true);
+        execute();
+        assertAllCommandsUsed();
+
+        assertCounterCount("Elite Scaleguard", CounterType.P1P1, 2);
+        assertPowerToughness(playerA, "Elite Scaleguard", 4, 5);
+    }
+
+    @Test
+    public void Basic2Test() {
+        setStrictChooseMode(true);
+        // When Elite Scaleguard enters the battlefield, bolster 2.
+        // Whenever a creature you control with a +1/+1 counter on it attacks, tap target creature defending player controls.
+        addCard(Zone.HAND, playerA, "Elite Scaleguard"); // Creature 2/3 {4}{W}
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
+        addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1);
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Elite Scaleguard");
+
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+
+        execute();
+        assertAllCommandsUsed();
+
+        assertPowerToughness(playerA, "Elite Scaleguard", 2, 3);
+        assertCounterCount("Silvercoat Lion", CounterType.P1P1, 2);
+        assertPowerToughness(playerA, "Silvercoat Lion", 4, 4);
+    }
+
+    @Test
+    public void EliteScaleguardTriggerTest() {
+        // When Elite Scaleguard enters the battlefield, bolster 2.
+        // Whenever a creature you control with a +1/+1 counter on it attacks, tap target creature defending player controls.
+        addCard(Zone.HAND, playerA, "Elite Scaleguard"); // Creature 2/3 {4}{W}
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
+        addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1);
+
+        addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox", 1);
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Elite Scaleguard");
+
+        attack(3, playerA, "Silvercoat Lion");
+        addTarget(playerA, "Pillarfield Ox"); // Tap from triggered ability of Elite Scaleguard
+
+        setStopAt(3, PhaseStep.POSTCOMBAT_MAIN);
+
+        setStrictChooseMode(true);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPowerToughness(playerA, "Elite Scaleguard", 2, 3);
+        assertCounterCount("Silvercoat Lion", CounterType.P1P1, 2);
+        assertPowerToughness(playerA, "Silvercoat Lion", 4, 4);
+        assertTapped("Silvercoat Lion", true);
+
+        assertPermanentCount(playerB, "Pillarfield Ox", 1);
+        assertTapped("Pillarfield Ox", true);
+
+        assertLife(playerB, 16);
+    }
+}

From f5f668e65994f971a5205226530450a89d93451b Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Sun, 21 Jun 2020 10:52:16 +0200
Subject: [PATCH 434/586] * Lurrus of the Dream-Den - Fixed its tracking of the
 use of the cast from graveyard ability (fixes #6882).

---
 .../src/mage/cards/l/LurrusOfTheDreamDen.java | 50 +++++++++++--------
 1 file changed, 28 insertions(+), 22 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/l/LurrusOfTheDreamDen.java b/Mage.Sets/src/mage/cards/l/LurrusOfTheDreamDen.java
index 4fbb000772..5500a93a93 100644
--- a/Mage.Sets/src/mage/cards/l/LurrusOfTheDreamDen.java
+++ b/Mage.Sets/src/mage/cards/l/LurrusOfTheDreamDen.java
@@ -1,7 +1,11 @@
 package mage.cards.l;
 
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
 import mage.MageInt;
 import mage.MageObject;
+import mage.MageObjectReference;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.AsThoughEffectImpl;
@@ -16,6 +20,7 @@ import mage.cards.CardSetInfo;
 import mage.constants.*;
 import mage.filter.FilterCard;
 import mage.filter.common.FilterPermanentCard;
+import mage.filter.predicate.Predicates;
 import mage.filter.predicate.mageobject.ConvertedManaCostPredicate;
 import mage.game.Game;
 import mage.game.events.GameEvent;
@@ -24,10 +29,6 @@ import mage.players.Player;
 import mage.target.targetpointer.FixedTarget;
 import mage.watchers.Watcher;
 
-import java.util.Set;
-import java.util.UUID;
-import mage.filter.predicate.Predicates;
-
 /**
  * @author TheElk801
  */
@@ -94,8 +95,8 @@ class LurrusOfTheDreamDenContinuousEffect extends ContinuousEffectImpl {
 
     LurrusOfTheDreamDenContinuousEffect() {
         super(Duration.WhileOnBattlefield, Layer.PlayerEffects, SubLayer.NA, Outcome.Benefit);
-        staticText = "During each of your turns, you may cast one permanent spell " +
-                "with converted mana cost 2 or less from your graveyard";
+        staticText = "During each of your turns, you may cast one permanent spell "
+                + "with converted mana cost 2 or less from your graveyard";
     }
 
     private LurrusOfTheDreamDenContinuousEffect(final LurrusOfTheDreamDenContinuousEffect effect) {
@@ -113,13 +114,16 @@ class LurrusOfTheDreamDenContinuousEffect extends ContinuousEffectImpl {
         if (player == null) {
             return false;
         }
-        if (!game.isActivePlayer(player.getId())) {
-            return false;
-        }
-        for (Card card : player.getGraveyard().getCards(filter, game)) {
-            ContinuousEffect effect = new LurrusOfTheDreamDenCastFromGraveyardEffect();
-            effect.setTargetPointer(new FixedTarget(card, game));
-            game.addEffect(effect, source);
+        if (game.isActivePlayer(player.getId())) {
+            LurrusOfTheDreamDenWatcher watcher = game.getState().getWatcher(LurrusOfTheDreamDenWatcher.class);
+            if (watcher != null && !watcher.isAbilityUsed(new MageObjectReference(source.getSourceId(), game))) {
+                // if not used yet, add effect per card in graveyard to cast it
+                for (Card card : player.getGraveyard().getCards(filter, game)) {
+                    ContinuousEffect effect = new LurrusOfTheDreamDenCastFromGraveyardEffect();
+                    effect.setTargetPointer(new FixedTarget(card, game));
+                    game.addEffect(effect, source);
+                }
+            }
         }
         return true;
     }
@@ -154,38 +158,40 @@ class LurrusOfTheDreamDenCastFromGraveyardEffect extends AsThoughEffectImpl {
         if (!affectedControllerId.equals(source.getControllerId())) {
             return false;
         }
-        LurrusOfTheDreamDenWatcher watcher = game.getState().getWatcher(LurrusOfTheDreamDenWatcher.class, source.getSourceId());
-        return watcher != null && !watcher.isAbilityUsed();
+        LurrusOfTheDreamDenWatcher watcher = game.getState().getWatcher(LurrusOfTheDreamDenWatcher.class);
+        return watcher != null && !watcher.isAbilityUsed(new MageObjectReference(source.getSourceId(), game));
     }
 }
 
 class LurrusOfTheDreamDenWatcher extends Watcher {
 
-    private boolean abilityUsed = false;
+    private final Set<MageObjectReference> abilityUsed = new HashSet<>();
 
     LurrusOfTheDreamDenWatcher() {
-        super(WatcherScope.CARD);
+        super(WatcherScope.GAME);
     }
 
     @Override
     public void watch(GameEvent event, Game game) {
-        if (event.getType() != GameEvent.EventType.SPELL_CAST || event.getZone() != Zone.GRAVEYARD) {
+        if (event.getType() != GameEvent.EventType.SPELL_CAST
+                || event.getZone() != Zone.GRAVEYARD
+                || event.getAdditionalReference() == null) { // permitting source to cast from graveyard
             return;
         }
         Spell spell = game.getSpell(event.getTargetId());
         if (spell == null || !spell.isPermanent() || spell.getConvertedManaCost() > 2) {
             return;
         }
-        abilityUsed = true;
+        abilityUsed.add(event.getAdditionalReference());
     }
 
     @Override
     public void reset() {
         super.reset();
-        abilityUsed = false;
+        abilityUsed.clear();
     }
 
-    boolean isAbilityUsed() {
-        return abilityUsed;
+    boolean isAbilityUsed(MageObjectReference mor) {
+        return abilityUsed.contains(mor);
     }
 }

From 832f1ebcd728ec9c7cb9a529bd41ca0513321104 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Sun, 21 Jun 2020 11:30:30 +0200
Subject: [PATCH 435/586] * Snickering Squirrel - Improved choice dialog text.

---
 Mage.Sets/src/mage/cards/s/SnickeringSquirrel.java | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/s/SnickeringSquirrel.java b/Mage.Sets/src/mage/cards/s/SnickeringSquirrel.java
index bc72bae2dd..8cce1e89f7 100644
--- a/Mage.Sets/src/mage/cards/s/SnickeringSquirrel.java
+++ b/Mage.Sets/src/mage/cards/s/SnickeringSquirrel.java
@@ -1,4 +1,3 @@
-
 package mage.cards.s;
 
 import java.util.UUID;
@@ -55,11 +54,13 @@ class SnickeringSquirrelEffect extends ReplacementEffectImpl {
     @Override
     public boolean replaceEvent(GameEvent event, Ability source, Game game) {
         Player controller = game.getPlayer(source.getControllerId());
-
-        if (controller != null) {
+        Player dieRoller = game.getPlayer(event.getPlayerId());
+        if (controller != null && dieRoller != null) {
             Permanent permanent = game.getPermanent(source.getSourceId());
-            if (permanent != null && permanent.canTap() && !permanent.isTapped()) {
-                if (controller.chooseUse(Outcome.AIDontUseIt, "Do you want to tap this to increase the result of a die any player rolled by 1?", null, "Yes", "No", source, game)) {
+            if (permanent != null && !permanent.isTapped()) {
+                if (controller.chooseUse(Outcome.AIDontUseIt, "Do you want to tap this to increase the result of a die ("
+                        + event.getAmount() + ") "
+                        + dieRoller.getName() + " rolled by 1?", null, "Yes", "No", source, game)) {
                     permanent.tap(game);
                     // ignore planar dies (dice roll amount of planar dies is equal to 0)
                     if (event.getAmount() > 0) {

From 3d2c452e3f51b67ac3bfc34b4862b438398df856 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Sun, 21 Jun 2020 11:31:43 +0200
Subject: [PATCH 436/586] * Pick choice Dialog - Increased default window size.
 Window size is changable now by the user.

---
 .../mage/client/dialog/PickChoiceDialog.form  |  11 +-
 .../mage/client/dialog/PickChoiceDialog.java  | 167 +++++++++---------
 2 files changed, 89 insertions(+), 89 deletions(-)

diff --git a/Mage.Client/src/main/java/mage/client/dialog/PickChoiceDialog.form b/Mage.Client/src/main/java/mage/client/dialog/PickChoiceDialog.form
index 8f4d4356cd..a6cffbba21 100644
--- a/Mage.Client/src/main/java/mage/client/dialog/PickChoiceDialog.form
+++ b/Mage.Client/src/main/java/mage/client/dialog/PickChoiceDialog.form
@@ -1,6 +1,9 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 
 <Form version="1.9" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JInternalFrameFormInfo">
+  <Properties>
+    <Property name="resizable" type="boolean" value="true"/>
+  </Properties>
   <SyntheticProperties>
     <SyntheticProperty name="formSizePolicy" type="int" value="1"/>
   </SyntheticProperties>
@@ -39,7 +42,7 @@
               <EmptySpace max="-2" attributes="0"/>
               <Component id="panelSearch" min="-2" max="-2" attributes="0"/>
               <EmptySpace max="-2" attributes="0"/>
-              <Component id="scrollList" pref="246" max="32767" attributes="0"/>
+              <Component id="scrollList" pref="282" max="32767" attributes="0"/>
               <EmptySpace max="-2" attributes="0"/>
               <Component id="panelCommands" min="-2" max="-2" attributes="0"/>
               <EmptySpace min="-2" max="-2" attributes="0"/>
@@ -55,8 +58,8 @@
           <Group type="103" groupAlignment="0" attributes="0">
               <Group type="102" attributes="0">
                   <Group type="103" groupAlignment="0" attributes="0">
-                      <Component id="labelMessage" alignment="1" pref="210" max="32767" attributes="0"/>
-                      <Component id="labelSubMessage" alignment="1" pref="210" max="32767" attributes="0"/>
+                      <Component id="labelMessage" alignment="1" pref="337" max="32767" attributes="0"/>
+                      <Component id="labelSubMessage" alignment="1" pref="337" max="32767" attributes="0"/>
                   </Group>
                   <EmptySpace min="0" pref="0" max="-2" attributes="0"/>
               </Group>
@@ -203,4 +206,4 @@
       </SubComponents>
     </Container>
   </SubComponents>
-</Form>
\ No newline at end of file
+</Form>
diff --git a/Mage.Client/src/main/java/mage/client/dialog/PickChoiceDialog.java b/Mage.Client/src/main/java/mage/client/dialog/PickChoiceDialog.java
index 2c992a715f..92ba6cac31 100644
--- a/Mage.Client/src/main/java/mage/client/dialog/PickChoiceDialog.java
+++ b/Mage.Client/src/main/java/mage/client/dialog/PickChoiceDialog.java
@@ -1,25 +1,22 @@
 package mage.client.dialog;
 
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import javax.swing.*;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
 import mage.choices.Choice;
 import mage.client.MageFrame;
 import mage.client.util.gui.MageDialogState;
 
-import javax.swing.*;
-import javax.swing.event.DocumentEvent;
-import javax.swing.event.DocumentListener;
-import java.awt.*;
-import java.awt.event.*;
-import java.util.*;
-import java.util.List;
-
 /**
  * @author JayDi85
  */
-
 public class PickChoiceDialog extends MageDialog {
 
     Choice choice;
-    List<KeyValueItem> allItems = new ArrayList<>();
+    java.util.List<KeyValueItem> allItems = new ArrayList<>();
     DefaultListModel<KeyValueItem> dataModel = new DefaultListModel<>();
 
     final private static String HTML_TEMPLATE = "<html><div style='text-align: center;'>%s</div></html>";
@@ -79,7 +76,7 @@ public class PickChoiceDialog extends MageDialog {
             this.editSearch.setText("");
         }
 
-        // listeners for inremental filtering        
+        // listeners for inremental filtering
         editSearch.getDocument().addDocumentListener(new DocumentListener() {
             @Override
             public void insertUpdate(DocumentEvent e) {
@@ -104,7 +101,7 @@ public class PickChoiceDialog extends MageDialog {
         editSearch.addKeyListener(new KeyListener() {
             @Override
             public void keyTyped(KeyEvent e) {
-                //System.out.println("types");                
+                //System.out.println("types");
             }
 
             @Override
@@ -152,8 +149,11 @@ public class PickChoiceDialog extends MageDialog {
         } else {
             MageFrame.getDesktop().add(this, JLayeredPane.PALETTE_LAYER);
         }
-        if (mageDialogState != null) mageDialogState.setStateToDialog(this);
-        else this.makeWindowCentered();
+        if (mageDialogState != null) {
+            mageDialogState.setStateToDialog(this);
+        } else {
+            this.makeWindowCentered();
+        }
 
         // final load
         loadData();
@@ -269,6 +269,7 @@ public class PickChoiceDialog extends MageDialog {
     }
 
     class KeyValueItem {
+
         private final String Key;
         private final String Value;
 
@@ -312,6 +313,8 @@ public class PickChoiceDialog extends MageDialog {
         btOK = new javax.swing.JButton();
         btCancel = new javax.swing.JButton();
 
+        setResizable(true);
+
         labelMessage.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
         labelMessage.setText("<html><div style='text-align: center;'>example long message example long message example long message example long message example long message</div></html>");
 
@@ -322,20 +325,20 @@ public class PickChoiceDialog extends MageDialog {
         javax.swing.GroupLayout panelHeaderLayout = new javax.swing.GroupLayout(panelHeader);
         panelHeader.setLayout(panelHeaderLayout);
         panelHeaderLayout.setHorizontalGroup(
-                panelHeaderLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-                        .addGroup(panelHeaderLayout.createSequentialGroup()
-                                .addGroup(panelHeaderLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-                                        .addComponent(labelMessage, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 210, Short.MAX_VALUE)
-                                        .addComponent(labelSubMessage, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 210, Short.MAX_VALUE))
-                                .addGap(0, 0, 0))
+            panelHeaderLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(panelHeaderLayout.createSequentialGroup()
+                .addGroup(panelHeaderLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                    .addComponent(labelMessage, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 337, Short.MAX_VALUE)
+                    .addComponent(labelSubMessage, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 337, Short.MAX_VALUE))
+                .addGap(0, 0, 0))
         );
         panelHeaderLayout.setVerticalGroup(
-                panelHeaderLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-                        .addGroup(panelHeaderLayout.createSequentialGroup()
-                                .addGap(0, 0, 0)
-                                .addComponent(labelMessage)
-                                .addGap(0, 0, 0)
-                                .addComponent(labelSubMessage))
+            panelHeaderLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(panelHeaderLayout.createSequentialGroup()
+                .addGap(0, 0, 0)
+                .addComponent(labelMessage)
+                .addGap(0, 0, 0)
+                .addComponent(labelSubMessage))
         );
 
         labelSearch.setText("Search:");
@@ -345,34 +348,28 @@ public class PickChoiceDialog extends MageDialog {
         javax.swing.GroupLayout panelSearchLayout = new javax.swing.GroupLayout(panelSearch);
         panelSearch.setLayout(panelSearchLayout);
         panelSearchLayout.setHorizontalGroup(
-                panelSearchLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-                        .addGroup(panelSearchLayout.createSequentialGroup()
-                                .addGap(0, 0, 0)
-                                .addComponent(labelSearch)
-                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
-                                .addComponent(editSearch)
-                                .addGap(0, 0, 0))
+            panelSearchLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(panelSearchLayout.createSequentialGroup()
+                .addGap(0, 0, 0)
+                .addComponent(labelSearch)
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(editSearch)
+                .addGap(0, 0, 0))
         );
         panelSearchLayout.setVerticalGroup(
-                panelSearchLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-                        .addGroup(panelSearchLayout.createSequentialGroup()
-                                .addGap(3, 3, 3)
-                                .addGroup(panelSearchLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
-                                        .addComponent(labelSearch)
-                                        .addComponent(editSearch, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
-                                .addGap(3, 3, 3))
+            panelSearchLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(panelSearchLayout.createSequentialGroup()
+                .addGap(3, 3, 3)
+                .addGroup(panelSearchLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                    .addComponent(labelSearch)
+                    .addComponent(editSearch, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+                .addGap(3, 3, 3))
         );
 
         listChoices.setModel(new javax.swing.AbstractListModel() {
-            String[] strings = {"item1", "item2", "item3"};
-
-            public int getSize() {
-                return strings.length;
-            }
-
-            public Object getElementAt(int i) {
-                return strings[i];
-            }
+            String[] strings = { "item1", "item2", "item3" };
+            public int getSize() { return strings.length; }
+            public Object getElementAt(int i) { return strings[i]; }
         });
         scrollList.setViewportView(listChoices);
 
@@ -393,25 +390,25 @@ public class PickChoiceDialog extends MageDialog {
         javax.swing.GroupLayout panelCommandsLayout = new javax.swing.GroupLayout(panelCommands);
         panelCommands.setLayout(panelCommandsLayout);
         panelCommandsLayout.setHorizontalGroup(
-                panelCommandsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-                        .addGroup(panelCommandsLayout.createSequentialGroup()
-                                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
-                                .addComponent(btOK)
-                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
-                                .addComponent(btCancel, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)
-                                .addContainerGap())
+            panelCommandsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(panelCommandsLayout.createSequentialGroup()
+                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                .addComponent(btOK)
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(btCancel, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)
+                .addContainerGap())
         );
 
-        panelCommandsLayout.linkSize(javax.swing.SwingConstants.HORIZONTAL, btCancel, btOK);
+        panelCommandsLayout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {btCancel, btOK});
 
         panelCommandsLayout.setVerticalGroup(
-                panelCommandsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-                        .addGroup(panelCommandsLayout.createSequentialGroup()
-                                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
-                                .addGroup(panelCommandsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
-                                        .addComponent(btCancel)
-                                        .addComponent(btOK))
-                                .addContainerGap())
+            panelCommandsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(panelCommandsLayout.createSequentialGroup()
+                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                .addGroup(panelCommandsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                    .addComponent(btCancel)
+                    .addComponent(btOK))
+                .addContainerGap())
         );
 
         getRootPane().setDefaultButton(btOK);
@@ -419,28 +416,28 @@ public class PickChoiceDialog extends MageDialog {
         javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
         getContentPane().setLayout(layout);
         layout.setHorizontalGroup(
-                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-                        .addGroup(layout.createSequentialGroup()
-                                .addContainerGap()
-                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-                                        .addComponent(scrollList, javax.swing.GroupLayout.Alignment.TRAILING)
-                                        .addComponent(panelCommands, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
-                                        .addComponent(panelHeader, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
-                                        .addComponent(panelSearch, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
-                                .addContainerGap())
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(layout.createSequentialGroup()
+                .addContainerGap()
+                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                    .addComponent(scrollList, javax.swing.GroupLayout.Alignment.TRAILING)
+                    .addComponent(panelCommands, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                    .addComponent(panelHeader, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                    .addComponent(panelSearch, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+                .addContainerGap())
         );
         layout.setVerticalGroup(
-                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-                        .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
-                                .addContainerGap()
-                                .addComponent(panelHeader, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
-                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
-                                .addComponent(panelSearch, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
-                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
-                                .addComponent(scrollList, javax.swing.GroupLayout.DEFAULT_SIZE, 246, Short.MAX_VALUE)
-                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
-                                .addComponent(panelCommands, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
-                                .addContainerGap())
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+                .addContainerGap()
+                .addComponent(panelHeader, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(panelSearch, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(scrollList, javax.swing.GroupLayout.DEFAULT_SIZE, 282, Short.MAX_VALUE)
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(panelCommands, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                .addContainerGap())
         );
 
         pack();
@@ -474,4 +471,4 @@ public class PickChoiceDialog extends MageDialog {
     private javax.swing.JPanel panelSearch;
     private javax.swing.JScrollPane scrollList;
     // End of variables declaration//GEN-END:variables
-}
\ No newline at end of file
+}

From ce0a94115604961c2e9bd2bad2ed7420d8010ac1 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Sun, 21 Jun 2020 11:50:32 +0200
Subject: [PATCH 437/586] * Fixed spelling of test command.

---
 .../src/test/java/org/mage/test/AI/basic/ExileTargetTest.java | 4 ++--
 .../cards/abilities/keywords/KickerWithFlashbackTest.java     | 2 +-
 .../org/mage/test/cards/continuous/PraetorsGraspTest.java     | 2 +-
 .../test/java/org/mage/test/cards/copy/SparkDoubleTest.java   | 2 +-
 .../mage/test/cards/cost/adventure/AdventureCardsTest.java    | 4 ++--
 .../org/mage/test/cards/enchantments/OathOfLiegesTest.java    | 2 +-
 .../java/org/mage/test/cards/mana/ConditionalManaTest.java    | 2 +-
 .../src/test/java/org/mage/test/cards/mana/ManaPoolTest.java  | 2 +-
 .../org/mage/test/cards/split/CastSplitCardsWithFuseTest.java | 2 +-
 .../java/org/mage/test/commander/duel/OpalPalaceTest.java     | 2 +-
 .../mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java | 4 ++--
 .../src/test/java/org/mage/test/testapi/TestAliases.java      | 4 ++--
 12 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/Mage.Tests/src/test/java/org/mage/test/AI/basic/ExileTargetTest.java b/Mage.Tests/src/test/java/org/mage/test/AI/basic/ExileTargetTest.java
index fd345c9e9b..67b03d91fd 100644
--- a/Mage.Tests/src/test/java/org/mage/test/AI/basic/ExileTargetTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/AI/basic/ExileTargetTest.java
@@ -25,11 +25,11 @@ public class ExileTargetTest extends CardTestCommander4Players {
         addCard(Zone.BATTLEFIELD, playerC, "Balduvian Bears", 1); // 2/2
 
         // must select opponent's Balduvian Bears
-        // showAvaileableAbilities("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
+        // showAvailableAbilities("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Oblivion Ring");
         //addTarget(playerA, "Balduvian Bears"); // disable to activate AI target choose
 
-        // showAvaileableAbilities("after", 1, PhaseStep.BEGIN_COMBAT, playerA);
+        // showAvailableAbilities("after", 1, PhaseStep.BEGIN_COMBAT, playerA);
         //setStrictChooseMode(true); // disable strict mode to activate AI for choosing
         setStopAt(1, PhaseStep.END_COMBAT);
         execute();
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/KickerWithFlashbackTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/KickerWithFlashbackTest.java
index 44e1dde0c8..6133020672 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/KickerWithFlashbackTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/KickerWithFlashbackTest.java
@@ -75,7 +75,7 @@ public class KickerWithFlashbackTest extends CardTestPlayerBase {
         addTarget(playerA, "Burst Lightning");
 
         // cast burst by flashback
-        // showAvaileableAbilities("after", 1, PhaseStep.BEGIN_COMBAT, playerA);
+        // showAvailableAbilities("after", 1, PhaseStep.BEGIN_COMBAT, playerA);
         activateAbility(1, PhaseStep.BEGIN_COMBAT, playerA, "Flashback");
         setChoice(playerA, "Yes"); // use kicker
         addTarget(playerA, playerB);
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/PraetorsGraspTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/PraetorsGraspTest.java
index 941ca49a46..477b208390 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/PraetorsGraspTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/PraetorsGraspTest.java
@@ -22,7 +22,7 @@ public class PraetorsGraspTest extends CardTestPlayerBase {
         addTarget(playerA, playerB);
         addTarget(playerA, "Mountain");
 
-        // showAvaileableAbilities("after", 1, PhaseStep.POSTCOMBAT_MAIN, playerA);
+        // showAvailableAbilities("after", 1, PhaseStep.POSTCOMBAT_MAIN, playerA);
         setStrictChooseMode(true);
         setStopAt(1, PhaseStep.END_TURN);
         execute();
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/SparkDoubleTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/SparkDoubleTest.java
index fc01423064..7c3d07f2e4 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/SparkDoubleTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/SparkDoubleTest.java
@@ -229,7 +229,7 @@ public class SparkDoubleTest extends CardTestPlayerBase {
 
         // make copy of copy by CreateTokenCopyTargetEffect
         // showBattlefield("before last copy", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
-        // showAvaileableAbilities("before last copy", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
+        // showAvailableAbilities("before last copy", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
         activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "create a token that");
         addTarget(playerA, "Akroma, Angel of Wrath[only copy]");
         waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/adventure/AdventureCardsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/adventure/AdventureCardsTest.java
index c73adcb959..8271863f2b 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/cost/adventure/AdventureCardsTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/adventure/AdventureCardsTest.java
@@ -525,7 +525,7 @@ public class AdventureCardsTest extends CardTestPlayerBase {
         addCounters(1, PhaseStep.UPKEEP, playerA, "Wrenn and Six", CounterType.LOYALTY, 5);
         activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "-7: You get an emblem");
         waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
-        // showAvaileableAbilities("abils", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
+        // showAvailableAbilities("abils", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
 
         // retrace - You may cast this card from your graveyard by discarding a land card as an additional cost to cast it
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share");
@@ -562,7 +562,7 @@ public class AdventureCardsTest extends CardTestPlayerBase {
 
         activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: Until your next");
         waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
-        // showAvaileableAbilities("abils", 1, PhaseStep.BEGIN_COMBAT, playerA);
+        // showAvailableAbilities("abils", 1, PhaseStep.BEGIN_COMBAT, playerA);
         castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Treats to Share");
 
         setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/OathOfLiegesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/OathOfLiegesTest.java
index ea8f4bac68..4c2542cb8c 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/OathOfLiegesTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/OathOfLiegesTest.java
@@ -202,7 +202,7 @@ public class OathOfLiegesTest extends CardTestPlayerBase {
         // cast oath copy by opponent
         // showBattlefield("A perms", 2, PhaseStep.POSTCOMBAT_MAIN, playerA);
         // showBattlefield("B perms", 2, PhaseStep.POSTCOMBAT_MAIN, playerB);
-        // showAvaileableAbilities("B abils", 2, PhaseStep.POSTCOMBAT_MAIN, playerB);
+        // showAvailableAbilities("B abils", 2, PhaseStep.POSTCOMBAT_MAIN, playerB);
         // showHand("B hand", 2, PhaseStep.POSTCOMBAT_MAIN, playerB);
         castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Copy Enchantment");
         setChoice(playerB, "Yes"); // use copy effect
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/mana/ConditionalManaTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/mana/ConditionalManaTest.java
index 3f7fe970cf..0a1fed3ee8 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/mana/ConditionalManaTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/mana/ConditionalManaTest.java
@@ -351,7 +351,7 @@ public class ConditionalManaTest extends CardTestPlayerBase {
         // computer must see available mana (4 red mana instead 2)
         //activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
         //activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
-        showAvaileableAbilities("abils", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
+        showAvailableAbilities("abils", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Precision Bolt", playerB);
 
         setStrictChooseMode(true);
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/mana/ManaPoolTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/mana/ManaPoolTest.java
index 7e3d76fa30..32a3ed8a6c 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/mana/ManaPoolTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/mana/ManaPoolTest.java
@@ -241,7 +241,7 @@ public class ManaPoolTest extends CardTestPlayerBase {
         checkManaPool("mana", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "R", 4);
 
         // use for ability
-        // showAvaileableAbilities("before ability", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
+        // showAvailableAbilities("before ability", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
         activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{X}:", playerB);
         setChoice(playerA, "X=3");
         waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithFuseTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithFuseTest.java
index af231c2753..1d1274381d 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithFuseTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithFuseTest.java
@@ -80,7 +80,7 @@ public class CastSplitCardsWithFuseTest extends CardTestPlayerBase {
         addCard(Zone.BATTLEFIELD, playerB, "Absolute Grace"); // Enchantment
         addCard(Zone.BATTLEFIELD, playerB, "Juggernaut"); // Artifact
 
-        // showAvaileableAbilities("abils", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
+        // showAvailableAbilities("abils", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "fused Wear // Tear");
         addTarget(playerA, "Juggernaut");
         addTarget(playerA, "Absolute Grace");
diff --git a/Mage.Tests/src/test/java/org/mage/test/commander/duel/OpalPalaceTest.java b/Mage.Tests/src/test/java/org/mage/test/commander/duel/OpalPalaceTest.java
index e0d3d48582..82a06303bb 100644
--- a/Mage.Tests/src/test/java/org/mage/test/commander/duel/OpalPalaceTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/commander/duel/OpalPalaceTest.java
@@ -29,7 +29,7 @@ public class OpalPalaceTest extends CardTestCommanderDuelBase {
 
         // showHand("hand", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
         // showCommand("command", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
-        // showAvaileableAbilities("abi", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
+        // showAvailableAbilities("abi", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
         activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {G}");
         activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}, {T}");
         setChoice(playerA, "Opal Palace"); // activate mana replace effect first (+3 counters)
diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
index b23d039a1c..f3afb9b960 100644
--- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
+++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
@@ -458,11 +458,11 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
         show(showName, turnNum, step, player, SHOW_COMMAND_EXILE);
     }
 
-    public void showAvaileableAbilities(String showName, int turnNum, PhaseStep step, TestPlayer player) {
+    public void showAvailableAbilities(String showName, int turnNum, PhaseStep step, TestPlayer player) {
         show(showName, turnNum, step, player, SHOW_COMMAND_AVAILABLE_ABILITIES);
     }
 
-    public void showAvaileableMana(String showName, int turnNum, PhaseStep step, TestPlayer player) {
+    public void showAvailableMana(String showName, int turnNum, PhaseStep step, TestPlayer player) {
         show(showName, turnNum, step, player, SHOW_COMMAND_AVAILABLE_MANA);
     }
 
diff --git a/Mage.Tests/src/test/java/org/mage/test/testapi/TestAliases.java b/Mage.Tests/src/test/java/org/mage/test/testapi/TestAliases.java
index f5a72e3721..f1a70c19b5 100644
--- a/Mage.Tests/src/test/java/org/mage/test/testapi/TestAliases.java
+++ b/Mage.Tests/src/test/java/org/mage/test/testapi/TestAliases.java
@@ -190,7 +190,7 @@ public class TestAliases extends CardTestPlayerBase {
         addCard(Zone.BATTLEFIELD, playerA, "Embermage Goblin", 2);
         addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1);
 
-        showAvaileableAbilities("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
+        showAvailableAbilities("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
         activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: {source} deals", "Silvercoat Lion");
         activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: {source} deals", "Silvercoat Lion");
 
@@ -208,7 +208,7 @@ public class TestAliases extends CardTestPlayerBase {
         addCard(Zone.BATTLEFIELD, playerA, "Embermage Goblin@goblin", 2);
         addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion@lion", 1);
 
-        showAvaileableAbilities("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
+        showAvailableAbilities("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
         activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: {source} deals", "@lion");
         activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: {source} deals", "@lion");
 

From a871b36bca46d5b259ee8815649954ada7a9b008 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Sun, 21 Jun 2020 15:07:47 +0200
Subject: [PATCH 438/586] * Fixed wrong zone for cost reduction ability
 (#6684).

---
 Mage.Sets/src/mage/cards/b/BoltBend.java           |  5 ++---
 Mage.Sets/src/mage/cards/g/GustOfWind.java         |  5 ++---
 Mage.Sets/src/mage/cards/i/IgneousElemental.java   |  5 ++---
 Mage.Sets/src/mage/cards/i/IntoTheStory.java       |  5 ++---
 Mage.Sets/src/mage/cards/l/LookoutsDispersal.java  |  3 +--
 Mage.Sets/src/mage/cards/m/MysticalDispute.java    |  7 +++----
 Mage.Sets/src/mage/cards/n/NotOfThisWorld.java     | 12 +++++-------
 Mage.Sets/src/mage/cards/o/OakhameAdversary.java   |  8 ++++----
 Mage.Sets/src/mage/cards/o/OfOneMind.java          |  5 ++---
 Mage.Sets/src/mage/cards/p/PriceOfFame.java        |  5 ++---
 Mage.Sets/src/mage/cards/s/SavageStomp.java        |  5 ++---
 Mage.Sets/src/mage/cards/s/ScourTheLaboratory.java |  3 +--
 Mage.Sets/src/mage/cards/s/SirenReaver.java        |  3 +--
 Mage.Sets/src/mage/cards/s/StoicRebuttal.java      |  8 +++-----
 Mage.Sets/src/mage/cards/s/StormwingEntity.java    | 11 +++++------
 Mage.Sets/src/mage/cards/t/TitanicBrawl.java       |  5 ++---
 Mage.Sets/src/mage/cards/w/WingedWords.java        |  9 ++++-----
 Mage.Sets/src/mage/cards/w/WizardsLightning.java   |  4 ++--
 Mage.Sets/src/mage/cards/w/WizardsRetort.java      |  3 +--
 19 files changed, 46 insertions(+), 65 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/b/BoltBend.java b/Mage.Sets/src/mage/cards/b/BoltBend.java
index c48d40bee9..1f27c67d0a 100644
--- a/Mage.Sets/src/mage/cards/b/BoltBend.java
+++ b/Mage.Sets/src/mage/cards/b/BoltBend.java
@@ -1,5 +1,6 @@
 package mage.cards.b;
 
+import java.util.UUID;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.condition.common.FerociousCondition;
 import mage.abilities.effects.common.ChooseNewTargetsTargetEffect;
@@ -13,8 +14,6 @@ import mage.filter.FilterStackObject;
 import mage.filter.predicate.mageobject.NumberOfTargetsPredicate;
 import mage.target.TargetStackObject;
 
-import java.util.UUID;
-
 /**
  * @author TheElk801
  */
@@ -31,7 +30,7 @@ public final class BoltBend extends CardImpl {
 
         // This spell costs {3} less to cast if you control a creature with power 4 or greater.
         this.addAbility(new SimpleStaticAbility(
-                Zone.STACK, new SpellCostReductionSourceEffect(3, FerociousCondition.instance)
+                Zone.ALL, new SpellCostReductionSourceEffect(3, FerociousCondition.instance)
         ).setRuleAtTheTop(true).addHint(FerociousHint.instance));
 
         // Change the target of target spell or ability with a single target.
diff --git a/Mage.Sets/src/mage/cards/g/GustOfWind.java b/Mage.Sets/src/mage/cards/g/GustOfWind.java
index bbbc825cf6..d0df498d57 100644
--- a/Mage.Sets/src/mage/cards/g/GustOfWind.java
+++ b/Mage.Sets/src/mage/cards/g/GustOfWind.java
@@ -1,5 +1,6 @@
 package mage.cards.g;
 
+import java.util.UUID;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.condition.Condition;
 import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
@@ -18,8 +19,6 @@ import mage.filter.common.FilterNonlandPermanent;
 import mage.filter.predicate.mageobject.AbilityPredicate;
 import mage.target.TargetPermanent;
 
-import java.util.UUID;
-
 /**
  * @author TheElk801
  */
@@ -42,7 +41,7 @@ public final class GustOfWind extends CardImpl {
 
         // This spell costs {2} less to cast if you control a creature with flying.
         this.addAbility(new SimpleStaticAbility(
-                Zone.STACK, new SpellCostReductionSourceEffect(2, condition)
+                Zone.ALL, new SpellCostReductionSourceEffect(2, condition)
         ).setRuleAtTheTop(true));
 
         // Return target nonland permanent you don't control to its owner's hand.
diff --git a/Mage.Sets/src/mage/cards/i/IgneousElemental.java b/Mage.Sets/src/mage/cards/i/IgneousElemental.java
index aba782659f..4aa3be3410 100644
--- a/Mage.Sets/src/mage/cards/i/IgneousElemental.java
+++ b/Mage.Sets/src/mage/cards/i/IgneousElemental.java
@@ -1,5 +1,6 @@
 package mage.cards.i;
 
+import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
@@ -15,8 +16,6 @@ import mage.constants.Zone;
 import mage.filter.StaticFilters;
 import mage.target.common.TargetCreaturePermanent;
 
-import java.util.UUID;
-
 /**
  * @author TheElk801
  */
@@ -30,7 +29,7 @@ public final class IgneousElemental extends CardImpl {
         this.toughness = new MageInt(3);
 
         // This spell costs {2} less to cast if there is a land card in your graveyard.
-        Ability ability = new SimpleStaticAbility(Zone.STACK, new SpellCostReductionSourceEffect(
+        Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(
                 2, new CardsInControllerGraveCondition(1, StaticFilters.FILTER_CARD_LAND)
         ).setText("This spell costs {2} less to cast if there is a land card in your graveyard."));
         ability.setRuleAtTheTop(true);
diff --git a/Mage.Sets/src/mage/cards/i/IntoTheStory.java b/Mage.Sets/src/mage/cards/i/IntoTheStory.java
index e48ef7bf07..e4ac44160c 100644
--- a/Mage.Sets/src/mage/cards/i/IntoTheStory.java
+++ b/Mage.Sets/src/mage/cards/i/IntoTheStory.java
@@ -1,5 +1,6 @@
 package mage.cards.i;
 
+import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.condition.Condition;
@@ -13,8 +14,6 @@ import mage.game.Game;
 import mage.game.Graveyard;
 import mage.players.Player;
 
-import java.util.UUID;
-
 /**
  * @author TheElk801
  */
@@ -25,7 +24,7 @@ public final class IntoTheStory extends CardImpl {
 
         // This spell costs {3} less to cast if an opponent has seven or more cards in their graveyard.
         this.addAbility(new SimpleStaticAbility(
-                Zone.STACK, new SpellCostReductionSourceEffect(3, IntoTheStoryCondition.instance)
+                Zone.ALL, new SpellCostReductionSourceEffect(3, IntoTheStoryCondition.instance)
         ).setRuleAtTheTop(true));
 
         // Draw four cards.
diff --git a/Mage.Sets/src/mage/cards/l/LookoutsDispersal.java b/Mage.Sets/src/mage/cards/l/LookoutsDispersal.java
index ca432b80c0..6177fe818b 100644
--- a/Mage.Sets/src/mage/cards/l/LookoutsDispersal.java
+++ b/Mage.Sets/src/mage/cards/l/LookoutsDispersal.java
@@ -1,4 +1,3 @@
-
 package mage.cards.l;
 
 import java.util.UUID;
@@ -32,7 +31,7 @@ public final class LookoutsDispersal extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}");
 
         // Lookout's Dispersal costs {1} less to cast if you control a Pirate.
-        Ability ability = new SimpleStaticAbility(Zone.STACK, new SpellCostReductionSourceEffect(1, new PermanentsOnTheBattlefieldCondition(filter)));
+        Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(1, new PermanentsOnTheBattlefieldCondition(filter)));
         ability.setRuleAtTheTop(true);
         this.addAbility(ability);
 
diff --git a/Mage.Sets/src/mage/cards/m/MysticalDispute.java b/Mage.Sets/src/mage/cards/m/MysticalDispute.java
index f2c290d95e..e290974dad 100644
--- a/Mage.Sets/src/mage/cards/m/MysticalDispute.java
+++ b/Mage.Sets/src/mage/cards/m/MysticalDispute.java
@@ -1,5 +1,7 @@
 package mage.cards.m;
 
+import java.util.Collection;
+import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.condition.Condition;
@@ -15,9 +17,6 @@ import mage.game.stack.StackObject;
 import mage.target.Target;
 import mage.target.TargetSpell;
 
-import java.util.Collection;
-import java.util.UUID;
-
 /**
  * @author TheElk801
  */
@@ -28,7 +27,7 @@ public final class MysticalDispute extends CardImpl {
 
         // This spell costs {2} less to cast if it targets a blue spell.
         this.addAbility(new SimpleStaticAbility(
-                Zone.STACK, new SpellCostReductionSourceEffect(2, MysticalDisputeCondition.instance)
+                Zone.ALL, new SpellCostReductionSourceEffect(2, MysticalDisputeCondition.instance)
         ).setRuleAtTheTop(true));
 
         // Counter target spell unless its controller pays {3}.
diff --git a/Mage.Sets/src/mage/cards/n/NotOfThisWorld.java b/Mage.Sets/src/mage/cards/n/NotOfThisWorld.java
index 651ff4a65d..aa0fe9ebdb 100644
--- a/Mage.Sets/src/mage/cards/n/NotOfThisWorld.java
+++ b/Mage.Sets/src/mage/cards/n/NotOfThisWorld.java
@@ -1,5 +1,7 @@
 package mage.cards.n;
 
+import java.util.Collection;
+import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.Mode;
 import mage.abilities.common.SimpleStaticAbility;
@@ -19,9 +21,6 @@ import mage.game.stack.StackObject;
 import mage.target.Target;
 import mage.target.TargetStackObject;
 
-import java.util.Collection;
-import java.util.UUID;
-
 /**
  * @author Rafbill
  */
@@ -42,7 +41,7 @@ public final class NotOfThisWorld extends CardImpl {
         this.getSpellAbility().addTarget(new TargetStackObject(filter));
 
         // Not of This World costs {7} less to cast if it targets a spell or ability that targets a creature you control with power 7 or greater.
-        this.addAbility(new SimpleStaticAbility(Zone.STACK, new SpellCostReductionSourceEffect(7, NotOfThisWorldCondition.instance)));
+        this.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(7, NotOfThisWorldCondition.instance)));
     }
 
     private NotOfThisWorld(final NotOfThisWorld card) {
@@ -65,7 +64,6 @@ enum NotOfThisWorldCondition implements Condition {
         filter.add(TargetController.YOU.getControllerPredicate());
     }
 
-
     @Override
     public boolean apply(Game game, Ability source) {
         StackObject sourceSpell = game.getStack().getStackObject(source.getSourceId());
@@ -88,8 +86,8 @@ enum NotOfThisWorldCondition implements Condition {
                 .flatMap(Collection::stream)
                 .map(game::getPermanentOrLKIBattlefield)
                 .anyMatch(permanent -> permanent != null && filter.match(
-                        permanent, sourceSpell.getSourceId(), sourceSpell.getControllerId(), game
-                ));
+                permanent, sourceSpell.getSourceId(), sourceSpell.getControllerId(), game
+        ));
     }
 
     @Override
diff --git a/Mage.Sets/src/mage/cards/o/OakhameAdversary.java b/Mage.Sets/src/mage/cards/o/OakhameAdversary.java
index bfc25c3e5c..7bd99cbe6e 100644
--- a/Mage.Sets/src/mage/cards/o/OakhameAdversary.java
+++ b/Mage.Sets/src/mage/cards/o/OakhameAdversary.java
@@ -1,23 +1,23 @@
 package mage.cards.o;
 
+import java.util.UUID;
 import mage.MageInt;
 import mage.ObjectColor;
 import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.condition.Condition;
+import mage.abilities.condition.common.OpponentControlsPermanentCondition;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect;
 import mage.abilities.keyword.DeathtouchAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
+import mage.constants.ComparisonType;
 import mage.constants.SubType;
 import mage.constants.Zone;
 import mage.filter.FilterPermanent;
 import mage.filter.predicate.mageobject.ColorPredicate;
-import java.util.UUID;
-import mage.abilities.condition.common.OpponentControlsPermanentCondition;
-import mage.constants.ComparisonType;
 
 /**
  * @author TheElk801
@@ -43,7 +43,7 @@ public final class OakhameAdversary extends CardImpl {
 
         // This spell costs {2} less to cast if your opponent controls a green permanent.
         this.addAbility(new SimpleStaticAbility(
-                Zone.STACK, new SpellCostReductionSourceEffect(2, condition)
+                Zone.ALL, new SpellCostReductionSourceEffect(2, condition)
         ).setRuleAtTheTop(true));
 
         // Deathtouch
diff --git a/Mage.Sets/src/mage/cards/o/OfOneMind.java b/Mage.Sets/src/mage/cards/o/OfOneMind.java
index 58df54b9a3..f81bb90277 100644
--- a/Mage.Sets/src/mage/cards/o/OfOneMind.java
+++ b/Mage.Sets/src/mage/cards/o/OfOneMind.java
@@ -1,5 +1,6 @@
 package mage.cards.o;
 
+import java.util.UUID;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.condition.CompoundCondition;
 import mage.abilities.condition.Condition;
@@ -15,8 +16,6 @@ import mage.filter.FilterPermanent;
 import mage.filter.common.FilterControlledCreaturePermanent;
 import mage.filter.predicate.Predicates;
 
-import java.util.UUID;
-
 /**
  * @author TheElk801
  */
@@ -40,7 +39,7 @@ public final class OfOneMind extends CardImpl {
 
         // This spell costs {2} less to cast if you control a Human creature and a non-Human creature.
         this.addAbility(new SimpleStaticAbility(
-                Zone.STACK, new SpellCostReductionSourceEffect(2, condition)
+                Zone.ALL, new SpellCostReductionSourceEffect(2, condition)
         ).setRuleAtTheTop(true));
 
         // Draw two cards.
diff --git a/Mage.Sets/src/mage/cards/p/PriceOfFame.java b/Mage.Sets/src/mage/cards/p/PriceOfFame.java
index 4a8dd77d97..68c180beaa 100644
--- a/Mage.Sets/src/mage/cards/p/PriceOfFame.java
+++ b/Mage.Sets/src/mage/cards/p/PriceOfFame.java
@@ -1,5 +1,6 @@
 package mage.cards.p;
 
+import java.util.UUID;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.condition.Condition;
 import mage.abilities.condition.common.SourceTargetsPermanentCondition;
@@ -15,8 +16,6 @@ import mage.filter.FilterPermanent;
 import mage.filter.common.FilterCreaturePermanent;
 import mage.target.common.TargetCreaturePermanent;
 
-import java.util.UUID;
-
 /**
  * @author TheElk801
  */
@@ -36,7 +35,7 @@ public final class PriceOfFame extends CardImpl {
 
         // This spell costs {2} less to cast if it targets a legendary creature.
         this.addAbility(new SimpleStaticAbility(
-                Zone.STACK, new SpellCostReductionSourceEffect(2, condition)
+                Zone.ALL, new SpellCostReductionSourceEffect(2, condition)
         ).setRuleAtTheTop(true));
 
         // Destroy target creature.
diff --git a/Mage.Sets/src/mage/cards/s/SavageStomp.java b/Mage.Sets/src/mage/cards/s/SavageStomp.java
index 993f367ce2..26db6aba9a 100644
--- a/Mage.Sets/src/mage/cards/s/SavageStomp.java
+++ b/Mage.Sets/src/mage/cards/s/SavageStomp.java
@@ -1,5 +1,6 @@
 package mage.cards.s;
 
+import java.util.UUID;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.condition.Condition;
 import mage.abilities.condition.common.SourceTargetsPermanentCondition;
@@ -19,8 +20,6 @@ import mage.filter.common.FilterControlledCreaturePermanent;
 import mage.target.common.TargetControlledCreaturePermanent;
 import mage.target.common.TargetCreaturePermanent;
 
-import java.util.UUID;
-
 /**
  * @author TheElk801
  */
@@ -36,7 +35,7 @@ public final class SavageStomp extends CardImpl {
 
         // Savage Stomp costs {2} less to cast if it targets a Dinosaur you control.
         this.addAbility(new SimpleStaticAbility(
-                Zone.STACK, new SpellCostReductionSourceEffect(2, condition)
+                Zone.ALL, new SpellCostReductionSourceEffect(2, condition)
         ).setRuleAtTheTop(true));
 
         // Put a +1/+1 counter on target creature you control. Then that creature fights target creature you don't control.
diff --git a/Mage.Sets/src/mage/cards/s/ScourTheLaboratory.java b/Mage.Sets/src/mage/cards/s/ScourTheLaboratory.java
index 4b52a814ce..ca30d92477 100644
--- a/Mage.Sets/src/mage/cards/s/ScourTheLaboratory.java
+++ b/Mage.Sets/src/mage/cards/s/ScourTheLaboratory.java
@@ -1,7 +1,6 @@
 package mage.cards.s;
 
 import java.util.UUID;
-
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.condition.common.DeliriumCondition;
@@ -23,7 +22,7 @@ public final class ScourTheLaboratory extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{U}{U}");
 
         // <i>Delirium</i> &mdash; Scour the Laboratory costs {2} less to cast if there are four or more card types among cards in your graveyard.
-        Ability ability = new SimpleStaticAbility(Zone.STACK, new SpellCostReductionSourceEffect(2, DeliriumCondition.instance));
+        Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(2, DeliriumCondition.instance));
         ability.setRuleAtTheTop(true);
         ability.setAbilityWord(AbilityWord.DELIRIUM);
         ability.addHint(DeliriumHint.instance);
diff --git a/Mage.Sets/src/mage/cards/s/SirenReaver.java b/Mage.Sets/src/mage/cards/s/SirenReaver.java
index a9c1fea7ba..edce1c538f 100644
--- a/Mage.Sets/src/mage/cards/s/SirenReaver.java
+++ b/Mage.Sets/src/mage/cards/s/SirenReaver.java
@@ -1,4 +1,3 @@
-
 package mage.cards.s;
 
 import java.util.UUID;
@@ -31,7 +30,7 @@ public final class SirenReaver extends CardImpl {
         this.toughness = new MageInt(2);
 
         // <i>Raid</i> — Siren Reaver costs {1} less to cast if you attacked this turn.
-        Ability ability = new SimpleStaticAbility(Zone.STACK, new SpellCostReductionSourceEffect(1, RaidCondition.instance));
+        Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(1, RaidCondition.instance));
         ability.setAbilityWord(AbilityWord.RAID);
         ability.setRuleAtTheTop(true);
         this.addAbility(ability, new PlayerAttackedWatcher());
diff --git a/Mage.Sets/src/mage/cards/s/StoicRebuttal.java b/Mage.Sets/src/mage/cards/s/StoicRebuttal.java
index a934014164..81352927b0 100644
--- a/Mage.Sets/src/mage/cards/s/StoicRebuttal.java
+++ b/Mage.Sets/src/mage/cards/s/StoicRebuttal.java
@@ -1,5 +1,3 @@
-
-
 package mage.cards.s;
 
 import java.util.UUID;
@@ -21,14 +19,14 @@ import mage.target.TargetSpell;
 public final class StoicRebuttal extends CardImpl {
 
     public StoicRebuttal(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{U}{U}");
+        super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}{U}");
 
         // <i>Metalcraft</i> &mdash; Stoic Rebuttal costs {1} less to cast if you control three or more artifacts.
-        Ability ability = new SimpleStaticAbility(Zone.STACK, new SpellCostReductionSourceEffect(1, MetalcraftCondition.instance));
+        Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(1, MetalcraftCondition.instance));
         ability.setRuleAtTheTop(true);
         ability.setAbilityWord(AbilityWord.METALCRAFT);
         this.addAbility(ability);
-        
+
         // Counter target spell.
         this.getSpellAbility().addTarget(new TargetSpell());
         this.getSpellAbility().addEffect(new CounterTargetEffect());
diff --git a/Mage.Sets/src/mage/cards/s/StormwingEntity.java b/Mage.Sets/src/mage/cards/s/StormwingEntity.java
index d910fffb42..45e90a6e14 100644
--- a/Mage.Sets/src/mage/cards/s/StormwingEntity.java
+++ b/Mage.Sets/src/mage/cards/s/StormwingEntity.java
@@ -1,5 +1,8 @@
 package mage.cards.s;
 
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
@@ -22,10 +25,6 @@ import mage.game.events.GameEvent;
 import mage.game.stack.Spell;
 import mage.watchers.Watcher;
 
-import java.util.HashSet;
-import java.util.Set;
-import java.util.UUID;
-
 /**
  * @author TheElk801
  */
@@ -43,7 +42,7 @@ public final class StormwingEntity extends CardImpl {
         this.toughness = new MageInt(3);
 
         // This spell costs {2}{U} less to cast if you've cast an instant or sorcery spell this turn.
-        this.addAbility(new SimpleStaticAbility(Zone.STACK, new SpellCostReductionSourceEffect(
+        this.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(
                 new ManaCostsImpl("{2}{U}"), StormwingEntityCondition.instance
         )).setRuleAtTheTop(true).addHint(hint), new StormwingEntityWatcher());
 
@@ -106,4 +105,4 @@ class StormwingEntityWatcher extends Watcher {
     boolean checkPlayer(UUID playerId) {
         return playerMap.contains(playerId);
     }
-}
\ No newline at end of file
+}
diff --git a/Mage.Sets/src/mage/cards/t/TitanicBrawl.java b/Mage.Sets/src/mage/cards/t/TitanicBrawl.java
index 29c627c3fd..54f079db20 100644
--- a/Mage.Sets/src/mage/cards/t/TitanicBrawl.java
+++ b/Mage.Sets/src/mage/cards/t/TitanicBrawl.java
@@ -1,5 +1,6 @@
 package mage.cards.t;
 
+import java.util.UUID;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.condition.Condition;
 import mage.abilities.condition.common.SourceTargetsPermanentCondition;
@@ -17,8 +18,6 @@ import mage.filter.predicate.permanent.CounterPredicate;
 import mage.target.common.TargetControlledCreaturePermanent;
 import mage.target.common.TargetCreaturePermanent;
 
-import java.util.UUID;
-
 /**
  * @author TheElk801
  */
@@ -38,7 +37,7 @@ public final class TitanicBrawl extends CardImpl {
 
         // This spell costs {1} less to cast if it targets a creature you control with a +1/+1 counter on it.
         this.addAbility(new SimpleStaticAbility(
-                Zone.STACK, new SpellCostReductionSourceEffect(1, condition)
+                Zone.ALL, new SpellCostReductionSourceEffect(1, condition)
         ).setRuleAtTheTop(true));
 
         // Target creature you control fights target creature you don't control.
diff --git a/Mage.Sets/src/mage/cards/w/WingedWords.java b/Mage.Sets/src/mage/cards/w/WingedWords.java
index 146ebf5111..2f8f344dc4 100644
--- a/Mage.Sets/src/mage/cards/w/WingedWords.java
+++ b/Mage.Sets/src/mage/cards/w/WingedWords.java
@@ -1,5 +1,6 @@
 package mage.cards.w;
 
+import java.util.UUID;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
@@ -12,8 +13,6 @@ import mage.constants.Zone;
 import mage.filter.common.FilterControlledPermanent;
 import mage.filter.predicate.mageobject.AbilityPredicate;
 
-import java.util.UUID;
-
 /**
  * @author TheElk801
  */
@@ -31,9 +30,9 @@ public final class WingedWords extends CardImpl {
 
         // This spell costs {1} less to cast if you control a creature with flying.
         this.addAbility(new SimpleStaticAbility(
-                Zone.STACK, new SpellCostReductionSourceEffect(
-                1, new PermanentsOnTheBattlefieldCondition(filter)
-        )).setRuleAtTheTop(true));
+                Zone.ALL, new SpellCostReductionSourceEffect(
+                        1, new PermanentsOnTheBattlefieldCondition(filter)
+                )).setRuleAtTheTop(true));
 
         // Draw two cards.
         this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2));
diff --git a/Mage.Sets/src/mage/cards/w/WizardsLightning.java b/Mage.Sets/src/mage/cards/w/WizardsLightning.java
index b13e313de1..b1d69d0283 100644
--- a/Mage.Sets/src/mage/cards/w/WizardsLightning.java
+++ b/Mage.Sets/src/mage/cards/w/WizardsLightning.java
@@ -1,4 +1,3 @@
-
 package mage.cards.w;
 
 import java.util.UUID;
@@ -14,6 +13,7 @@ import mage.constants.SubType;
 import mage.constants.Zone;
 import mage.filter.common.FilterControlledPermanent;
 import mage.target.common.TargetAnyTarget;
+
 /**
  *
  * @author Will
@@ -30,7 +30,7 @@ public final class WizardsLightning extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R}");
 
         // Wizard's Lightning costs {2} less to cast if you control a Wizard.
-        Ability ability = new SimpleStaticAbility(Zone.STACK, new SpellCostReductionSourceEffect(2, new PermanentsOnTheBattlefieldCondition(filter)));
+        Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(2, new PermanentsOnTheBattlefieldCondition(filter)));
         ability.setRuleAtTheTop(true);
         this.addAbility(ability);
 
diff --git a/Mage.Sets/src/mage/cards/w/WizardsRetort.java b/Mage.Sets/src/mage/cards/w/WizardsRetort.java
index aecee64c22..d58c678ad3 100644
--- a/Mage.Sets/src/mage/cards/w/WizardsRetort.java
+++ b/Mage.Sets/src/mage/cards/w/WizardsRetort.java
@@ -1,4 +1,3 @@
-
 package mage.cards.w;
 
 import java.util.UUID;
@@ -31,7 +30,7 @@ public final class WizardsRetort extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}{U}");
 
         // Wizard's Retort costs {1} less to cast if you control a Wizard.
-        Ability ability = new SimpleStaticAbility(Zone.STACK, new SpellCostReductionSourceEffect(1, new PermanentsOnTheBattlefieldCondition(filter)));
+        Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(1, new PermanentsOnTheBattlefieldCondition(filter)));
         ability.setRuleAtTheTop(true);
         this.addAbility(ability);
 

From 4fda8eab02fcc923268ac5363ff682fbd910d9cc Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sun, 21 Jun 2020 09:21:46 -0400
Subject: [PATCH 439/586] updated JMP spoiler and reprints

---
 Mage.Sets/src/mage/sets/Jumpstart.java | 129 ++++++++++++++++++++++-
 Utils/mtg-cards-data.txt               | 139 +++++++++++++++++++++++--
 2 files changed, 260 insertions(+), 8 deletions(-)

diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index 408c38b1e6..69eeb2f7a3 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -20,6 +20,7 @@ public final class Jumpstart extends ExpansionSet {
         this.blockName = "Jumpstart";
         this.hasBasicLands = true;
 
+        cards.add(new SetCardInfo("Act of Treason", 289, Rarity.COMMON, mage.cards.a.ActOfTreason.class));
         cards.add(new SetCardInfo("Aegis Turtle", 138, Rarity.COMMON, mage.cards.a.AegisTurtle.class));
         cards.add(new SetCardInfo("Aegis of the Heavens", 79, Rarity.UNCOMMON, mage.cards.a.AegisOfTheHeavens.class));
         cards.add(new SetCardInfo("Aerial Assault", 80, Rarity.COMMON, mage.cards.a.AerialAssault.class));
@@ -28,14 +29,22 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Affectionate Indrik", 373, Rarity.UNCOMMON, mage.cards.a.AffectionateIndrik.class));
         cards.add(new SetCardInfo("Aggressive Urge", 374, Rarity.COMMON, mage.cards.a.AggressiveUrge.class));
         cards.add(new SetCardInfo("Agonizing Syphon", 199, Rarity.COMMON, mage.cards.a.AgonizingSyphon.class));
+        cards.add(new SetCardInfo("Ajani's Chosen", 82, Rarity.RARE, mage.cards.a.AjanisChosen.class));
+        cards.add(new SetCardInfo("Alabaster Mage", 83, Rarity.UNCOMMON, mage.cards.a.AlabasterMage.class));
         cards.add(new SetCardInfo("Alloy Myr", 457, Rarity.COMMON, mage.cards.a.AlloyMyr.class));
         cards.add(new SetCardInfo("Ambassador Oak", 375, Rarity.COMMON, mage.cards.a.AmbassadorOak.class));
         cards.add(new SetCardInfo("Ancestral Statue", 458, Rarity.COMMON, mage.cards.a.AncestralStatue.class));
+        cards.add(new SetCardInfo("Angel of Mercy", 84, Rarity.COMMON, mage.cards.a.AngelOfMercy.class));
         cards.add(new SetCardInfo("Angel of the Dire Hour", 85, Rarity.RARE, mage.cards.a.AngelOfTheDireHour.class));
+        cards.add(new SetCardInfo("Angelic Arbiter", 86, Rarity.RARE, mage.cards.a.AngelicArbiter.class));
+        cards.add(new SetCardInfo("Angelic Edict", 87, Rarity.COMMON, mage.cards.a.AngelicEdict.class));
+        cards.add(new SetCardInfo("Angelic Page", 88, Rarity.UNCOMMON, mage.cards.a.AngelicPage.class));
+        cards.add(new SetCardInfo("Arbor Armament", 376, Rarity.COMMON, mage.cards.a.ArborArmament.class));
         cards.add(new SetCardInfo("Arcane Encyclopedia", 459, Rarity.UNCOMMON, mage.cards.a.ArcaneEncyclopedia.class));
         cards.add(new SetCardInfo("Archaeomender", 9, Rarity.COMMON, mage.cards.a.Archaeomender.class));
         cards.add(new SetCardInfo("Archon of Justice", 89, Rarity.RARE, mage.cards.a.ArchonOfJustice.class));
         cards.add(new SetCardInfo("Archon of Redemption", 90, Rarity.RARE, mage.cards.a.ArchonOfRedemption.class));
+        cards.add(new SetCardInfo("Armorcraft Judge", 377, Rarity.UNCOMMON, mage.cards.a.ArmorcraftJudge.class));
         cards.add(new SetCardInfo("Ashmouth Hound", 290, Rarity.COMMON, mage.cards.a.AshmouthHound.class));
         cards.add(new SetCardInfo("Assassin's Strike", 200, Rarity.UNCOMMON, mage.cards.a.AssassinsStrike.class));
         cards.add(new SetCardInfo("Assault Formation", 378, Rarity.RARE, mage.cards.a.AssaultFormation.class));
@@ -43,23 +52,38 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Awakener Druid", 379, Rarity.UNCOMMON, mage.cards.a.AwakenerDruid.class));
         cards.add(new SetCardInfo("Bake into a Pie", 201, Rarity.COMMON, mage.cards.b.BakeIntoAPie.class));
         cards.add(new SetCardInfo("Ball Lightning", 291, Rarity.RARE, mage.cards.b.BallLightning.class));
+        cards.add(new SetCardInfo("Barrage of Expendables", 292, Rarity.UNCOMMON, mage.cards.b.BarrageOfExpendables.class));
+        cards.add(new SetCardInfo("Barter in Blood", 202, Rarity.UNCOMMON, mage.cards.b.BarterInBlood.class));
+        cards.add(new SetCardInfo("Bathe in Dragonfire", 293, Rarity.COMMON, mage.cards.b.BatheInDragonfire.class));
         cards.add(new SetCardInfo("Battlefield Promotion", 91, Rarity.COMMON, mage.cards.b.BattlefieldPromotion.class));
+        cards.add(new SetCardInfo("Battleground Geist", 139, Rarity.UNCOMMON, mage.cards.b.BattlegroundGeist.class));
+        cards.add(new SetCardInfo("Beetleback Chief", 294, Rarity.UNCOMMON, mage.cards.b.BeetlebackChief.class));
         cards.add(new SetCardInfo("Befuddle", 140, Rarity.COMMON, mage.cards.b.Befuddle.class));
+        cards.add(new SetCardInfo("Belltower Sphinx", 141, Rarity.UNCOMMON, mage.cards.b.BelltowerSphinx.class));
         cards.add(new SetCardInfo("Black Cat", 203, Rarity.COMMON, mage.cards.b.BlackCat.class));
+        cards.add(new SetCardInfo("Black Market", 204, Rarity.RARE, mage.cards.b.BlackMarket.class));
+        cards.add(new SetCardInfo("Blessed Spirits", 92, Rarity.UNCOMMON, mage.cards.b.BlessedSpirits.class));
         cards.add(new SetCardInfo("Blighted Bat", 205, Rarity.COMMON, mage.cards.b.BlightedBat.class));
+        cards.add(new SetCardInfo("Blindblast", 295, Rarity.COMMON, mage.cards.b.Blindblast.class));
         cards.add(new SetCardInfo("Blood Artist", 206, Rarity.UNCOMMON, mage.cards.b.BloodArtist.class));
         cards.add(new SetCardInfo("Blood Divination", 207, Rarity.UNCOMMON, mage.cards.b.BloodDivination.class));
         cards.add(new SetCardInfo("Blood Host", 208, Rarity.UNCOMMON, mage.cards.b.BloodHost.class));
         cards.add(new SetCardInfo("Bloodbond Vampire", 209, Rarity.UNCOMMON, mage.cards.b.BloodbondVampire.class));
         cards.add(new SetCardInfo("Bloodhunter Bat", 210, Rarity.COMMON, mage.cards.b.BloodhunterBat.class));
+        cards.add(new SetCardInfo("Bloodrage Brawler", 296, Rarity.UNCOMMON, mage.cards.b.BloodrageBrawler.class));
         cards.add(new SetCardInfo("Bloodrock Cyclops", 297, Rarity.COMMON, mage.cards.b.BloodrockCyclops.class));
+        cards.add(new SetCardInfo("Bloodshot Trainee", 298, Rarity.UNCOMMON, mage.cards.b.BloodshotTrainee.class));
         cards.add(new SetCardInfo("Bogbrew Witch", 211, Rarity.RARE, mage.cards.b.BogbrewWitch.class));
+        cards.add(new SetCardInfo("Boggart Brute", 299, Rarity.COMMON, mage.cards.b.BoggartBrute.class));
         cards.add(new SetCardInfo("Bone Picker", 212, Rarity.UNCOMMON, mage.cards.b.BonePicker.class));
         cards.add(new SetCardInfo("Bone Splinters", 213, Rarity.COMMON, mage.cards.b.BoneSplinters.class));
+        cards.add(new SetCardInfo("Borderland Marauder", 300, Rarity.COMMON, mage.cards.b.BorderlandMarauder.class));
+        cards.add(new SetCardInfo("Borderland Minotaur", 301, Rarity.COMMON, mage.cards.b.BorderlandMinotaur.class));
         cards.add(new SetCardInfo("Brightmare", 2, Rarity.UNCOMMON, mage.cards.b.Brightmare.class));
         cards.add(new SetCardInfo("Brindle Shoat", 380, Rarity.UNCOMMON, mage.cards.b.BrindleShoat.class));
         cards.add(new SetCardInfo("Brushstrider", 381, Rarity.UNCOMMON, mage.cards.b.Brushstrider.class));
         cards.add(new SetCardInfo("Bubbling Cauldron", 460, Rarity.UNCOMMON, mage.cards.b.BubblingCauldron.class));
+        cards.add(new SetCardInfo("Bulwark Giant", 93, Rarity.COMMON, mage.cards.b.BulwarkGiant.class));
         cards.add(new SetCardInfo("Burglar Rat", 214, Rarity.COMMON, mage.cards.b.BurglarRat.class));
         cards.add(new SetCardInfo("Buried Ruin", 491, Rarity.UNCOMMON, mage.cards.b.BuriedRuin.class));
         cards.add(new SetCardInfo("Cadaver Imp", 215, Rarity.COMMON, mage.cards.c.CadaverImp.class));
@@ -67,9 +91,12 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Cathar's Companion", 94, Rarity.COMMON, mage.cards.c.CatharsCompanion.class));
         cards.add(new SetCardInfo("Cathars' Crusade", 95, Rarity.RARE, mage.cards.c.CatharsCrusade.class));
         cards.add(new SetCardInfo("Cauldron Familiar", 216, Rarity.COMMON, mage.cards.c.CauldronFamiliar.class));
+        cards.add(new SetCardInfo("Celestial Mantle", 96, Rarity.RARE, mage.cards.c.CelestialMantle.class));
         cards.add(new SetCardInfo("Cemetery Recruitment", 217, Rarity.COMMON, mage.cards.c.CemeteryRecruitment.class));
         cards.add(new SetCardInfo("Chain Lightning", 302, Rarity.UNCOMMON, mage.cards.c.ChainLightning.class));
         cards.add(new SetCardInfo("Chamber Sentry", 461, Rarity.RARE, mage.cards.c.ChamberSentry.class));
+        cards.add(new SetCardInfo("Champion of Lambholt", 383, Rarity.RARE, mage.cards.c.ChampionOfLambholt.class));
+        cards.add(new SetCardInfo("Charmbreaker Devils", 303, Rarity.RARE, mage.cards.c.CharmbreakerDevils.class));
         cards.add(new SetCardInfo("Chart a Course", 142, Rarity.UNCOMMON, mage.cards.c.ChartACourse.class));
         cards.add(new SetCardInfo("Child of Night", 218, Rarity.COMMON, mage.cards.c.ChildOfNight.class));
         cards.add(new SetCardInfo("Chromatic Sphere", 462, Rarity.COMMON, mage.cards.c.ChromaticSphere.class));
@@ -77,7 +104,11 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Cloudreader Sphinx", 143, Rarity.COMMON, mage.cards.c.CloudreaderSphinx.class));
         cards.add(new SetCardInfo("Cloudshift", 97, Rarity.COMMON, mage.cards.c.Cloudshift.class));
         cards.add(new SetCardInfo("Coastal Piracy", 144, Rarity.UNCOMMON, mage.cards.c.CoastalPiracy.class));
+        cards.add(new SetCardInfo("Collateral Damage", 305, Rarity.COMMON, mage.cards.c.CollateralDamage.class));
         cards.add(new SetCardInfo("Commune with Dinosaurs", 384, Rarity.COMMON, mage.cards.c.CommuneWithDinosaurs.class));
+        cards.add(new SetCardInfo("Corpse Hauler", 219, Rarity.COMMON, mage.cards.c.CorpseHauler.class));
+        cards.add(new SetCardInfo("Corpse Traders", 220, Rarity.UNCOMMON, mage.cards.c.CorpseTraders.class));
+        cards.add(new SetCardInfo("Cradle of Vitality", 98, Rarity.RARE, mage.cards.c.CradleOfVitality.class));
         cards.add(new SetCardInfo("Craterhoof Behemoth", 385, Rarity.MYTHIC, mage.cards.c.CraterhoofBehemoth.class));
         cards.add(new SetCardInfo("Crookclaw Transmuter", 145, Rarity.COMMON, mage.cards.c.CrookclawTransmuter.class));
         cards.add(new SetCardInfo("Crow of Dark Tidings", 221, Rarity.COMMON, mage.cards.c.CrowOfDarkTidings.class));
@@ -85,25 +116,37 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Cryptic Serpent", 146, Rarity.UNCOMMON, mage.cards.c.CrypticSerpent.class));
         cards.add(new SetCardInfo("Curiosity", 147, Rarity.UNCOMMON, mage.cards.c.Curiosity.class));
         cards.add(new SetCardInfo("Curious Obsession", 148, Rarity.UNCOMMON, mage.cards.c.CuriousObsession.class));
+        cards.add(new SetCardInfo("Dance with Devils", 306, Rarity.UNCOMMON, mage.cards.d.DanceWithDevils.class));
         cards.add(new SetCardInfo("Dauntless Onslaught", 99, Rarity.UNCOMMON, mage.cards.d.DauntlessOnslaught.class));
         cards.add(new SetCardInfo("Dawntreader Elk", 387, Rarity.COMMON, mage.cards.d.DawntreaderElk.class));
         cards.add(new SetCardInfo("Death's Approach", 222, Rarity.COMMON, mage.cards.d.DeathsApproach.class));
         cards.add(new SetCardInfo("Departed Deckhand", 149, Rarity.UNCOMMON, mage.cards.d.DepartedDeckhand.class));
         cards.add(new SetCardInfo("Dinrova Horror", 450, Rarity.UNCOMMON, mage.cards.d.DinrovaHorror.class));
         cards.add(new SetCardInfo("Divine Arrow", 100, Rarity.COMMON, mage.cards.d.DivineArrow.class));
+        cards.add(new SetCardInfo("Doublecast", 307, Rarity.UNCOMMON, mage.cards.d.Doublecast.class));
         cards.add(new SetCardInfo("Douse in Gloom", 223, Rarity.COMMON, mage.cards.d.DouseInGloom.class));
+        cards.add(new SetCardInfo("Draconic Roar", 308, Rarity.COMMON, mage.cards.d.DraconicRoar.class));
+        cards.add(new SetCardInfo("Dragon Fodder", 309, Rarity.COMMON, mage.cards.d.DragonFodder.class));
+        cards.add(new SetCardInfo("Dragon Hatchling", 310, Rarity.COMMON, mage.cards.d.DragonHatchling.class));
+        cards.add(new SetCardInfo("Dragonloft Idol", 463, Rarity.UNCOMMON, mage.cards.d.DragonloftIdol.class));
+        cards.add(new SetCardInfo("Dragonlord's Servant", 311, Rarity.UNCOMMON, mage.cards.d.DragonlordsServant.class));
+        cards.add(new SetCardInfo("Dragonspeaker Shaman", 312, Rarity.UNCOMMON, mage.cards.d.DragonspeakerShaman.class));
         cards.add(new SetCardInfo("Drainpipe Vermin", 224, Rarity.COMMON, mage.cards.d.DrainpipeVermin.class));
         cards.add(new SetCardInfo("Drana, Liberator of Malakir", 225, Rarity.MYTHIC, mage.cards.d.DranaLiberatorOfMalakir.class));
         cards.add(new SetCardInfo("Dreamstone Hedron", 464, Rarity.UNCOMMON, mage.cards.d.DreamstoneHedron.class));
         cards.add(new SetCardInfo("Drover of the Mighty", 388, Rarity.UNCOMMON, mage.cards.d.DroverOfTheMighty.class));
+        cards.add(new SetCardInfo("Dualcaster Mage", 313, Rarity.RARE, mage.cards.d.DualcasterMage.class));
         cards.add(new SetCardInfo("Duelist's Heritage", 101, Rarity.RARE, mage.cards.d.DuelistsHeritage.class));
         cards.add(new SetCardInfo("Dutiful Attendant", 226, Rarity.COMMON, mage.cards.d.DutifulAttendant.class));
         cards.add(new SetCardInfo("Dwynen's Elite", 389, Rarity.UNCOMMON, mage.cards.d.DwynensElite.class));
         cards.add(new SetCardInfo("Elemental Uprising", 390, Rarity.COMMON, mage.cards.e.ElementalUprising.class));
         cards.add(new SetCardInfo("Elvish Archdruid", 391, Rarity.RARE, mage.cards.e.ElvishArchdruid.class));
+        cards.add(new SetCardInfo("Emancipation Angel", 102, Rarity.UNCOMMON, mage.cards.e.EmancipationAngel.class));
         cards.add(new SetCardInfo("Enlarge", 392, Rarity.UNCOMMON, mage.cards.e.Enlarge.class));
         cards.add(new SetCardInfo("Entomber Exarch", 227, Rarity.UNCOMMON, mage.cards.e.EntomberExarch.class));
         cards.add(new SetCardInfo("Erratic Visionary", 150, Rarity.COMMON, mage.cards.e.ErraticVisionary.class));
+        cards.add(new SetCardInfo("Essence Flux", 151, Rarity.UNCOMMON, mage.cards.e.EssenceFlux.class));
+        cards.add(new SetCardInfo("Etali, Primal Storm", 314, Rarity.RARE, mage.cards.e.EtaliPrimalStorm.class));
         cards.add(new SetCardInfo("Eternal Taskmaster", 228, Rarity.UNCOMMON, mage.cards.e.EternalTaskmaster.class));
         cards.add(new SetCardInfo("Eternal Thirst", 229, Rarity.COMMON, mage.cards.e.EternalThirst.class));
         cards.add(new SetCardInfo("Exclude", 152, Rarity.UNCOMMON, mage.cards.e.Exclude.class));
@@ -112,6 +155,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Explore", 393, Rarity.COMMON, mage.cards.e.Explore.class));
         cards.add(new SetCardInfo("Exquisite Blood", 231, Rarity.RARE, mage.cards.e.ExquisiteBlood.class));
         cards.add(new SetCardInfo("Fa'adiyah Seer", 394, Rarity.COMMON, mage.cards.f.FaadiyahSeer.class));
+        cards.add(new SetCardInfo("Face of Divinity", 103, Rarity.UNCOMMON, mage.cards.f.FaceOfDivinity.class));
         cards.add(new SetCardInfo("Falkenrath Noble", 232, Rarity.UNCOMMON, mage.cards.f.FalkenrathNoble.class));
         cards.add(new SetCardInfo("Fanatical Firebrand", 315, Rarity.COMMON, mage.cards.f.FanaticalFirebrand.class));
         cards.add(new SetCardInfo("Fell Specter", 233, Rarity.UNCOMMON, mage.cards.f.FellSpecter.class));
@@ -120,9 +164,18 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Feral Prowler", 397, Rarity.COMMON, mage.cards.f.FeralProwler.class));
         cards.add(new SetCardInfo("Fertilid", 398, Rarity.COMMON, mage.cards.f.Fertilid.class));
         cards.add(new SetCardInfo("Festering Newt", 234, Rarity.COMMON, mage.cards.f.FesteringNewt.class));
+        cards.add(new SetCardInfo("Flame Lash", 316, Rarity.COMMON, mage.cards.f.FlameLash.class));
         cards.add(new SetCardInfo("Flames of the Firebrand", 317, Rarity.UNCOMMON, mage.cards.f.FlamesOfTheFirebrand.class));
+        cards.add(new SetCardInfo("Flames of the Raze-Boar", 318, Rarity.UNCOMMON, mage.cards.f.FlamesOfTheRazeBoar.class));
+        cards.add(new SetCardInfo("Flametongue Kavu", 319, Rarity.UNCOMMON, mage.cards.f.FlametongueKavu.class));
+        cards.add(new SetCardInfo("Fling", 320, Rarity.COMMON, mage.cards.f.Fling.class));
+        cards.add(new SetCardInfo("Flurry of Horns", 321, Rarity.COMMON, mage.cards.f.FlurryOfHorns.class));
+        cards.add(new SetCardInfo("Forced Worship", 104, Rarity.COMMON, mage.cards.f.ForcedWorship.class));
         cards.add(new SetCardInfo("Forest", 70, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Forge Devil", 322, Rarity.COMMON, mage.cards.f.ForgeDevil.class));
+        cards.add(new SetCardInfo("Fortify", 105, Rarity.COMMON, mage.cards.f.Fortify.class));
         cards.add(new SetCardInfo("Funeral Rites", 235, Rarity.COMMON, mage.cards.f.FuneralRites.class));
+        cards.add(new SetCardInfo("Furnace Whelp", 323, Rarity.COMMON, mage.cards.f.FurnaceWhelp.class));
         cards.add(new SetCardInfo("Fusion Elemental", 451, Rarity.UNCOMMON, mage.cards.f.FusionElemental.class));
         cards.add(new SetCardInfo("Gargoyle Sentinel", 465, Rarity.UNCOMMON, mage.cards.g.GargoyleSentinel.class));
         cards.add(new SetCardInfo("Ghalta, Primal Hunger", 399, Rarity.RARE, mage.cards.g.GhaltaPrimalHunger.class));
@@ -133,37 +186,65 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Gifted Aetherborn", 239, Rarity.UNCOMMON, mage.cards.g.GiftedAetherborn.class));
         cards.add(new SetCardInfo("Gingerbrute", 466, Rarity.COMMON, mage.cards.g.Gingerbrute.class));
         cards.add(new SetCardInfo("Gird for Battle", 106, Rarity.UNCOMMON, mage.cards.g.GirdForBattle.class));
+        cards.add(new SetCardInfo("Goblin Chieftain", 324, Rarity.RARE, mage.cards.g.GoblinChieftain.class));
+        cards.add(new SetCardInfo("Goblin Commando", 325, Rarity.UNCOMMON, mage.cards.g.GoblinCommando.class));
+        cards.add(new SetCardInfo("Goblin Goon", 326, Rarity.RARE, mage.cards.g.GoblinGoon.class));
+        cards.add(new SetCardInfo("Goblin Instigator", 327, Rarity.COMMON, mage.cards.g.GoblinInstigator.class));
+        cards.add(new SetCardInfo("Goblin Lore", 328, Rarity.UNCOMMON, mage.cards.g.GoblinLore.class));
+        cards.add(new SetCardInfo("Goblin Rally", 329, Rarity.UNCOMMON, mage.cards.g.GoblinRally.class));
+        cards.add(new SetCardInfo("Goblin Shortcutter", 330, Rarity.COMMON, mage.cards.g.GoblinShortcutter.class));
+        cards.add(new SetCardInfo("Gonti, Lord of Luxury", 240, Rarity.RARE, mage.cards.g.GontiLordOfLuxury.class));
         cards.add(new SetCardInfo("Grave Bramble", 401, Rarity.COMMON, mage.cards.g.GraveBramble.class));
         cards.add(new SetCardInfo("Gravewaker", 241, Rarity.RARE, mage.cards.g.Gravewaker.class));
         cards.add(new SetCardInfo("Grim Lavamancer", 331, Rarity.RARE, mage.cards.g.GrimLavamancer.class));
+        cards.add(new SetCardInfo("Gristle Grinner", 242, Rarity.UNCOMMON, mage.cards.g.GristleGrinner.class));
+        cards.add(new SetCardInfo("Guardian Idol", 467, Rarity.UNCOMMON, mage.cards.g.GuardianIdol.class));
+        cards.add(new SetCardInfo("Hamletback Goliath", 332, Rarity.RARE, mage.cards.h.HamletbackGoliath.class));
+        cards.add(new SetCardInfo("Harvester of Souls", 243, Rarity.RARE, mage.cards.h.HarvesterOfSouls.class));
         cards.add(new SetCardInfo("Healer's Hawk", 107, Rarity.COMMON, mage.cards.h.HealersHawk.class));
+        cards.add(new SetCardInfo("Heartfire", 333, Rarity.COMMON, mage.cards.h.Heartfire.class));
         cards.add(new SetCardInfo("Hedron Archive", 468, Rarity.UNCOMMON, mage.cards.h.HedronArchive.class));
+        cards.add(new SetCardInfo("Hellrider", 334, Rarity.RARE, mage.cards.h.Hellrider.class));
         cards.add(new SetCardInfo("Herald's Horn", 469, Rarity.UNCOMMON, mage.cards.h.HeraldsHorn.class));
         cards.add(new SetCardInfo("High Sentinels of Arashin", 108, Rarity.RARE, mage.cards.h.HighSentinelsOfArashin.class));
         cards.add(new SetCardInfo("Homing Lightning", 335, Rarity.UNCOMMON, mage.cards.h.HomingLightning.class));
         cards.add(new SetCardInfo("Hungry Flames", 336, Rarity.COMMON, mage.cards.h.HungryFlames.class));
         cards.add(new SetCardInfo("Hunter's Insight", 402, Rarity.UNCOMMON, mage.cards.h.HuntersInsight.class));
+        cards.add(new SetCardInfo("Indomitable Will", 109, Rarity.COMMON, mage.cards.i.IndomitableWill.class));
+        cards.add(new SetCardInfo("Inferno Hellion", 337, Rarity.UNCOMMON, mage.cards.i.InfernoHellion.class));
         cards.add(new SetCardInfo("Initiate's Companion", 403, Rarity.COMMON, mage.cards.i.InitiatesCompanion.class));
         cards.add(new SetCardInfo("Innocent Blood", 244, Rarity.COMMON, mage.cards.i.InnocentBlood.class));
         cards.add(new SetCardInfo("Inspired Charge", 110, Rarity.COMMON, mage.cards.i.InspiredCharge.class));
+        cards.add(new SetCardInfo("Inspiring Call", 404, Rarity.UNCOMMON, mage.cards.i.InspiringCall.class));
         cards.add(new SetCardInfo("Inspiring Captain", 111, Rarity.COMMON, mage.cards.i.InspiringCaptain.class));
         cards.add(new SetCardInfo("Inspiring Unicorn", 112, Rarity.UNCOMMON, mage.cards.i.InspiringUnicorn.class));
         cards.add(new SetCardInfo("Ironroot Warlord", 452, Rarity.UNCOMMON, mage.cards.i.IronrootWarlord.class));
+        cards.add(new SetCardInfo("Ironshell Beetle", 405, Rarity.COMMON, mage.cards.i.IronshellBeetle.class));
         cards.add(new SetCardInfo("Irresistible Prey", 406, Rarity.UNCOMMON, mage.cards.i.IrresistiblePrey.class));
         cards.add(new SetCardInfo("Isamaru, Hound of Konda", 113, Rarity.RARE, mage.cards.i.IsamaruHoundOfKonda.class));
-        cards.add(new SetCardInfo("Island", 48, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Island", 46, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Jousting Dummy", 470, Rarity.COMMON, mage.cards.j.JoustingDummy.class));
         cards.add(new SetCardInfo("Juggernaut", 471, Rarity.UNCOMMON, mage.cards.j.Juggernaut.class));
         cards.add(new SetCardInfo("Kalastria Nightwatch", 245, Rarity.COMMON, mage.cards.k.KalastriaNightwatch.class));
         cards.add(new SetCardInfo("Keeper of Fables", 407, Rarity.UNCOMMON, mage.cards.k.KeeperOfFables.class));
         cards.add(new SetCardInfo("Kels, Fight Fixer", 15, Rarity.RARE, mage.cards.k.KelsFightFixer.class));
+        cards.add(new SetCardInfo("Kiln Fiend", 338, Rarity.COMMON, mage.cards.k.KilnFiend.class));
         cards.add(new SetCardInfo("Kira, Great Glass-Spinner", 154, Rarity.RARE, mage.cards.k.KiraGreatGlassSpinner.class));
         cards.add(new SetCardInfo("Kitesail Corsair", 155, Rarity.COMMON, mage.cards.k.KitesailCorsair.class));
         cards.add(new SetCardInfo("Knight of the Tusk", 114, Rarity.COMMON, mage.cards.k.KnightOfTheTusk.class));
+        cards.add(new SetCardInfo("Knightly Valor", 115, Rarity.COMMON, mage.cards.k.KnightlyValor.class));
+        cards.add(new SetCardInfo("Kor Spiritdancer", 116, Rarity.RARE, mage.cards.k.KorSpiritdancer.class));
+        cards.add(new SetCardInfo("Krenko, Mob Boss", 339, Rarity.RARE, mage.cards.k.KrenkoMobBoss.class));
+        cards.add(new SetCardInfo("Languish", 246, Rarity.RARE, mage.cards.l.Languish.class));
         cards.add(new SetCardInfo("Last Gasp", 247, Rarity.COMMON, mage.cards.l.LastGasp.class));
+        cards.add(new SetCardInfo("Lathliss, Dragon Queen", 340, Rarity.RARE, mage.cards.l.LathlissDragonQueen.class));
+        cards.add(new SetCardInfo("Launch Party", 248, Rarity.COMMON, mage.cards.l.LaunchParty.class));
+        cards.add(new SetCardInfo("Lawless Broker", 249, Rarity.COMMON, mage.cards.l.LawlessBroker.class));
         cards.add(new SetCardInfo("Lawmage's Binding", 453, Rarity.COMMON, mage.cards.l.LawmagesBinding.class));
         cards.add(new SetCardInfo("Leaf Gilder", 408, Rarity.COMMON, mage.cards.l.LeafGilder.class));
         cards.add(new SetCardInfo("Leave in the Dust", 156, Rarity.COMMON, mage.cards.l.LeaveInTheDust.class));
+        cards.add(new SetCardInfo("Lena, Selfless Champion", 117, Rarity.RARE, mage.cards.l.LenaSelflessChampion.class));
+        cards.add(new SetCardInfo("Lifecrafter's Gift", 409, Rarity.UNCOMMON, mage.cards.l.LifecraftersGift.class));
         cards.add(new SetCardInfo("Lightning Axe", 341, Rarity.UNCOMMON, mage.cards.l.LightningAxe.class));
         cards.add(new SetCardInfo("Lightning Bolt", 342, Rarity.UNCOMMON, mage.cards.l.LightningBolt.class));
         cards.add(new SetCardInfo("Lightning Diadem", 343, Rarity.COMMON, mage.cards.l.LightningDiadem.class));
@@ -174,6 +255,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Lightwalker", 118, Rarity.COMMON, mage.cards.l.Lightwalker.class));
         cards.add(new SetCardInfo("Liliana's Elite", 250, Rarity.UNCOMMON, mage.cards.l.LilianasElite.class));
         cards.add(new SetCardInfo("Liliana's Reaver", 251, Rarity.RARE, mage.cards.l.LilianasReaver.class));
+        cards.add(new SetCardInfo("Linvala, Keeper of Silence", 119, Rarity.MYTHIC, mage.cards.l.LinvalaKeeperOfSilence.class));
         cards.add(new SetCardInfo("Living Lightning", 23, Rarity.UNCOMMON, mage.cards.l.LivingLightning.class));
         cards.add(new SetCardInfo("Long Road Home", 120, Rarity.UNCOMMON, mage.cards.l.LongRoadHome.class));
         cards.add(new SetCardInfo("Lurking Predators", 410, Rarity.RARE, mage.cards.l.LurkingPredators.class));
@@ -181,36 +263,50 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Maelstrom Archangel", 454, Rarity.MYTHIC, mage.cards.m.MaelstromArchangel.class));
         cards.add(new SetCardInfo("Magma Jet", 346, Rarity.UNCOMMON, mage.cards.m.MagmaJet.class));
         cards.add(new SetCardInfo("Magmaquake", 347, Rarity.RARE, mage.cards.m.Magmaquake.class));
+        cards.add(new SetCardInfo("Makeshift Munitions", 348, Rarity.UNCOMMON, mage.cards.m.MakeshiftMunitions.class));
         cards.add(new SetCardInfo("Malakir Familiar", 253, Rarity.UNCOMMON, mage.cards.m.MalakirFamiliar.class));
         cards.add(new SetCardInfo("Mana Geode", 472, Rarity.COMMON, mage.cards.m.ManaGeode.class));
         cards.add(new SetCardInfo("Marauder's Axe", 473, Rarity.COMMON, mage.cards.m.MaraudersAxe.class));
         cards.add(new SetCardInfo("Mark of the Vampire", 254, Rarity.COMMON, mage.cards.m.MarkOfTheVampire.class));
+        cards.add(new SetCardInfo("Mausoleum Turnkey", 255, Rarity.UNCOMMON, mage.cards.m.MausoleumTurnkey.class));
         cards.add(new SetCardInfo("Mentor of the Meek", 121, Rarity.RARE, mage.cards.m.MentorOfTheMeek.class));
         cards.add(new SetCardInfo("Mesa Unicorn", 122, Rarity.COMMON, mage.cards.m.MesaUnicorn.class));
         cards.add(new SetCardInfo("Meteor Golem", 474, Rarity.UNCOMMON, mage.cards.m.MeteorGolem.class));
         cards.add(new SetCardInfo("Miasmic Mummy", 256, Rarity.COMMON, mage.cards.m.MiasmicMummy.class));
         cards.add(new SetCardInfo("Mikaeus, the Lunarch", 123, Rarity.MYTHIC, mage.cards.m.MikaeusTheLunarch.class));
+        cards.add(new SetCardInfo("Minotaur Skullcleaver", 349, Rarity.COMMON, mage.cards.m.MinotaurSkullcleaver.class));
+        cards.add(new SetCardInfo("Minotaur Sureshot", 350, Rarity.COMMON, mage.cards.m.MinotaurSureshot.class));
         cards.add(new SetCardInfo("Mire Triton", 257, Rarity.UNCOMMON, mage.cards.m.MireTriton.class));
         cards.add(new SetCardInfo("Mirrodin's Core", 492, Rarity.UNCOMMON, mage.cards.m.MirrodinsCore.class));
         cards.add(new SetCardInfo("Molten Ravager", 351, Rarity.COMMON, mage.cards.m.MoltenRavager.class));
         cards.add(new SetCardInfo("Moment of Heroism", 124, Rarity.COMMON, mage.cards.m.MomentOfHeroism.class));
         cards.add(new SetCardInfo("Momentous Fall", 411, Rarity.RARE, mage.cards.m.MomentousFall.class));
-        cards.add(new SetCardInfo("Mountain", 64, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Mountain", 62, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Mugging", 352, Rarity.COMMON, mage.cards.m.Mugging.class));
+        cards.add(new SetCardInfo("Murmuring Phantasm", 157, Rarity.COMMON, mage.cards.m.MurmuringPhantasm.class));
         cards.add(new SetCardInfo("Myr Sire", 475, Rarity.COMMON, mage.cards.m.MyrSire.class));
         cards.add(new SetCardInfo("Mystic Archaeologist", 158, Rarity.RARE, mage.cards.m.MysticArchaeologist.class));
         cards.add(new SetCardInfo("Narcolepsy", 159, Rarity.COMMON, mage.cards.n.Narcolepsy.class));
         cards.add(new SetCardInfo("Nature's Way", 412, Rarity.UNCOMMON, mage.cards.n.NaturesWay.class));
+        cards.add(new SetCardInfo("Nebelgast Herald", 160, Rarity.UNCOMMON, mage.cards.n.NebelgastHerald.class));
+        cards.add(new SetCardInfo("Nessian Hornbeetle", 413, Rarity.UNCOMMON, mage.cards.n.NessianHornbeetle.class));
         cards.add(new SetCardInfo("New Horizons", 414, Rarity.COMMON, mage.cards.n.NewHorizons.class));
+        cards.add(new SetCardInfo("Nightshade Stinger", 258, Rarity.COMMON, mage.cards.n.NightshadeStinger.class));
         cards.add(new SetCardInfo("Nocturnal Feeder", 16, Rarity.COMMON, mage.cards.n.NocturnalFeeder.class));
         cards.add(new SetCardInfo("Nyxathid", 259, Rarity.RARE, mage.cards.n.Nyxathid.class));
         cards.add(new SetCardInfo("Octoprophet", 161, Rarity.COMMON, mage.cards.o.Octoprophet.class));
+        cards.add(new SetCardInfo("Ogre Slumlord", 260, Rarity.RARE, mage.cards.o.OgreSlumlord.class));
         cards.add(new SetCardInfo("Oneirophage", 162, Rarity.UNCOMMON, mage.cards.o.Oneirophage.class));
+        cards.add(new SetCardInfo("Oona's Blackguard", 261, Rarity.UNCOMMON, mage.cards.o.OonasBlackguard.class));
         cards.add(new SetCardInfo("Oracle of Mul Daya", 415, Rarity.RARE, mage.cards.o.OracleOfMulDaya.class));
         cards.add(new SetCardInfo("Orazca Frillback", 416, Rarity.COMMON, mage.cards.o.OrazcaFrillback.class));
         cards.add(new SetCardInfo("Ormos, Archive Keeper", 13, Rarity.RARE, mage.cards.o.OrmosArchiveKeeper.class));
+        cards.add(new SetCardInfo("Ornery Goblin", 353, Rarity.COMMON, mage.cards.o.OrneryGoblin.class));
+        cards.add(new SetCardInfo("Outnumber", 354, Rarity.COMMON, mage.cards.o.Outnumber.class));
         cards.add(new SetCardInfo("Overgrown Battlement", 417, Rarity.UNCOMMON, mage.cards.o.OvergrownBattlement.class));
         cards.add(new SetCardInfo("Pacifism", 125, Rarity.COMMON, mage.cards.p.Pacifism.class));
         cards.add(new SetCardInfo("Parasitic Implant", 262, Rarity.COMMON, mage.cards.p.ParasiticImplant.class));
+        cards.add(new SetCardInfo("Path of Bravery", 126, Rarity.RARE, mage.cards.p.PathOfBravery.class));
         cards.add(new SetCardInfo("Path to Exile", 127, Rarity.UNCOMMON, mage.cards.p.PathToExile.class));
         cards.add(new SetCardInfo("Patron of the Valiant", 128, Rarity.UNCOMMON, mage.cards.p.PatronOfTheValiant.class));
         cards.add(new SetCardInfo("Peel from Reality", 163, Rarity.COMMON, mage.cards.p.PeelFromReality.class));
@@ -224,27 +320,36 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Phyrexian Tower", 493, Rarity.RARE, mage.cards.p.PhyrexianTower.class));
         cards.add(new SetCardInfo("Pillar of Flame", 355, Rarity.COMMON, mage.cards.p.PillarOfFlame.class));
         cards.add(new SetCardInfo("Pirate's Cutlass", 477, Rarity.COMMON, mage.cards.p.PiratesCutlass.class));
+        cards.add(new SetCardInfo("Plagued Rusalka", 268, Rarity.COMMON, mage.cards.p.PlaguedRusalka.class));
         cards.add(new SetCardInfo("Plains", 38, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Pouncing Cheetah", 419, Rarity.COMMON, mage.cards.p.PouncingCheetah.class));
         cards.add(new SetCardInfo("Prescient Chimera", 164, Rarity.COMMON, mage.cards.p.PrescientChimera.class));
         cards.add(new SetCardInfo("Presence of Gond", 420, Rarity.COMMON, mage.cards.p.PresenceOfGond.class));
+        cards.add(new SetCardInfo("Primeval Bounty", 421, Rarity.MYTHIC, mage.cards.p.PrimevalBounty.class));
         cards.add(new SetCardInfo("Primordial Sage", 422, Rarity.RARE, mage.cards.p.PrimordialSage.class));
         cards.add(new SetCardInfo("Prophetic Prism", 478, Rarity.COMMON, mage.cards.p.PropheticPrism.class));
         cards.add(new SetCardInfo("Prosperous Pirates", 165, Rarity.COMMON, mage.cards.p.ProsperousPirates.class));
         cards.add(new SetCardInfo("Pyroclastic Elemental", 356, Rarity.UNCOMMON, mage.cards.p.PyroclasticElemental.class));
         cards.add(new SetCardInfo("Rageblood Shaman", 357, Rarity.RARE, mage.cards.r.RagebloodShaman.class));
         cards.add(new SetCardInfo("Raging Regisaur", 455, Rarity.UNCOMMON, mage.cards.r.RagingRegisaur.class));
+        cards.add(new SetCardInfo("Raise the Alarm", 129, Rarity.COMMON, mage.cards.r.RaiseTheAlarm.class));
         cards.add(new SetCardInfo("Rampaging Brontodon", 423, Rarity.RARE, mage.cards.r.RampagingBrontodon.class));
+        cards.add(new SetCardInfo("Rapacious Dragon", 358, Rarity.UNCOMMON, mage.cards.r.RapaciousDragon.class));
+        cards.add(new SetCardInfo("Rattlechains", 166, Rarity.RARE, mage.cards.r.Rattlechains.class));
         cards.add(new SetCardInfo("Ravenous Baloth", 424, Rarity.RARE, mage.cards.r.RavenousBaloth.class));
         cards.add(new SetCardInfo("Ravenous Chupacabra", 269, Rarity.UNCOMMON, mage.cards.r.RavenousChupacabra.class));
         cards.add(new SetCardInfo("Read the Runes", 167, Rarity.RARE, mage.cards.r.ReadTheRunes.class));
         cards.add(new SetCardInfo("Reanimate", 270, Rarity.RARE, mage.cards.r.Reanimate.class));
+        cards.add(new SetCardInfo("Reckless Scholar", 168, Rarity.UNCOMMON, mage.cards.r.RecklessScholar.class));
         cards.add(new SetCardInfo("Release the Dogs", 4, Rarity.UNCOMMON, mage.cards.r.ReleaseTheDogs.class));
+        cards.add(new SetCardInfo("Rhox Faithmender", 130, Rarity.RARE, mage.cards.r.RhoxFaithmender.class));
         cards.add(new SetCardInfo("Rhystic Study", 169, Rarity.RARE, mage.cards.r.RhysticStudy.class));
         cards.add(new SetCardInfo("Riddle of Lightning", 359, Rarity.UNCOMMON, mage.cards.r.RiddleOfLightning.class));
         cards.add(new SetCardInfo("Riptide Laboratory", 494, Rarity.RARE, mage.cards.r.RiptideLaboratory.class));
         cards.add(new SetCardInfo("Rise of the Dark Realms", 271, Rarity.MYTHIC, mage.cards.r.RiseOfTheDarkRealms.class));
         cards.add(new SetCardInfo("Rishadan Airship", 170, Rarity.COMMON, mage.cards.r.RishadanAirship.class));
+        cards.add(new SetCardInfo("Rishkar, Peema Renegade", 425, Rarity.RARE, mage.cards.r.RishkarPeemaRenegade.class));
+        cards.add(new SetCardInfo("Rogue's Gloves", 479, Rarity.UNCOMMON, mage.cards.r.RoguesGloves.class));
         cards.add(new SetCardInfo("Ronom Unicorn", 131, Rarity.COMMON, mage.cards.r.RonomUnicorn.class));
         cards.add(new SetCardInfo("Roving Keep", 480, Rarity.COMMON, mage.cards.r.RovingKeep.class));
         cards.add(new SetCardInfo("Rumbling Baloth", 426, Rarity.COMMON, mage.cards.r.RumblingBaloth.class));
@@ -253,15 +358,22 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Sage's Row Savant", 171, Rarity.COMMON, mage.cards.s.SagesRowSavant.class));
         cards.add(new SetCardInfo("Sailor of Means", 172, Rarity.COMMON, mage.cards.s.SailorOfMeans.class));
         cards.add(new SetCardInfo("Sangromancer", 272, Rarity.RARE, mage.cards.s.Sangromancer.class));
+        cards.add(new SetCardInfo("Sanitarium Skeleton", 273, Rarity.COMMON, mage.cards.s.SanitariumSkeleton.class));
+        cards.add(new SetCardInfo("Sarkhan's Rage", 360, Rarity.COMMON, mage.cards.s.SarkhansRage.class));
+        cards.add(new SetCardInfo("Sarkhan's Unsealing", 361, Rarity.RARE, mage.cards.s.SarkhansUnsealing.class));
         cards.add(new SetCardInfo("Savage Stomp", 427, Rarity.UNCOMMON, mage.cards.s.SavageStomp.class));
         cards.add(new SetCardInfo("Scarecrone", 482, Rarity.RARE, mage.cards.s.Scarecrone.class));
         cards.add(new SetCardInfo("Scourge of Nel Toth", 274, Rarity.RARE, mage.cards.s.ScourgeOfNelToth.class));
+        cards.add(new SetCardInfo("Scroll of Avacyn", 483, Rarity.COMMON, mage.cards.s.ScrollOfAvacyn.class));
+        cards.add(new SetCardInfo("Scrounging Bandar", 428, Rarity.COMMON, mage.cards.s.ScroungingBandar.class));
         cards.add(new SetCardInfo("Scuttlemutt", 484, Rarity.UNCOMMON, mage.cards.s.Scuttlemutt.class));
         cards.add(new SetCardInfo("Sea Gate Oracle", 173, Rarity.COMMON, mage.cards.s.SeaGateOracle.class));
         cards.add(new SetCardInfo("Seismic Elemental", 362, Rarity.UNCOMMON, mage.cards.s.SeismicElemental.class));
+        cards.add(new SetCardInfo("Selhoff Occultist", 174, Rarity.COMMON, mage.cards.s.SelhoffOccultist.class));
         cards.add(new SetCardInfo("Selvala, Heart of the Wilds", 429, Rarity.MYTHIC, mage.cards.s.SelvalaHeartOfTheWilds.class));
         cards.add(new SetCardInfo("Sengir Vampire", 275, Rarity.UNCOMMON, mage.cards.s.SengirVampire.class));
         cards.add(new SetCardInfo("Serendib Efreet", 175, Rarity.RARE, mage.cards.s.SerendibEfreet.class));
+        cards.add(new SetCardInfo("Serra Angel", 132, Rarity.UNCOMMON, mage.cards.s.SerraAngel.class));
         cards.add(new SetCardInfo("Settle the Score", 276, Rarity.UNCOMMON, mage.cards.s.SettleTheScore.class));
         cards.add(new SetCardInfo("Shambling Goblin", 277, Rarity.COMMON, mage.cards.s.ShamblingGoblin.class));
         cards.add(new SetCardInfo("Sharding Sphinx", 176, Rarity.RARE, mage.cards.s.ShardingSphinx.class));
@@ -269,6 +381,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Sigiled Starfish", 177, Rarity.UNCOMMON, mage.cards.s.SigiledStarfish.class));
         cards.add(new SetCardInfo("Signpost Scarecrow", 485, Rarity.COMMON, mage.cards.s.SignpostScarecrow.class));
         cards.add(new SetCardInfo("Silhana Wayfinder", 430, Rarity.UNCOMMON, mage.cards.s.SilhanaWayfinder.class));
+        cards.add(new SetCardInfo("Sin Prodder", 363, Rarity.RARE, mage.cards.s.SinProdder.class));
         cards.add(new SetCardInfo("Skittering Surveyor", 486, Rarity.COMMON, mage.cards.s.SkitteringSurveyor.class));
         cards.add(new SetCardInfo("Sky Tether", 133, Rarity.UNCOMMON, mage.cards.s.SkyTether.class));
         cards.add(new SetCardInfo("Slate Street Ruffian", 279, Rarity.COMMON, mage.cards.s.SlateStreetRuffian.class));
@@ -278,6 +391,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Spectral Sailor", 178, Rarity.UNCOMMON, mage.cards.s.SpectralSailor.class));
         cards.add(new SetCardInfo("Spitting Earth", 364, Rarity.COMMON, mage.cards.s.SpittingEarth.class));
         cards.add(new SetCardInfo("Sporemound", 433, Rarity.COMMON, mage.cards.s.Sporemound.class));
+        cards.add(new SetCardInfo("Stab Wound", 281, Rarity.UNCOMMON, mage.cards.s.StabWound.class));
         cards.add(new SetCardInfo("Storm Sculptor", 179, Rarity.COMMON, mage.cards.s.StormSculptor.class));
         cards.add(new SetCardInfo("Supply Runners", 7, Rarity.UNCOMMON, mage.cards.s.SupplyRunners.class));
         cards.add(new SetCardInfo("Suspicious Bookcase", 487, Rarity.UNCOMMON, mage.cards.s.SuspiciousBookcase.class));
@@ -293,7 +407,10 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Tempting Witch", 283, Rarity.UNCOMMON, mage.cards.t.TemptingWitch.class));
         cards.add(new SetCardInfo("Terramorphic Expanse", 78, Rarity.COMMON, mage.cards.t.TerramorphicExpanse.class));
         cards.add(new SetCardInfo("Terrarion", 488, Rarity.COMMON, mage.cards.t.Terrarion.class));
+        cards.add(new SetCardInfo("Thermo-Alchemist", 365, Rarity.COMMON, mage.cards.t.ThermoAlchemist.class));
         cards.add(new SetCardInfo("Thirst for Knowledge", 183, Rarity.UNCOMMON, mage.cards.t.ThirstForKnowledge.class));
+        cards.add(new SetCardInfo("Thought Collapse", 184, Rarity.COMMON, mage.cards.t.ThoughtCollapse.class));
+        cards.add(new SetCardInfo("Thought Scour", 185, Rarity.COMMON, mage.cards.t.ThoughtScour.class));
         cards.add(new SetCardInfo("Thragtusk", 436, Rarity.RARE, mage.cards.t.Thragtusk.class));
         cards.add(new SetCardInfo("Thriving Bluff", 33, Rarity.COMMON, mage.cards.t.ThrivingBluff.class));
         cards.add(new SetCardInfo("Thriving Grove", 34, Rarity.COMMON, mage.cards.t.ThrivingGrove.class));
@@ -301,9 +418,12 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Thriving Isle", 36, Rarity.COMMON, mage.cards.t.ThrivingIsle.class));
         cards.add(new SetCardInfo("Thriving Moor", 37, Rarity.COMMON, mage.cards.t.ThrivingMoor.class));
         cards.add(new SetCardInfo("Thundering Spineback", 437, Rarity.UNCOMMON, mage.cards.t.ThunderingSpineback.class));
+        cards.add(new SetCardInfo("Tibalt's Rager", 366, Rarity.UNCOMMON, mage.cards.t.TibaltsRager.class));
         cards.add(new SetCardInfo("Time to Feed", 438, Rarity.COMMON, mage.cards.t.TimeToFeed.class));
         cards.add(new SetCardInfo("Tithebearer Giant", 284, Rarity.COMMON, mage.cards.t.TithebearerGiant.class));
+        cards.add(new SetCardInfo("Torch Fiend", 367, Rarity.COMMON, mage.cards.t.TorchFiend.class));
         cards.add(new SetCardInfo("Towering Titan", 31, Rarity.MYTHIC, mage.cards.t.ToweringTitan.class));
+        cards.add(new SetCardInfo("Towering-Wave Mystic", 186, Rarity.COMMON, mage.cards.t.ToweringWaveMystic.class));
         cards.add(new SetCardInfo("Trusty Retriever", 8, Rarity.COMMON, mage.cards.t.TrustyRetriever.class));
         cards.add(new SetCardInfo("Ulvenwald Hydra", 439, Rarity.MYTHIC, mage.cards.u.UlvenwaldHydra.class));
         cards.add(new SetCardInfo("Unstable Obelisk", 489, Rarity.UNCOMMON, mage.cards.u.UnstableObelisk.class));
@@ -311,13 +431,18 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Vampire Neonate", 285, Rarity.COMMON, mage.cards.v.VampireNeonate.class));
         cards.add(new SetCardInfo("Vastwood Zendikon", 440, Rarity.COMMON, mage.cards.v.VastwoodZendikon.class));
         cards.add(new SetCardInfo("Vedalken Archmage", 187, Rarity.RARE, mage.cards.v.VedalkenArchmage.class));
+        cards.add(new SetCardInfo("Vedalken Entrancer", 188, Rarity.COMMON, mage.cards.v.VedalkenEntrancer.class));
         cards.add(new SetCardInfo("Verdant Embrace", 441, Rarity.RARE, mage.cards.v.VerdantEmbrace.class));
+        cards.add(new SetCardInfo("Voice of the Provinces", 137, Rarity.COMMON, mage.cards.v.VoiceOfTheProvinces.class));
         cards.add(new SetCardInfo("Volcanic Fallout", 368, Rarity.UNCOMMON, mage.cards.v.VolcanicFallout.class));
+        cards.add(new SetCardInfo("Volley Veteran", 369, Rarity.UNCOMMON, mage.cards.v.VolleyVeteran.class));
         cards.add(new SetCardInfo("Voyage's End", 189, Rarity.COMMON, mage.cards.v.VoyagesEnd.class));
         cards.add(new SetCardInfo("Wailing Ghoul", 286, Rarity.COMMON, mage.cards.w.WailingGhoul.class));
         cards.add(new SetCardInfo("Wall of Blossoms", 442, Rarity.UNCOMMON, mage.cards.w.WallOfBlossoms.class));
+        cards.add(new SetCardInfo("Wall of Lost Thoughts", 190, Rarity.UNCOMMON, mage.cards.w.WallOfLostThoughts.class));
         cards.add(new SetCardInfo("Wall of Vines", 443, Rarity.COMMON, mage.cards.w.WallOfVines.class));
         cards.add(new SetCardInfo("Warden of Evos Isle", 191, Rarity.UNCOMMON, mage.cards.w.WardenOfEvosIsle.class));
+        cards.add(new SetCardInfo("Warfire Javelineer", 370, Rarity.UNCOMMON, mage.cards.w.WarfireJavelineer.class));
         cards.add(new SetCardInfo("Warmonger's Chariot", 490, Rarity.UNCOMMON, mage.cards.w.WarmongersChariot.class));
         cards.add(new SetCardInfo("Waterknot", 192, Rarity.COMMON, mage.cards.w.Waterknot.class));
         cards.add(new SetCardInfo("Weaver of Lightning", 371, Rarity.UNCOMMON, mage.cards.w.WeaverOfLightning.class));
diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt
index e92660fc54..d824061739 100644
--- a/Utils/mtg-cards-data.txt
+++ b/Utils/mtg-cards-data.txt
@@ -37511,7 +37511,7 @@ Shacklegeist|Core Set 2021|70|R|{1}{U}|Creature - Spirit|2|2|Flying$Shacklegeist
 Shipwreck Dowser|Core Set 2021|71|U|{3}{U}{U}|Creature - Merfolk Wizard|3|3|Prowess$When Shipwreck Dowser enters the battlefield, return target instant or sorcery card from your graveyard to your hand.|
 Spined Megalodon|Core Set 2021|72|C|{5}{U}{U}|Creature - Shark|5|7|Hexproof$Whenever Spined Megalodon attacks, scry 1.|
 Stormwing Entity|Core Set 2021|73|R|{3}{U}{U}|Creature - Elemental|3|3|This spell costs {2}{U} less to cast if you've cast an instant or sorcery spell this turn.$Flying$Prowess$When Stormwing Entity enters the battlefield, scry 2.|
-Sublime Epiphany|Core Set 2021|74|R|{4}{U}{U}|Instant|||Choose one or more —$• Counter target spell$• Counter target activated or triggered ability.$• Return target nonland permanent to its owner's hand.$• Create a token that's a copy of target creature you control.$• Target player draws a card.|
+Sublime Epiphany|Core Set 2021|74|R|{4}{U}{U}|Instant|||Choose one or more —$• Counter target spell.$• Counter target activated or triggered ability.$• Return target nonland permanent to its owner's hand.$• Create a token that's a copy of target creature you control.$• Target player draws a card.|
 Teferi, Master of Time|Core Set 2021|75|M|{2}{U}{U}|Legendary Planeswalker - Teferi|3|You may activate loyalty abilities of Teferi, Master of Time on any player's turn any time you could cast an instant.$+1: Draw a card, then discard a card.$−3: Target creature you don't control phases out.$−10: Take two extra turns after this one.|
 Teferi's Ageless Insight|Core Set 2021|76|R|{2}{U}{U}|Legendary Enchantment|||If you would draw a card except the first one you draw in each of your draw steps, draw two cards instead.|
 Teferi's Protege|Core Set 2021|77|C|{2}{U}|Creature - Human Wizard|2|3|{1}{U}, {T}: Draw a card, then discard a card.|
@@ -37723,9 +37723,6 @@ Garruk, Savage Herald|Core Set 2021|336|M|{4}{G}{G}|Legendary Planeswalker - Gar
 Garruk's Warsteed|Core Set 2021|337|R|{3}{G}{G}|Creature - Rhino|3|5|Vigilance$When Garruk's Warsteed enters the battlefield, you may search your library and/or graveyard for a card named Garruk, Savage Herald, reveal it, and put it into your hand. If you search your library this way, shuffle it.|
 Predatory Wurm|Core Set 2021|338|U|{3}{G}|Creature - Wurm|4|4|Vigilance$Predatory Wurm gets +2/+2 as long as you control a Garruk planeswalker.|
 Wildwood Patrol|Core Set 2021|339|C|{2}{G}|Creature - Centaur Scout|4|2|Trample|
-Fortify|Jumpstart|FOR|C|{2}{W}|Instant|||Choose one —$• Creatures you control get +2/+0 until end of turn.$• Creatures you control get +0/+2 until end of turn.|
-Lena, Selfless Champion|Jumpstart|LSC|R|{4}{W}{W}|Legendary Creature - Human Knight|3|3|When Lena, Selfless Champion enters the battlefield, create a 1/1 white Soldier creature token for each nontoken creature you control.$Sacrifice Lena: Creatures you control with power less than Lena's power gain indestructible until end of turn.|
-Raise the Alarm|Jumpstart|RTA|C|{1}{W}|Instant|||Create two 1/1 white Soldier creature tokens.|
 Blessed Sanctuary|Jumpstart|1|R|{3}{W}{W}|Enchantment|||Prevent all noncombat damage that would be dealt to you and creatures you control.$Whenever a nontoken creature enters the battlefield under your control, create a 2/2 white Unicorn creature token.|
 Brightmare|Jumpstart|2|U|{2}{W}|Creature - Unicorn|2|3|When Brightmare enters the battlefield, tap up to one target creature. You gain life equal to that creature's power.|
 Emiel the Blessed|Jumpstart|3|M|{2}{W}{W}|Legendary Creature - Unicorn|4|4|{3}: Exile another target creature you control, then return it to the battlefield under its owner's control.$Whenever another creature enters the battlefield under your control, you may pay {G/W}. If you do, put a +1/+1 counter on it. If it's a Unicorn, put two +1/+1 counters on it instead.|
@@ -37735,6 +37732,7 @@ Stone Haven Pilgrim|Jumpstart|6|U|{1}{W}|Creature - Kor Cleric|2|2|Whenever Ston
 Supply Runners|Jumpstart|7|U|{4}{W}|Creature - Dog|2|2|When Supply Runners enters the battlefield, put a +1/+1 counter on each other creature you control.|
 Trusty Retriever|Jumpstart|8|C|{3}{W}|Creature - Dog|2|3|When Trusty Retriever enters the battlefield, choose one —$• Put a +1/+1 counter on Trusty Retriever.$• Return target artifact or enchantment card from your graveyard to your hand.|
 Archaeomender|Jumpstart|9|C|{2}{U}|Creature - Human Wizard|2|3|When Archaeomender enters the battlefield, return target artifact card from your graveyard to your hand.|
+Bruvac the Grandiloquent|Jumpstart|10|M|{2}{U}|Legendary Creature - Human Advisor|1|4|If an opponent would mill one or more cards, they mill twice that many cards insted.|
 Corsair Captain|Jumpstart|11|R|{2}{U}|Creature - Human Pirate|2|2|When Corsair Captain enters the battlefield, create a treasure token.$Other Pirates you control get +1/+1.|
 Inniaz, the Gale Force|Jumpstart|12|R|{3}{U}{U}|Legendary Creature - Djinn|4|4|Flying${2}{W/U}: Attacking creatures with flying get +1/+1 until end of turn.$Whenever three or more creatures you control with flying attack, each player gains control of a nonland permanent of your choice controlled by the player to their right.|
 Ormos, Archive Keeper|Jumpstart|13|R|{4}{U}{U}|Legendary Creature - Sphinx|5|5|Flying$If you would draw a card while your library has no cards in it, instead put five +1/+1 counters on Ormos, Archive Keeper.${1}{U}{U}, Discard three cards with different names: Draw five cards.|
@@ -37743,13 +37741,17 @@ Kels, Fight Fixer|Jumpstart|15|R|{2}{B}{B}|Legendary Creature - Azra Warlock|4|3
 Nocturnal Feeder|Jumpstart|16|C|{2}{B}|Creature - Vampire Rogue|2|1|Flying$When Nocturnal Feeder dies, each opponent loses 2 life and you gain 2 life.|
 Tinybones, Trinket Thief|Jumpstart|17|M|{1}{B}|Legendary Creature - Skeleton Rogue|1|2|At the beginning of each end step, if an opponent discarded a card this turn, you draw a card and you lose 1 life.${4}{B}{B}: Each opponent with no cards in hand loses 10 life.|
 Witch of the Moors|Jumpstart|18|R|{3}{B}{B}|Creature - Human Warlock|4|4|Deathtouch$At the beginning of your end step, if you gained life this turn, each opponent sacrifices a creature and you return up to one target creature card from your graveyard to your hand.|
+Chained Brute|Jumpstart|19|U|{1}{R}|Creature - Devil|4|3|Chained Brute doesn't untap during your untap step.${1}, Sacrifice another creature: Untap Chained Brute. Activate this ability only during your turn.|
+Immolating Gyre|Jumpstart|20|M|{4}{R}{R}|Sorcery|||Immolating Gyre deals X damage to each creature and planeswalker you don't control, where X is the number of instant and sorcery cards in your graveyard,|
 Lightning Phoenix|Jumpstart|21|R|{2}{R}|Creature - Phoenix|2|2|Flying, haste$Lightning Phoenix can't block.$At the beginning of your end step, if an opponent was dealt 3 or more damage this turn, you may pay {R}. If you do, return Lightning Phoenix from your graveyard to the battlefield.|
 Lightning Visionary|Jumpstart|22|C|{1}{R}|Creature - Minotaur Shaman|2|1|Prowess|
 Living Lightning|Jumpstart|23|U|{3}{R}|Creature - Elemental Shaman|3|2|When Living Lightning dies, return target instant or sorcery card from your graveyard to your hand.|
 Muxus, Goblin Grandee|Jumpstart|24|R|{4}{R}{R}|Legendary Creature - Goblin Noble|4|4|When Muxus, Goblin Grandee enters the battlefield, reveal the top six cards of your library. Put all Goblin creature cards with converted mana cost 5 or less from among them onto the battlefield and the rest on the bottom of your library in a random order.$Whenever Muxus attacks, it gets +1/+1 until end of turn for each other Goblin you control.|
 Sethron, Hurloon General|Jumpstart|25|R|{3}{R}{R}|Legendary Creature - Minotaur Warrior|4|4|Whenever Sethron, Hurloon General or another nontoken Minotaur enters the battlefield under your control, create a 2/3 red Minotaur creature token.${2}{B/R}: Minotaurs you control get +1/+0 and gain menace and haste until end of turn.|
 Spiteful Prankster|Jumpstart|26|U|{2}{R}|Creature - Devil|3|2|As long as it's your turn, Spiteful Prankster has first strike.$Whenever another creature dies, Spiteful Prankster deals 1 damage to target player or planeswalker.|
+Zurzoth, Chaos Rider|Jumpstart|27|R|{2}{R}|Legendary Creature - Devil|2|3|Whenever an opponent draws their first card each turn, if it's not their turn, you create a 1/1 red Devil creature token with "When this creature dies, it deals 1 damage to any target."$Whenever one or more Devils you control attack one or more players, you and those players each draw a card, then discard a card at random.|
 Allosaurus Shepherd|Jumpstart|28|M|{G}|Creature - Elf Shaman|1|1|Allosaurus Shepherd can't be countered.$Green spells you control can't be countered.${4}{G}{G}: Until end of turn, each Elf creature you control has base power and toughness 5/5 and becomes a Dinosaur in addition to its other creature types.|
+Branching Evolution|Jumpstart|29|R|{2}{G}|Enchantment|||If one or more +1/+1 counters would be put a on a creature you control, twice that many +1/+1 counters are put on that creature instead.|
 Neyith of the Dire Hunt|Jumpstart|30|R|{2}{G}{G}|Legendary Creature - Human Warrior|3|3|Whenever one or more creatures you control fight or become blocked, draw a card.$At the beginning of combat on your turn, you may pay {2}{R/G}. If you do, double target creature's power until end of turn. That creature must be blocked this combat if able.|
 Towering Titan|Jumpstart|31|M|{4}{G}{G}|Creature - Giant|0|0|Towering Titan enters the battlefield with X +1/+1 counters on it, where X is the total toughness of other creatures you control.$Sacrifice a creature with defender: All creatures gain trample until end of turn.|
 Lightning-Core Excavator|Jumpstart|32|C|{1}|Artifact Creature - Golem|0|3|{5}, {T}, Sacrifice Lightning-Core Excavator: It deals 3 damage to any target.|
@@ -37759,48 +37761,74 @@ Thriving Heath|Jumpstart|35|C||Land|||Thriving Heath enters the battlefield tapp
 Thriving Isle|Jumpstart|36|C||Land|||Thriving Isle enters the battlefield tapped.$As Thriving Isle enters the battlefield, choose a color other than blue.${T}: Add {U} or one mana of the chosen color.|
 Thriving Moor|Jumpstart|37|C||Land|||Thriving Moor enters the battlefield tapped.$As Thriving Moor enters the battlefield, choose a color other than black.${T}: Add {B} or one mana of the chosen color.|
 Plains|Jumpstart|38|C||Basic Land - Plains|||({T}: Add {W}.)|
-Island|Jumpstart|48|C||Basic Land - Island|||({T}: Add {U}.)|
+Island|Jumpstart|46|C||Basic Land - Island|||({T}: Add {U}.)|
 Swamp|Jumpstart|54|C||Basic Land - Swamp|||({T}: Add {B}.)|
-Mountain|Jumpstart|64|C||Basic Land - Mountain|||({T}: Add {R}.)|
+Mountain|Jumpstart|62|C||Basic Land - Mountain|||({T}: Add {R}.)|
 Forest|Jumpstart|70|C||Basic Land - Forest|||({T}: Add {G}.)|
 Terramorphic Expanse|Jumpstart|78|C||Land|||{T}, Sacrifice Terramorphic Expanse: Search your library for a basic land card, put it onto the battlefield tapped, then shuffle your library.|
 Aegis of the Heavens|Jumpstart|79|U|{1}{W}|Instant|||Target creature gets +1/+7 until end of turn.|
 Aerial Assault|Jumpstart|80|C|{2}{W}|Sorcery|||Destroy target tapped creature. You gain 1 life for each creature you control with flying.|
 Affa Guard Hound|Jumpstart|81|U|{2}{W}|Creature - Hound|2|2|Flash$When Affa Guard Hound enters the battlefield, target creature gets +0/+3 until end of turn.|
+Ajani's Chosen|Jumpstart|82|R|{2}{W}{W}|Creature - Cat Soldier|3|3|Whenever an enchantment enters the battlefield under your control, create a 2/2 white Cat creature token. If that enchantment is an Aura, you may attach it to the token.|
+Alabaster Mage|Jumpstart|83|U|{1}{W}|Creature - Human Wizard|2|1|{1}{W}: Target creature you control gains lifelink until end of turn.|
+Angel of Mercy|Jumpstart|84|C|{4}{W}|Creature - Angel|3|3|Flying$When Angel of Mercy enters the battlefield, you gain 3 life.|
 Angel of the Dire Hour|Jumpstart|85|R|{5}{W}{W}|Creature - Angel|5|4|Flash$Flying$When Angel of the Dire Hour enters the battlefield, if you cast it from your hand, exile all attacking creatures.|
+Angelic Arbiter|Jumpstart|86|R|{5}{W}{W}|Creature - Angel|5|6|Flying$Each opponent who cast a spell this turn can't attack with creatures.$Each opponent who attacked with a creature this turn can't cast spells.|
+Angelic Edict|Jumpstart|87|C|{4}{W}|Sorcery|||Exile target creature or enchantment.|
+Angelic Page|Jumpstart|88|U|{1}{W}|Creature - Angel Spirit|1|1|Flying${T}: Target attacking or blocking creature gets +1/+1 until end of turn.|
 Archon of Justice|Jumpstart|89|R|{3}{W}{W}|Creature - Archon|4|4|Flying$When Archon of Justice dies, exile target permanent.|
 Archon of Redemption|Jumpstart|90|R|{3}{W}{W}|Creature - Archon|3|4|Flying$Whenever Archon of Redemption or another creature with flying enters the battlefield under your control, you may gain life equal to that creature's power.|
 Battlefield Promotion|Jumpstart|91|C|{1}{W}|Instant|||Put a +1/+1 counter on target creature. That creature gains first strike until end of turn. You gain 2 life.|
+Blessed Spirits|Jumpstart|92|U|{2}{W}|Creature - Spirit|2|2|Flying$Whenever you cast an enchantment spell, put a +1/+1 counter on Blessed Spirits.|
+Bulwark Giant|Jumpstart|93|C|{5}{W}|Creature - Giant Soldier|3|6|When Bulwark Giant enters the battlefield, you gain 5 life.|
 Cathar's Companion|Jumpstart|94|C|{2}{W}|Creature - Hound|3|1|Whenever you cast a noncreature spell, Cathar's Companion gains indestructible until end of turn.|
 Cathars' Crusade|Jumpstart|95|R|{3}{W}{W}|Enchantment|||Whenever a creature enters the battlefield under your control, put a +1/+1 counter on each creature you control.|
+Celestial Mantle|Jumpstart|96|R|{3}{W}{W}{W}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +3/+3.$Whenever enchanted creature deals combat damage to a player, double its controller's life total.|
 Cloudshift|Jumpstart|97|C|{W}|Instant|||Exile target creature you control, then return that card to the battlefield under your control.|
+Cradle of Vitality|Jumpstart|98|R|{3}{W}|Enchantment|||Whenever you gain life, you may pay {1}{W}. If you do, put a +1/+1 counter on target creature for each 1 life you gained.|
 Dauntless Onslaught|Jumpstart|99|U|{2}{W}|Instant|||Up to two target creatures each get +2/+2 until end of turn.|
 Divine Arrow|Jumpstart|100|C|{1}{W}|Instant|||Divine Arrow deals 4 damage to target attacking or blocking creature.|
 Duelist's Heritage|Jumpstart|101|R|{2}{W}|Enchantment|||Whenever one or more creatures attack, you may have target attacking creature gain double strike until end of turn.|
+Emancipation Angel|Jumpstart|102|U|{1}{W}{W}|Creature - Angel|3|3|Flying$When Emancipation Angel enters the battlefield, return a permanent you control to its owner's hand.|
+Face of Divinity|Jumpstart|103|U|{2}{W}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +2/+2.$As long as another Aura is attached to enchanted creature, it has first strike and lifelink.|
+Forced Worship|Jumpstart|104|C|{1}{W}|Enchantment - Aura|||Enchant creature$Enchanted creature can't attack.${2}{W}: Return Forced Worship to its owner's hand.|
+Fortify|Jumpstart|105|C|{2}{W}|Instant|||Choose one —$• Creatures you control get +2/+0 until end of turn.$• Creatures you control get +0/+2 until end of turn.|
 Gird for Battle|Jumpstart|106|U|{W}|Sorcery|||Put a +1/+1 counter on each of up to two target creatures.|
 Healer's Hawk|Jumpstart|107|C|{W}|Creature - Bird|1|1|Flying, lifelink|
 High Sentinels of Arashin|Jumpstart|108|R|{3}{W}|Creature - Bird Soldier|3|4|Flying$High Sentinels of Arashin gets +1/+1 for each other creature you control with a +1/+1 counter on it.${3}{W}: Put a +1/+1 counter on target creature.|
+Indomitable Will|Jumpstart|109|C|{1}{W}|Enchantment - Aura|||Flash$Enchant creature$Enchanted creature gets +1/+2.|
 Inspired Charge|Jumpstart|110|C|{2}{W}{W}|Instant|||Creatures you control get +2/+1 until end of turn.|
 Inspiring Captain|Jumpstart|111|C|{3}{W}|Creature - Human Knight|3|3|When Inspiring Captain enters the battlefield, creatures you control get +1/+1 until end of turn.|
 Inspiring Unicorn|Jumpstart|112|U|{2}{W}{W}|Creature - Unicorn|2|2|Whenever Inspiring Unicorn attacks, creatures you control get +1/+1 until end of turn.|
 Isamaru, Hound of Konda|Jumpstart|113|R|{W}|Legendary Creature - Hound|2|2||
 Knight of the Tusk|Jumpstart|114|C|{4}{W}{W}|Creature - Human Knight|3|7|Vigilance|
+Knightly Valor|Jumpstart|115|C|{4}{W}|Enchantment - Aura|||Enchant creature$When Knightly Valor enters the battlefield, create a 2/2 white Knight creature token with vigilance.$Enchanted creature gets +2/+2 and has vigilance.|
+Kor Spiritdancer|Jumpstart|116|R|{1}{W}|Creature - Kor Wizard|0|2|Kor Spiritdancer gets +2/+2 for each Aura attached to it.$Whenever you cast an Aura spell, you may draw a card.|
+Lena, Selfless Champion|Jumpstart|117|R|{4}{W}{W}|Legendary Creature - Human Knight|3|3|When Lena, Selfless Champion enters the battlefield, create a 1/1 white Soldier creature token for each nontoken creature you control.$Sacrifice Lena: Creatures you control with power less than Lena's power gain indestructible until end of turn.|
 Lightwalker|Jumpstart|118|C|{1}{W}|Creature - Human Warrior|2|1|Lightwalker has flying as long as it has a +1/+1 counter on it.|
+Linvala, Keeper of Silence|Jumpstart|119|M|{2}{W}{W}|Legendary Creature - Angel|3|4|Flying$Activated abilities of creatures your opponents control can't be activated.|
 Long Road Home|Jumpstart|120|U|{1}{W}|Instant|||Exile target creature. At the beginning of the next end step, return that card to the battlefield under its owner's control with a +1/+1 counter on it.|
 Mentor of the Meek|Jumpstart|121|R|{2}{W}|Creature - Human Soldier|2|2|Whenever another creature with power 2 or less enters the battlefield under your control, you may pay {1}. If you do, draw a card.|
 Mesa Unicorn|Jumpstart|122|C|{1}{W}|Creature - Unicorn|2|2|Lifelink|
 Mikaeus, the Lunarch|Jumpstart|123|M|{X}{W}|Legendary Creature - Human Cleric|0|0|Mikaeus, the Lunarch enters the battlefield with X +1/+1 counters on it.${T}: Put a +1/+1 counter on Mikaeus.${T}, Remove a +1/+1 counter from Mikaeus: Put a +1/+1 counter on each other creature you control.|
 Moment of Heroism|Jumpstart|124|C|{1}{W}|Instant|||Target creature gets +2/+2 and gains lifelink until end of turn.|
 Pacifism|Jumpstart|125|C|{1}{W}|Enchantment - Aura|||Enchant creature$Enchanted creature can't attack or block.|
+Path of Bravery|Jumpstart|126|R|{2}{W}|Enchantment|||As long as your life total is greater than or equal to your starting life total, creatures you control get +1/+1.$Whenever one or more creatures you control attack, you gain life equal to the number of attacking creatures.|
 Path to Exile|Jumpstart|127|U|{W}|Instant|||Exile target creature. Its controller may search their library for a basic land card, put that card onto the battlefield tapped, then shuffle their library.|
 Patron of the Valiant|Jumpstart|128|U|{3}{W}{W}|Creature - Angel|4|4|Flying$When Patron of the Valiant enters the battlefield, put a +1/+1 counter on each creature you control with a +1/+1 counter on it.|
+Raise the Alarm|Jumpstart|129|C|{1}{W}|Instant|||Create two 1/1 white Soldier creature tokens.|
+Rhox Faithmender|Jumpstart|130|R|{3}{W}|Creature - Rhino Monk|1|5|Lifelink$If you would gain life, you gain twice that much life instead.|
 Ronom Unicorn|Jumpstart|131|C|{1}{W}|Creature - Unicorn|2|2|Sacrifice Ronom Unicorn: Destroy target enchantment.|
+Serra Angel|Jumpstart|132|U|{3}{W}{W}|Creature - Angel|4|4|Flying, vigilance|
 Sky Tether|Jumpstart|133|U|{W}|Enchantment - Aura|||Enchant creature$Enchanted creature has defender and loses flying.|
 Take Heart|Jumpstart|134|C|{W}|Instant|||Target creature gets +2/+2 until end of turn. You gain 1 life for each attacking creature you control.|
 Tandem Tactics|Jumpstart|135|C|{1}{W}|Instant|||Up to two target creatures each get +1/+2 until end of turn. You gain 2 life.|
 Valorous Stance|Jumpstart|136|U|{1}{W}|Instant|||Choose one —$• Target creature gains indestructible until end of turn.$• Destroy target creature with toughness 4 or greater.|
+Voice of the Provinces|Jumpstart|137|C|{4}{W}{W}|Creature - Angel|3|3|Flying$When Voice of the Provinces enters the battlefield, create a 1/1 white Human creature token.|
 Aegis Turtle|Jumpstart|138|C|{U}|Creature - Turtle|0|5||
+Battleground Geist|Jumpstart|139|U|{4}{U}|Creature - Spirit|3|3|Flying$Other Spirit creatures you control get +1/+0.|
 Befuddle|Jumpstart|140|C|{2}{U}|Instant|||Target creature gets -4/-0 until end of turn.$Draw a card.|
+Belltower Sphinx|Jumpstart|141|U|{4}{U}|Creature - Sphinx|2|5|Flying$Whenever a source deals damage to Belltower Sphinx, that source's controller puts that many cards from the top of their library into their graveyard.|
 Chart a Course|Jumpstart|142|U|{1}{U}|Sorcery|||Draw two cards. Then discard a card unless you attacked this turn.|
 Cloudreader Sphinx|Jumpstart|143|C|{4}{U}|Creature - Sphinx|3|4|Flying$When Cloudreader Sphinx enters the battlefield, scry 2.|
 Coastal Piracy|Jumpstart|144|U|{2}{U}{U}|Enchantment|||Whenever a creature you control deals combat damage to an opponent, you may draw a card.|
@@ -37810,24 +37838,30 @@ Curiosity|Jumpstart|147|U|{U}|Enchantment - Aura|||Enchant creature$Whenever enc
 Curious Obsession|Jumpstart|148|U|{U}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +1/+1 and has "Whenever this creature deals combat damage to a player, you may draw a card."$At the beginning of your end step, if you didn't attack with a creature this turn, sacrifice Curious Obsession.|
 Departed Deckhand|Jumpstart|149|U|{1}{U}|Creature - Spirit Pirate|2|2|When Departed Deckhand becomes the target of a spell, sacrifice it.$Departed Deckhand can't be blocked except by Spirits.${3}{U}: Another target creature you control can't be blocked this turn except by Spirits.|
 Erratic Visionary|Jumpstart|150|C|{1}{U}|Creature - Human Wizard|1|3|{1}{U}, {T}: Draw a card, then discard a card.|
+Essence Flux|Jumpstart|151|U|{U}|Instant|||Exile target creature you control, then return that card to the battlefield under its owner's control. If it's a Spirit, put a +1/+1 counter on it.|
 Exclude|Jumpstart|152|U|{2}{U}|Instant|||Counter target creature spell.$Draw a card.|
 Exclusion Mage|Jumpstart|153|U|{2}{U}|Creature - Human Wizard|2|2|When Exclusion Mage enters the battlefield, return target creature an opponent controls to its owner's hand.|
 Kira, Great Glass-Spinner|Jumpstart|154|R|{1}{U}{U}|Legendary Creature - Spirit|2|2|Flying$Creatures you control have "Whenever this creature becomes the target of a spell or ability for the first time each turn, counter that spell or ability."|
 Kitesail Corsair|Jumpstart|155|C|{1}{U}|Creature - Human Pirate|2|1|Kitesail Corsair has flying as long as it's attacking.|
 Leave in the Dust|Jumpstart|156|C|{3}{U}|Instant|||Return target nonland permanent to its owner's hand.$Draw a card.|
+Murmuring Phantasm|Jumpstart|157|C|{1}{U}|Creature - Spirit|0|5|Defender|
 Mystic Archaeologist|Jumpstart|158|R|{1}{U}|Creature - Human Wizard|2|1|{3}{U}{U}: Draw two cards.|
 Narcolepsy|Jumpstart|159|C|{1}{U}|Enchantment - Aura|||Enchant creature$At the beginning of each upkeep, if enchanted creature is untapped, tap it.|
+Nebelgast Herald|Jumpstart|160|U|{2}{U}|Creature - Spirit|2|1|Flash$Flying$Whenever Nebelgast Herald or another Spirit enters the battlefield under your control, tap target creature an opponent controls.|
 Octoprophet|Jumpstart|161|C|{3}{U}|Creature - Octopus|3|3|When Octoprophet enters the battlefield, scry 2.|
 Oneirophage|Jumpstart|162|U|{3}{U}|Creature - Squid Illusion|1|2|Flying$Whenever you draw a card, put a +1/+1 counter on Oneirophage.|
 Peel from Reality|Jumpstart|163|C|{1}{U}|Instant|||Return target creature you control and target creature you don't control to their owners' hands.|
 Prescient Chimera|Jumpstart|164|C|{3}{U}{U}|Creature - Chimera|3|4|Flying$Whenever you cast an instant or sorcery spell, scry 1.|
 Prosperous Pirates|Jumpstart|165|C|{4}{U}|Creature - Human Pirate|3|4|When Prosperous Pirates enters the battlefield, create two Treasure tokens.|
+Rattlechains|Jumpstart|166|R|{1}{U}|Creature - Spirit|2|1|Flash$Flying$When Rattlechains enters the battlefield, target Spirit gains hexproof until end of turn.$You may cast Spirit spells as though they had flash.|
 Read the Runes|Jumpstart|167|R|{X}{U}|Instant|||Draw X cards. For each card drawn this way, discard a card unless you sacrifice a permanent.|
+Reckless Scholar|Jumpstart|168|U|{2}{U}|Creature - Human Wizard|2|1|{T}: Target player draws a card, then discards a card.|
 Rhystic Study|Jumpstart|169|R|{2}{U}|Enchantment|||Whenever an opponent casts a spell, you may draw a card unless that player pays {1}.|
 Rishadan Airship|Jumpstart|170|C|{2}{U}|Creature - Human Pirate|3|1|Flying$Rishadan Airship can block only creatures with flying.|
 Sage's Row Savant|Jumpstart|171|C|{1}{U}|Creature - Vedalken Wizard|2|1|When Sage's Row Savant enters the battlefield, scry 2.|
 Sailor of Means|Jumpstart|172|C|{2}{U}|Creature - Human Pirate|1|4|When Sailor of Means enters the battlefield, create a Treasure token.|
 Sea Gate Oracle|Jumpstart|173|C|{2}{U}|Creature - Human Wizard|1|3|When Sea Gate Oracle enters the battlefield, look at the top two cards of your library. Put one of them into your hand and the other on the bottom of your library.|
+Selhoff Occultist|Jumpstart|174|C|{2}{U}|Creature - Human Rogue|2|3|Whenever Selhoff Occultist or another creature dies, target player puts the top card of their library into their graveyard.|
 Serendib Efreet|Jumpstart|175|R|{2}{U}|Creature - Efreet|3|4|Flying$At the beginning of your upkeep, Serendib Efreet deals 1 damage to you.|
 Sharding Sphinx|Jumpstart|176|R|{4}{U}{U}|Artifact Creature - Sphinx|4|4|Flying$Whenever an artifact creature you control deals combat damage to a player, you may create a 1/1 blue Thopter artifact creature token with flying.|
 Sigiled Starfish|Jumpstart|177|U|{1}{U}|Creature - Starfish|0|3|{T}: Scry 1.|
@@ -37837,8 +37871,13 @@ Sweep Away|Jumpstart|180|C|{2}{U}|Instant|||Return target creature to its owner'
 Talrand, Sky Summoner|Jumpstart|181|R|{2}{U}{U}|Legendary Creature - Merfolk Wizard|2|2|Whenever you cast an instant or sorcery spell, create a 2/2 blue Drake creature token with flying.|
 Talrand's Invocation|Jumpstart|182|U|{2}{U}{U}|Sorcery|||Create two 2/2 blue Drake creature tokens with flying.|
 Thirst for Knowledge|Jumpstart|183|U|{2}{U}|Instant|||Draw three cards. Then discard two cards unless you discard an artifact card.|
+Thought Collapse|Jumpstart|184|C|{1}{U}{U}|Instant|||Counter target spell. Its controller puts the top three cards of their library into their graveyard.|
+Thought Scour|Jumpstart|185|C|{U}|Instant|||Target player puts the top two cards of their library into their graveyard.$Draw a card.|
+Towering-Wave Mystic|Jumpstart|186|C|{1}{U}|Creature - Merfolk Wizard|2|1|Whenever Towering-Wave Mystic deals damage, target player puts that many cards from the top of their library into their graveyard.|
 Vedalken Archmage|Jumpstart|187|R|{2}{U}{U}|Creature - Vedalken Wizard|0|2|Whenever you cast an artifact spell, draw a card.|
+Vedalken Entrancer|Jumpstart|188|C|{3}{U}|Creature - Vedalken Wizard|1|4|{U}, {T}: Target player puts the top two cards of their library into their graveyard.|
 Voyage's End|Jumpstart|189|C|{1}{U}|Instant|||Return target creature to its owner's hand. Scry 1.|
+Wall of Lost Thoughts|Jumpstart|190|U|{1}{U}|Creature - Wall|0|4|Defender$When Wall of Lost Thoughts enters the battlefield, target player puts the top four cards of their library into their graveyard.|
 Warden of Evos Isle|Jumpstart|191|U|{2}{U}|Creature - Bird Wizard|2|2|Flying$Creature spells with flying you cast cost {1} less to cast.|
 Waterknot|Jumpstart|192|C|{1}{U}{U}|Enchantment - Aura|||Enchant creature$When Waterknot enters the battlefield, tap enchanted creature.$Enchanted creature doesn't untap during its controller's untap step.|
 Whelming Wave|Jumpstart|193|R|{2}{U}{U}|Sorcery|||Return all creatures to their owners' hands except for Krakens, Leviathans, Octopuses, and Serpents.|
@@ -37850,7 +37889,9 @@ Wizard's Retort|Jumpstart|198|U|{1}{U}{U}|Instant|||This spell costs {1} less to
 Agonizing Syphon|Jumpstart|199|C|{3}{B}|Sorcery|||Agonizing Syphon deals 3 damage to any target and you gain 3 life.|
 Assassin's Strike|Jumpstart|200|U|{4}{B}{B}|Sorcery|||Destroy target creature. Its controller discards a card.|
 Bake into a Pie|Jumpstart|201|C|{2}{B}{B}|Instant|||Destroy target creature. Create a Food token.|
+Barter in Blood|Jumpstart|202|U|{2}{B}{B}|Sorcery|||Each player sacrifices two creatures.|
 Black Cat|Jumpstart|203|C|{1}{B}|Creature - Zombie Cat|1|1|When Black Cat dies, target opponent discards a card at random.|
+Black Market|Jumpstart|204|R|{3}{B}{B}|Enchantment|||Whenever a creature dies, put a charge counter on Black Market.$At the beginning of your precombat main phase, add {B} for each charge counter on Black Market.|
 Blighted Bat|Jumpstart|205|C|{2}{B}|Creature - Zombie Bat|2|1|Flying${1}: Blighted Bat gains haste until end of turn.|
 Blood Artist|Jumpstart|206|U|{1}{B}|Creature - Vampire|0|1|Whenever Blood Artist or another creature dies, target player loses 1 life and you gain 1 life.|
 Blood Divination|Jumpstart|207|U|{3}{B}|Sorcery|||As an additional cost to cast this spell, sacrifice a creature.$Draw three cards.|
@@ -37865,6 +37906,8 @@ Cadaver Imp|Jumpstart|215|C|{1}{B}{B}|Creature - Imp|1|1|Flying$When Cadaver Imp
 Cauldron Familiar|Jumpstart|216|C|{B}|Creature - Cat|1|1|When Cauldron Familiar enters the battlefield, each opponent loses 1 life and you gain 1 life.$Sacrifice a Food: Return Cauldron Familiar from your graveyard to the battlefield.|
 Cemetery Recruitment|Jumpstart|217|C|{1}{B}|Sorcery|||Return target creature card from your graveyard to your hand. If it's a Zombie card, draw a card.|
 Child of Night|Jumpstart|218|C|{1}{B}|Creature - Vampire|2|1|Lifelink|
+Corpse Hauler|Jumpstart|219|C|{1}{B}|Creature - Human Rogue|2|1|{2}{B}, Sacrifice Corpse Hauler: Return another target creature card from your graveyard to your hand.|
+Corpse Traders|Jumpstart|220|U|{3}{B}|Creature - Human Rogue|3|3|{2}{B}, Sacrifice a creature: Target opponent reveals their hand. You choose a card from it. That player discards that card. Activate this ability only any time you could cast a sorcery.|
 Crow of Dark Tidings|Jumpstart|221|C|{2}{B}|Creature - Zombie Bird|2|1|Flying$When Crow of Dark Tidings enters the battlefield or dies, put the top two cards of your library into your graveyard.|
 Death's Approach|Jumpstart|222|C|{B}|Enchantment - Aura|||Enchant creature$Enchanted creature gets -X/-X, where X is the number of creature cards in its controller's graveyard.|
 Douse in Gloom|Jumpstart|223|C|{2}{B}|Instant|||Douse in Gloom deals 2 damage to target creature and you gain 2 life.|
@@ -37884,28 +37927,40 @@ Ghoulcaller Gisa|Jumpstart|236|M|{3}{B}{B}|Legendary Creature - Human Wizard|3|4
 Ghoulcaller's Accomplice|Jumpstart|237|C|{1}{B}|Creature - Human Rogue|2|2|{3}{B}, Exile Ghoulcaller's Accomplice from your graveyard: Create a 2/2 black Zombie creature token. Activate this ability only any time you could cast a sorcery.|
 Ghoulraiser|Jumpstart|238|C|{1}{B}{B}|Creature - Zombie|2|2|When Ghoulraiser enters the battlefield, return a Zombie card at random from your graveyard to your hand.|
 Gifted Aetherborn|Jumpstart|239|U|{B}{B}|Creature - Aetherborn Vampire|2|3|Deathtouch, lifelink|
+Gonti, Lord of Luxury|Jumpstart|240|R|{2}{B}{B}|Legendary Creature - Aetherborn Rogue|2|3|Deathtouch$When Gonti, Lord of Luxury enters the battlefield, look at the top four cards of target opponent's library, exile one of them face down, then put the rest on the bottom of that library in a random order. You may look at and cast that card for as long as it remains exiled, and you may spend mana as though it were mana of any type to cast that spell.|
 Gravewaker|Jumpstart|241|R|{4}{B}{B}|Creature - Bird Spirit|5|5|Flying${5}{B}{B}: Return target creature card from your graveyard to the battlefield tapped.|
+Gristle Grinner|Jumpstart|242|U|{4}{B}|Creature - Zombie|3|3|Whenever a creature dies, Gristle Grinner gets +2/+2 until end of turn.|
+Harvester of Souls|Jumpstart|243|R|{4}{B}{B}|Creature - Demon|5|5|Deathtouch$Whenever another nontoken creature dies, you may draw a card.|
 Innocent Blood|Jumpstart|244|C|{B}|Sorcery|||Each player sacrifices a creature.|
 Kalastria Nightwatch|Jumpstart|245|C|{4}{B}|Creature - Vampire Warrior Ally|4|5|Whenever you gain life, Kalastria Nightwatch gains flying until end of turn.|
+Languish|Jumpstart|246|R|{2}{B}{B}|Sorcery|||All creatures get -4/-4 until end of turn.|
 Last Gasp|Jumpstart|247|C|{1}{B}|Instant|||Target creature gets -3/-3 until end of turn.|
+Launch Party|Jumpstart|248|C|{3}{B}|Instant|||As an additional cost to cast this spell, sacrifice a creature.$Destroy target creature. Its controller loses 2 life.|
+Lawless Broker|Jumpstart|249|C|{2}{B}|Creature - Aetherborn Rogue|3|2|When Lawless Broker dies, put a +1/+1 counter on target creature you control.|
 Liliana's Elite|Jumpstart|250|U|{2}{B}|Creature - Zombie|1|1|Liliana's Elite gets +1/+1 for each creature card in your graveyard.|
 Liliana's Reaver|Jumpstart|251|R|{2}{B}{B}|Creature - Zombie|4|3|Deathtouch$Whenever Liliana's Reaver deals combat damage to a player, that player discards a card and you create a tapped 2/2 black Zombie creature token.|
 Macabre Waltz|Jumpstart|252|C|{1}{B}|Sorcery|||Return up to two target creature cards from your graveyard to your hand, then discard a card.|
 Malakir Familiar|Jumpstart|253|U|{2}{B}|Creature - Bat|2|1|Flying, deathtouch$Whenever you gain life, Malakir Familiar gets +1/+1 until end of turn.|
 Mark of the Vampire|Jumpstart|254|C|{3}{B}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +2/+2 and has lifelink.|
+Mausoleum Turnkey|Jumpstart|255|U|{3}{B}|Creature - Ogre Rogue|3|2|When Mausoleum Turnkey enters the battlefield, return target creature card of an opponent's choice from your graveyard to your hand.|
 Miasmic Mummy|Jumpstart|256|C|{1}{B}|Creature - Zombie Jackal|2|2|When Miasmic Mummy enters the battlefield, each player discards a card.|
 Mire Triton|Jumpstart|257|U|{1}{B}|Creature - Zombie Merfolk|2|1|Deathtouch$When Mire Triton enters the battlefield, put the top two cards of your library into your graveyard and you gain 2 life.|
+Nightshade Stinger|Jumpstart|258|C|{B}|Creature - Faerie Rogue|1|1|Flying$Nightshade Stinger can't block.|
 Nyxathid|Jumpstart|259|R|{1}{B}{B}|Creature - Elemental|7|7|As Nyxathid enters the battlefield, choose an opponent.$Nyxathid gets -1/-1 for each card in the chosen player's hand.|
+Ogre Slumlord|Jumpstart|260|R|{3}{B}{B}|Creature - Ogre Rogue|3|3|Whenever another nontoken creature dies, you may create a 1/1 black Rat creature token.$Rats you control have deathtouch.|
+Oona's Blackguard|Jumpstart|261|U|{1}{B}|Creature - Faerie Rogue|1|1|Flying$Each other Rogue creature you control enters the battlefield with an additional +1/+1 counter on it.$Whenever a creature you control with a +1/+1 counter on it deals combat damage to a player, that player discards a card.|
 Parasitic Implant|Jumpstart|262|C|{3}{B}|Enchantment - Aura|||Enchant creature$At the beginning of your upkeep, enchanted creature's controller sacrifices it and you create a 1/1 colorless Myr artifact creature token.|
 Phyrexian Broodlings|Jumpstart|263|C|{1}{B}{B}|Creature - Minion|2|2|{1}, Sacrifice a creature: Put a +1/+1 counter on Phyrexian Broodlings.|
 Phyrexian Debaser|Jumpstart|264|C|{3}{B}|Creature - Carrier|2|2|Flying${T}, Sacrifice Phyrexian Debaser: Target creature gets -2/-2 until end of turn.|
 Phyrexian Gargantua|Jumpstart|265|U|{4}{B}{B}|Creature - Horror|4|4|When Phyrexian Gargantua enters the battlefield, you draw two cards and you lose 2 life.|
 Phyrexian Rager|Jumpstart|266|C|{2}{B}|Creature - Horror|2|2|When Phyrexian Rager enters the battlefield, you draw a card and you lose 1 life.|
 Phyrexian Reclamation|Jumpstart|267|U|{B}|Enchantment|||{1}{B}, Pay 2 life: Return target creature card from your graveyard to your hand.|
+Plagued Rusalka|Jumpstart|268|C|{B}|Creature - Spirit|1|1|{B}, Sacrifice a creature: Target creature gets -1/-1 until end of turn.|
 Ravenous Chupacabra|Jumpstart|269|U|{2}{B}{B}|Creature - Beast Horror|2|2|When Ravenous Chupacabra enters the battlefield, destroy target creature an opponent controls.|
 Reanimate|Jumpstart|270|R|{B}|Sorcery|||Put target creature card from a graveyard onto the battlefield under your control. You lose life equal to its converted mana cost.|
 Rise of the Dark Realms|Jumpstart|271|M|{7}{B}{B}|Sorcery|||Put all creature cards from all graveyards onto the battlefield under your control.|
 Sangromancer|Jumpstart|272|R|{2}{B}{B}|Creature - Vampire Shaman|3|3|Flying$Whenever a creature an opponent controls dies, you may gain 3 life.$Whenever an opponent discards a card, you may gain 3 life.|
+Sanitarium Skeleton|Jumpstart|273|C|{B}|Creature - Skeleton|1|2|{2}{B}: Return Sanitarium Skeleton from your graveyard to your hand.|
 Scourge of Nel Toth|Jumpstart|274|R|{5}{B}{B}|Creature - Zombie Dragon|6|6|Flying$You may cast Scourge of Nel Toth from your graveyard by paying {B}{B} and sacrificing two creatures rather than paying its mana cost.|
 Sengir Vampire|Jumpstart|275|U|{3}{B}{B}|Creature - Vampire|4|4|Flying$Whenever a creature dealt damage by Sengir Vampire this turn dies, put a +1/+1 counter on Sengir Vampire.|
 Settle the Score|Jumpstart|276|U|{2}{B}{B}|Sorcery|||Exile target creature. Put two loyalty counters on a planeswalker you control.|
@@ -37913,6 +37968,7 @@ Shambling Goblin|Jumpstart|277|C|{B}|Creature - Zombie Goblin|1|1|When Shambling
 Sheoldred, Whispering One|Jumpstart|278|M|{5}{B}{B}|Legendary Creature - Praetor|6|6|Swampwalk$At the beginning of your upkeep, return target creature card from your graveyard to the battlefield.$At the beginning of each opponent's upkeep, that player sacrifices a creature.|
 Slate Street Ruffian|Jumpstart|279|C|{2}{B}|Creature - Human Warrior|2|2|Whenever Slate Street Ruffian becomes blocked, defending player discards a card.|
 Soul Salvage|Jumpstart|280|C|{2}{B}|Sorcery|||Return up to two target creature cards from your graveyard to your hand.|
+Stab Wound|Jumpstart|281|U|{2}{B}|Enchantment - Aura|||Enchant creature$Enchanted creature gets -2/-2.$At the beginning of the upkeep of enchanted creature's controller, that player loses 2 life.|
 Swarm of Bloodflies|Jumpstart|282|U|{4}{B}|Creature - Insect|0|0|Flying$Swarm of Bloodflies enters the battlefield with two +1/+1 counters on it.$Whenever another creature dies, put a +1/+1 counter on Swarm of Bloodflies.|
 Tempting Witch|Jumpstart|283|U|{2}{B}|Creature - Human Warlock|1|3|When Tempting Witch enters the battlefield, create a Food token.${2}, {T}, Sacrifice a Food: Target player loses 3 life.|
 Tithebearer Giant|Jumpstart|284|C|{5}{B}|Creature - Giant Warrior|4|5|When Tithebearer Giant enters the battlefield, you draw a card and you lose 1 life.|
@@ -37920,16 +37976,58 @@ Vampire Neonate|Jumpstart|285|C|{B}|Creature - Vampire|0|3|{2}, {T}: Each oppone
 Wailing Ghoul|Jumpstart|286|C|{1}{B}|Creature - Zombie|1|3|When Wailing Ghoul enters the battlefield, put the top two cards of your library into your graveyard.|
 Wight of Precinct Six|Jumpstart|287|C|{1}{B}|Creature - Zombie|1|1|Wight of Precinct Six gets +1/+1 for each creature card in your opponents' graveyards.|
 Zombie Infestation|Jumpstart|288|U|{1}{B}|Enchantment|||Discard two cards: Create a 2/2 black Zombie creature token.|
+Act of Treason|Jumpstart|289|C|{2}{R}|Sorcery|||Gain control of target creature until end of turn. Untap that creature. It gains haste until end of turn.|
 Ashmouth Hound|Jumpstart|290|C|{1}{R}|Creature - Elemental Hound|2|1|Whenever Ashmouth Hound blocks or becomes blocked by a creature, Ashmouth Hound deals 1 damage to that creature.|
 Ball Lightning|Jumpstart|291|R|{R}{R}{R}|Creature - Elemental|6|1|Trample$Haste$At the beginning of the end step, sacrifice Ball Lightning.|
+Barrage of Expendables|Jumpstart|292|U|{R}|Enchantment|||{R}, Sacrifice a creature: Barrage of Expendables deals 1 damage to any target.|
+Bathe in Dragonfire|Jumpstart|293|C|{2}{R}|Sorcery|||Bathe in Dragonfire deals 4 damage to target creature.|
+Beetleback Chief|Jumpstart|294|U|{2}{R}{R}|Creature - Goblin Warrior|2|2|When Beetleback Chief enters the battlefield, create two 1/1 red Goblin creature tokens.|
+Blindblast|Jumpstart|295|C|{2}{R}|Instant|||Blindblast deals 1 damage to target creature. That creature can't block this turn.$Draw a card.|
+Bloodrage Brawler|Jumpstart|296|U|{1}{R}|Creature - Minotaur Warrior|4|3|When Bloodrage Brawler enters the battlefield, discard a card.|
 Bloodrock Cyclops|Jumpstart|297|C|{2}{R}|Creature - Cyclops|3|3|Bloodrock Cyclops attacks each combat if able.|
+Bloodshot Trainee|Jumpstart|298|U|{3}{R}|Creature - Goblin Warrior|2|3|{T}: Bloodshot Trainee deals 4 damage to target creature. Activate this ability only if Bloodshot Trainee's power is 4 or greater.|
+Boggart Brute|Jumpstart|299|C|{2}{R}|Creature - Goblin Warrior|3|2|Menace|
+Borderland Marauder|Jumpstart|300|C|{1}{R}|Creature - Human Warrior|1|2|Whenever Borderland Marauder attacks, it gets +2/+0 until end of turn.|
+Borderland Minotaur|Jumpstart|301|C|{2}{R}{R}|Creature - Minotaur Warrior|4|3||
 Chain Lightning|Jumpstart|302|U|{R}|Sorcery|||Chain Lightning deals 3 damage to any target. Then that player or that permanent's controller may pay {R}{R}. If the player does, they may copy this spell and may choose a new target for that copy.|
+Charmbreaker Devils|Jumpstart|303|R|{5}{R}|Creature - Devil|4|4|At the beginning of your upkeep, return an instant or sorcery card at random from your graveyard to your hand.$Whenever you cast an instant or sorcery spell, Charmbreaker Devils gets +4/+0 until end of turn.|
 Cinder Elemental|Jumpstart|304|U|{3}{R}|Creature - Elemental|2|2|{X}{R}, {T}, Sacrifice Cinder Elemental: It deals X damage to any target.|
+Collateral Damage|Jumpstart|305|C|{R}|Instant|||As an additional cost to cast this spell, sacrifice a creature.$Collateral Damage deals 3 damage to any target.|
+Dance with Devils|Jumpstart|306|U|{3}{R}|Instant|||Create two 1/1 red Devil creature tokens. They have "When this creature dies, it deals 1 damage to any target."|
+Doublecast|Jumpstart|307|U|{R}{R}|Sorcery|||When you cast your next instant or sorcery spell this turn, copy that spell. You may choose new targets for the copy.|
+Draconic Roar|Jumpstart|308|C|{1}{R}|Instant|||As an additional cost to cast this spell, you may reveal a Dragon card from your hand.$Draconic Roar deals 3 damage to target creature. If you revealed a Dragon card or controlled a Dragon as you cast this spell, Draconic Roar deals 3 damage to that creature's controller.|
+Dragon Fodder|Jumpstart|309|C|{1}{R}|Sorcery|||Create two 1/1 red Goblin creature tokens.|
+Dragon Hatchling|Jumpstart|310|C|{1}{R}|Creature - Dragon|0|1|Flying${R}: Dragon Hatchling gets +1/+0 until end of turn.|
+Dragonlord's Servant|Jumpstart|311|U|{1}{R}|Creature - Goblin Shaman|1|3|Dragon spells you cast cost {1} less to cast.|
+Dragonspeaker Shaman|Jumpstart|312|U|{1}{R}{R}|Creature - Human Barbarian Shaman|2|2|Dragon spells you cast cost {2} less to cast.|
+Dualcaster Mage|Jumpstart|313|R|{1}{R}{R}|Creature - Human Wizard|2|2|Flash$When Dualcaster Mage enters the battlefield, copy target instant or sorcery spell. You may choose new targets for the copy.|
+Etali, Primal Storm|Jumpstart|314|R|{4}{R}{R}|Legendary Creature - Elder Dinosaur|6|6|Whenever Etali, Primal Storm attacks, exile the top card of each player's library, then you may cast any number of spells from among those cards without paying their mana costs.|
 Fanatical Firebrand|Jumpstart|315|C|{R}|Creature - Goblin Pirate|1|1|Haste${T}, Sacrifice Fanatical Firebrand: It deals 1 damage to any target.|
+Flame Lash|Jumpstart|316|C|{3}{R}|Instant|||Flame Lash deals 4 damage to any target.|
 Flames of the Firebrand|Jumpstart|317|U|{2}{R}|Sorcery|||Flames of the Firebrand deals 3 damage divided as you choose among one, two, or three targets.|
+Flames of the Raze-Boar|Jumpstart|318|U|{5}{R}|Instant|||Flames of the Raze-Boar deals 4 damage to target creature an opponent controls. Then Flames of the Raze-Boar deals 2 damage to each other creature that player controls if you control a creature with power 4 or greater.|
+Flametongue Kavu|Jumpstart|319|U|{3}{R}|Creature - Kavu|4|2|When Flametongue Kavu enters the battlefield, it deals 4 damage to target creature.|
+Fling|Jumpstart|320|C|{1}{R}|Instant|||As an additional cost to cast this spell, sacrifice a creature.$Fling deals damage equal to the sacrificed creature's power to any target.|
+Flurry of Horns|Jumpstart|321|C|{4}{R}|Sorcery|||Create two 2/3 red Minotaur creature tokens with haste.|
+Forge Devil|Jumpstart|322|C|{R}|Creature - Devil|1|1|When Forge Devil enters the battlefield, it deals 1 damage to target creature and 1 damage to you.|
+Furnace Whelp|Jumpstart|323|C|{2}{R}{R}|Creature - Dragon|2|2|Flying${R}: Furnace Whelp gets +1/+0 until end of turn.|
+Goblin Chieftain|Jumpstart|324|R|{1}{R}{R}|Creature - Goblin|2|2|Haste$Other Goblin creatures you control get +1/+1 and have haste.|
+Goblin Commando|Jumpstart|325|U|{4}{R}|Creature - Goblin|2|2|When Goblin Commando enters the battlefield, it deals 2 damage to target creature.|
+Goblin Goon|Jumpstart|326|R|{3}{R}|Creature - Goblin Mutant|6|6|Goblin Goon can't attack unless you control more creatures than defending player.$Goblin Goon can't block unless you control more creatures than attacking player.|
+Goblin Instigator|Jumpstart|327|C|{1}{R}|Creature - Goblin Rogue|1|1|When Goblin Instigator enters the battlefield, create a 1/1 red Goblin creature token.|
+Goblin Lore|Jumpstart|328|U|{1}{R}|Sorcery|||Draw four cards, then discard three cards at random.|
+Goblin Rally|Jumpstart|329|U|{3}{R}{R}|Sorcery|||Create four 1/1 red Goblin creature tokens.|
+Goblin Shortcutter|Jumpstart|330|C|{1}{R}|Creature - Goblin Scout|2|1|When Goblin Shortcutter enters the battlefield, target creature can't block this turn.|
 Grim Lavamancer|Jumpstart|331|R|{R}|Creature - Human Wizard|1|1|{R}, {T}, Exile two cards from your graveyard: Grim Lavamancer deals 2 damage to any target.|
+Hamletback Goliath|Jumpstart|332|R|{6}{R}|Creature - Giant Warrior|6|6|Whenever another creature enters the battlefield, you may put X +1/+1 counters on Hamletback Goliath, where X is that creature's power.|
+Heartfire|Jumpstart|333|C|{1}{R}|Instant|||As an additional cost to cast this spell, sacrifice a creature or planeswalker.$Heartfire deals 4 damage to any target.|
+Hellrider|Jumpstart|334|R|{2}{R}{R}|Creature - Devil|3|3|Haste$Whenever a creature you control attacks, Hellrider deals 1 damage to the player or planeswalker it's attacking.|
 Homing Lightning|Jumpstart|335|U|{2}{R}{R}|Instant|||Homing Lightning deals 4 damage to target creature and each other creature with the same name as that creature.|
 Hungry Flames|Jumpstart|336|C|{2}{R}|Instant|||Hungry Flames deals 3 damage to target creature and 2 damage to target player or planeswalker.|
+Inferno Hellion|Jumpstart|337|U|{3}{R}|Creature - Hellion|7|3|Trample$At the beginning of each end step, if Inferno Hellion attacked or blocked this turn, its owner shuffles it into their library.|
+Kiln Fiend|Jumpstart|338|C|{1}{R}|Creature - Elemental Beast|1|2|Whenever you cast an instant or sorcery spell, Kiln Fiend gets +3/+0 until end of turn.|
+Krenko, Mob Boss|Jumpstart|339|R|{2}{R}{R}|Legendary Creature - Goblin Warrior|3|3|{T}: Create X 1/1 red Goblin creature tokens, where X is the number of Goblins you control.|
+Lathliss, Dragon Queen|Jumpstart|340|R|{4}{R}{R}|Legendary Creature - Dragon|6|6|Flying$Whenever another nontoken Dragon enters the battlefield under your control, create a 5/5 red Dragon creature token with flying.${1}{R}: Dragons you control get +1/+0 until end of turn.|
 Lightning Axe|Jumpstart|341|U|{R}|Instant|||As an additional cost to cast this spell, discard a card or pay {5}.$Lightning Axe deals 5 damage to target creature.|
 Lightning Bolt|Jumpstart|342|U|{R}|Instant|||Lightning Bolt deals 3 damage to any target.|
 Lightning Diadem|Jumpstart|343|C|{5}{R}|Enchantment - Aura|||Enchant creature$When Lightning Diadem enters the battlefield, it deals 2 damage to any target.$Enchanted creature gets +2/+2.|
@@ -37937,24 +38035,42 @@ Lightning Elemental|Jumpstart|344|C|{3}{R}|Creature - Elemental|4|1|Haste|
 Lightning Shrieker|Jumpstart|345|C|{4}{R}|Creature - Dragon|5|5|Flying, trample, haste$At the beginning of the end step, Lightning Shrieker's owner shuffles it into their library.|
 Magma Jet|Jumpstart|346|U|{1}{R}|Instant|||Magma Jet deals 2 damage to any target. Scry 2.|
 Magmaquake|Jumpstart|347|R|{X}{R}{R}|Instant|||Magmaquake deals X damage to each creature without flying and each planeswalker.|
+Makeshift Munitions|Jumpstart|348|U|{1}{R}|Enchantment|||{1}, Sacrifice an artifact or creature: Makeshift Munitions deals 1 damage to any target.|
+Minotaur Skullcleaver|Jumpstart|349|C|{2}{R}|Creature - Minotaur Berserker|2|2|Haste$When Minotaur Skullcleaver enters the battlefield, it gets +2/+0 until end of turn.|
+Minotaur Sureshot|Jumpstart|350|C|{2}{R}|Creature - Minotaur Archer|2|3|Reach${1}{R}: Minotaur Sureshot gets +1/+0 until end of turn.|
 Molten Ravager|Jumpstart|351|C|{2}{R}|Creature - Elemental|0|4|{R}: Molten Ravager gets +1/+0 until end of turn.|
+Mugging|Jumpstart|352|C|{R}|Sorcery|||Mugging deals 2 damage to target creature. That creature can't block this turn.|
+Ornery Goblin|Jumpstart|353|C|{1}{R}|Creature - Goblin Warrior|2|1|Whenever Ornery Goblin blocks or becomes blocked by a creature, Ornery Goblin deals 1 damage to that creature.|
+Outnumber|Jumpstart|354|C|{R}|Instant|||Outnumber deals damage to target creature equal to the number of creatures you control.|
 Pillar of Flame|Jumpstart|355|C|{R}|Sorcery|||Pillar of Flame deals 2 damage to any target. If a creature dealt damage this way would die this turn, exile it instead.|
 Pyroclastic Elemental|Jumpstart|356|U|{3}{R}{R}|Creature - Elemental|5|4|{1}{R}{R}: Pyroclastic Elemental deals 1 damage to target player.|
 Rageblood Shaman|Jumpstart|357|R|{1}{R}{R}|Creature - Minotaur Shaman|2|3|Trample$Other Minotaur creatures you control get +1/+1 and have trample.|
+Rapacious Dragon|Jumpstart|358|U|{4}{R}|Creature - Dragon|3|3|Flying$When Rapacious Dragon enters the battlefield, create two Treasure tokens.|
 Riddle of Lightning|Jumpstart|359|U|{3}{R}{R}|Instant|||Choose any target. Scry 3, then reveal the top card of your library. Riddle of Lightning deals damage equal to that card's converted mana cost to that permanent or player.|
+Sarkhan's Rage|Jumpstart|360|C|{4}{R}|Instant|||Sarkhan's Rage deals 5 damage to any target. If you control no Dragons, Sarkhan's Rage deals 2 damage to you.|
+Sarkhan's Unsealing|Jumpstart|361|R|{3}{R}|Enchantment|||Whenever you cast a creature spell with power 4, 5, or 6, Sarkhan's Unsealing deals 4 damage to any target.$Whenever you cast a creature spell with power 7 or greater, Sarkhan's Unsealing deals 4 damage to each opponent and each creature and planeswalker they control.|
 Seismic Elemental|Jumpstart|362|U|{3}{R}{R}|Creature - Elemental|4|4|When Seismic Elemental enters the battlefield, creatures without flying can't block this turn.|
+Sin Prodder|Jumpstart|363|R|{2}{R}|Creature - Devil|3|2|Menace$At the beginning of your upkeep, reveal the top card of your library. Any opponent may have you put that card into your graveyard. If a player does, Sin Prodder deals damage to that player equal to that card's converted mana cost. Otherwise, put that card into your hand.|
 Spitting Earth|Jumpstart|364|C|{1}{R}|Sorcery|||Spitting Earth deals damage to target creature equal to the number of Mountains you control.|
+Thermo-Alchemist|Jumpstart|365|C|{1}{R}|Creature - Human Shaman|0|3|Defender${T}: Thermo-Alchemist deals 1 damage to each opponent.$Whenever you cast an instant or sorcery spell, untap Thermo-Alchemist.|
+Tibalt's Rager|Jumpstart|366|U|{1}{R}|Creature - Devil|1|2|When Tibalt's Rager dies, it deals 1 damage to any target.${1}{R}: Tibalt's Rager gets +2/+0 until end of turn.|
+Torch Fiend|Jumpstart|367|C|{1}{R}|Creature - Devil|2|1|{R}, Sacrifice Torch Fiend: Destroy target artifact.|
 Volcanic Fallout|Jumpstart|368|U|{1}{R}{R}|Instant|||This spell can't be countered.$Volcanic Fallout deals 2 damage to each creature and each player.|
+Volley Veteran|Jumpstart|369|U|{3}{R}|Creature - Goblin Warrior|4|2|When Volley Veteran enters the battlefield, it deals damage to target creature an opponent controls equal to the number of Goblins you control.|
+Warfire Javelineer|Jumpstart|370|U|{3}{R}|Creature - Minotaur Warrior|2|3|When Warfire Javelineer enters the battlefield, it deals X damage to target creature an opponent controls, where X is the number of instant and sorcery cards in your graveyard.|
 Weaver of Lightning|Jumpstart|371|U|{2}{R}|Creature - Human Shaman|1|4|Reach$Whenever you cast an instant or sorcery spell, Weaver of Lightning deals 1 damage to target creature an opponent controls.|
 Young Pyromancer|Jumpstart|372|U|{1}{R}|Creature - Human Shaman|2|1|Whenever you cast an instant or sorcery spell, create a 1/1 red Elemental creature token.|
 Affectionate Indrik|Jumpstart|373|U|{5}{G}|Creature - Beast|4|4|When Affectionate Indrik enters the battlefield, you may have it fight target creature you don't control.|
 Aggressive Urge|Jumpstart|374|C|{1}{G}|Instant|||Target creature gets +1/+1 until end of turn.$Draw a card.|
 Ambassador Oak|Jumpstart|375|C|{3}{G}|Creature - Treefolk Warrior|3|3|When Ambassador Oak enters the battlefield, create a 1/1 green Elf Warrior creature token.|
+Arbor Armament|Jumpstart|376|C|{G}|Instant|||Put a +1/+1 counter on target creature. That creature gains reach until end of turn.|
+Armorcraft Judge|Jumpstart|377|U|{3}{G}|Creature - Elf Artificer|3|3|When Armorcraft Judge enters the battlefield, draw a card for each creature you control with a +1/+1 counter on it.|
 Assault Formation|Jumpstart|378|R|{1}{G}|Enchantment|||Each creature you control assigns combat damage equal to its toughness rather than its power.${G}: Target creature with defender can attack this turn as though it didn't have defender.${2}{G}: Creatures you control get +0/+1 until end of turn.|
 Awakener Druid|Jumpstart|379|U|{2}{G}|Creature - Human Druid|1|1|When Awakener Druid enters the battlefield, target Forest becomes a 4/5 green Treefolk creature for as long as Awakener Druid remains on the battlefield. It's still a land.|
 Brindle Shoat|Jumpstart|380|U|{1}{G}|Creature - Boar|1|1|When Brindle Shoat dies, create a 3/3 green Boar creature token.|
 Brushstrider|Jumpstart|381|U|{1}{G}|Creature - Beast|3|1|Vigilance|
 Carven Caryatid|Jumpstart|382|U|{1}{G}{G}|Creature - Spirit|2|5|Defender$When Carven Caryatid enters the battlefield, draw a card.|
+Champion of Lambholt|Jumpstart|383|R|{1}{G}{G}|Creature - Human Warrior|1|1|Creatures with power less than Champion of Lambholt's power can't block creatures you control.$Whenever another creature enters the battlefield under your control, put a +1/+1 counter on Champion of Lambholt.|
 Commune with Dinosaurs|Jumpstart|384|C|{G}|Sorcery|||Look at the top five cards of your library. You may reveal a Dinosaur or land card from among them and put it into your hand. Put the rest on the bottom of your library in any order.|
 Craterhoof Behemoth|Jumpstart|385|M|{5}{G}{G}{G}|Creature - Beast|5|5|Haste$When Craterhoof Behemoth enters the battlefield, creatures you control gain trample and get +X/+X until end of turn, where X is the number of creatures you control.|
 Crushing Canopy|Jumpstart|386|C|{2}{G}|Instant|||Choose one —$• Destroy target creature with flying.$• Destroy target enchantment.|
@@ -37975,12 +38091,16 @@ Ghirapur Guide|Jumpstart|400|U|{2}{G}|Creature - Elf Scout|3|2|{2}{G}: Target cr
 Grave Bramble|Jumpstart|401|C|{1}{G}{G}|Creature - Plant|3|4|Defender, protection from Zombies|
 Hunter's Insight|Jumpstart|402|U|{2}{G}|Instant|||Choose target creature you control. Whenever that creature deals combat damage to a player or planeswalker this turn, draw that many cards.|
 Initiate's Companion|Jumpstart|403|C|{1}{G}|Creature - Cat|3|1|Whenever Initiate's Companion deals combat damage to a player, untap target creature or land.|
+Inspiring Call|Jumpstart|404|U|{2}{G}|Instant|||Draw a card for each creature you control with a +1/+1 counter on it. Those creatures gain indestructible until end of turn.|
+Ironshell Beetle|Jumpstart|405|C|{1}{G}|Creature - Insect|1|1|When Ironshell Beetle enters the battlefield, put a +1/+1 counter on target creature.|
 Irresistible Prey|Jumpstart|406|U|{G}|Sorcery|||Target creature must be blocked this turn if able.$Draw a card.|
 Keeper of Fables|Jumpstart|407|U|{3}{G}{G}|Creature - Cat|4|5|Whenever one or more non-Human creatures you control deal combat damage to a player, draw a card.|
 Leaf Gilder|Jumpstart|408|C|{1}{G}|Creature - Elf Druid|2|1|{T}: Add {G}.|
+Lifecrafter's Gift|Jumpstart|409|U|{3}{G}|Instant|||Put a +1/+1 counter on target creature, then put a +1/+1 counter on each creature you control with a +1/+1 counter on it.|
 Lurking Predators|Jumpstart|410|R|{4}{G}{G}|Enchantment|||Whenever an opponent casts a spell, reveal the top card of your library. If it's a creature card, put it onto the battlefield. Otherwise, you may put that card on the bottom of your library.|
 Momentous Fall|Jumpstart|411|R|{2}{G}{G}|Instant|||As an additional cost to cast this spell, sacrifice a creature.$You draw cards equal to the sacrificed creature's power, then you gain life equal to its toughness.|
 Nature's Way|Jumpstart|412|U|{1}{G}|Sorcery|||Target creature you control gains vigilance and trample until end of turn. It deals damage equal to its power to target creature you don't control.|
+Nessian Hornbeetle|Jumpstart|413|U|{1}{G}|Creature - Insect|2|2|At the beginning of combat on your turn, if you control another creature with power 4 or greater, put a +1/+1 counter on Nessian Hornbeetle.|
 New Horizons|Jumpstart|414|C|{2}{G}|Enchantment - Aura|||Enchant land$When New Horizons enters the battlefield, put a +1/+1 counter on target creature you control.$Enchanted land has "{T}: Add two mana of any one color."|
 Oracle of Mul Daya|Jumpstart|415|R|{3}{G}|Creature - Elf Shaman|2|2|You may play an additional land on each of your turns.$Play with the top card of your library revealed.$You may play lands from the top of your library.|
 Orazca Frillback|Jumpstart|416|C|{2}{G}|Creature - Dinosaur|4|2||
@@ -37988,11 +38108,14 @@ Overgrown Battlement|Jumpstart|417|U|{1}{G}|Creature - Wall|0|4|Defender${T}: Ad
 Penumbra Bobcat|Jumpstart|418|C|{2}{G}|Creature - Cat|2|1|When Penumbra Bobcat dies, create a 2/1 black Cat creature token.|
 Pouncing Cheetah|Jumpstart|419|C|{2}{G}|Creature - Cat|3|2|Flash|
 Presence of Gond|Jumpstart|420|C|{2}{G}|Enchantment - Aura|||Enchant creature$Enchanted creature has "{T}: Create a 1/1 green Elf Warrior creature token."|
+Primeval Bounty|Jumpstart|421|M|{5}{G}|Enchantment|||Whenever you cast a creature spell, create a 3/3 green Beast creature token.$Whenever you cast a noncreature spell, put three +1/+1 counters on target creature you control.$Whenever a land enters the battlefield under your control, you gain 3 life.|
 Primordial Sage|Jumpstart|422|R|{4}{G}{G}|Creature - Spirit|4|5|Whenever you cast a creature spell, you may draw a card.|
 Rampaging Brontodon|Jumpstart|423|R|{5}{G}{G}|Creature - Dinosaur|7|7|Trample$Whenever Rampaging Brontodon attacks, it gets +1/+1 until end of turn for each land you control.|
 Ravenous Baloth|Jumpstart|424|R|{2}{G}{G}|Creature - Beast|4|4|Sacrifice a Beast: You gain 4 life.|
+Rishkar, Peema Renegade|Jumpstart|425|R|{2}{G}|Legendary Creature - Elf Druid|2|2|When Rishkar, Peema Renegade enters the battlefield, put a +1/+1 counter on each of up to two target creatures.$Each creature you control with a counter on it has "{T}: Add {G}."|
 Rumbling Baloth|Jumpstart|426|C|{2}{G}{G}|Creature - Beast|4|4||
 Savage Stomp|Jumpstart|427|U|{2}{G}|Sorcery|||This spell costs {2} less to cast if it targets a Dinosaur you control.$Put a +1/+1 counter on target creature you control. Then that creature fights target creature you don't control.|
+Scrounging Bandar|Jumpstart|428|C|{1}{G}|Creature - Cat Monkey|0|0|Scrounging Bandar enters the battlefield with two +1/+1 counters on it.$At the beginning of your upkeep, you may move any number of +1/+1 counters from Scrounging Bandar onto another target creature.|
 Selvala, Heart of the Wilds|Jumpstart|429|M|{1}{G}{G}|Legendary Creature - Elf Scout|2|3|Whenever another creature enters the battlefield, its controller may draw a card if its power is greater than each other creature's power.${G}, {T}: Add X mana in any combination of colors, where X is the greatest power among creatures you control.|
 Silhana Wayfinder|Jumpstart|430|U|{1}{G}|Creature - Elf Scout|2|1|When Silhana Wayfinder enters the battlefield, look at the top four cards of your library. You may reveal a creature or land card from among them and put it on top of your library. Put the rest on the bottom of your library in a random order.|
 Somberwald Stag|Jumpstart|431|U|{3}{G}{G}|Creature - Elk|4|3|When Somberwald Stag enters the battlefield, you may have it fight target creature you don't control.|
@@ -38027,9 +38150,11 @@ Arcane Encyclopedia|Jumpstart|459|U|{3}|Artifact|||{3}, {T}: Draw a card.|
 Bubbling Cauldron|Jumpstart|460|U|{2}|Artifact|||{1}, {T}, Sacrifice a creature: You gain 4 life.${1}, {T}, Sacrifice a creature named Festering Newt: Each opponent loses 4 life. You gain life equal to the life lost this way.|
 Chamber Sentry|Jumpstart|461|R|{X}|Artifact Creature - Construct|0|0|Chamber Sentry enters the battlefield with a +1/+1 counter on it for each color of mana spent to cast it.${X}, {T}, Remove X +1/+1 counters from Chamber Sentry: It deals X damage to any target.${W}{U}{B}{R}{G}: Return Chamber Sentry from your graveyard to your hand.|
 Chromatic Sphere|Jumpstart|462|C|{1}|Artifact|||{1}, {T}, Sacrifice Chromatic Sphere: Add one mana of any color. Draw a card.|
+Dragonloft Idol|Jumpstart|463|U|{4}|Artifact Creature - Gargoyle|3|3|As long as you control a Dragon, Dragonloft Idol gets +1/+1 and has flying and trample.|
 Dreamstone Hedron|Jumpstart|464|U|{6}|Artifact|||{T}: Add {C}{C}{C}.${3}, {T}, Sacrifice Dreamstone Hedron: Draw three cards.|
 Gargoyle Sentinel|Jumpstart|465|U|{3}|Artifact Creature - Gargoyle|3|3|Defender${3}: Until end of turn, Gargoyle Sentinel loses defender and gains flying.|
 Gingerbrute|Jumpstart|466|C|{1}|Artifact Creature - Food Golem|1|1|Haste${1}: Gingerbrute can't be blocked this turn except by creatures with haste.${2}, {T}, Sacrifice Gingerbrute: You gain 3 life.|
+Guardian Idol|Jumpstart|467|U|{2}|Artifact|||Guardian Idol enters the battlefield tapped.${T}: Add {C}.${2}: Guardian Idol becomes a 2/2 Golem artifact creature until end of turn.|
 Hedron Archive|Jumpstart|468|U|{4}|Artifact|||{T}: Add {C}{C}.${2}, {T}, Sacrifice Hedron Archive: Draw two cards.|
 Herald's Horn|Jumpstart|469|U|{3}|Artifact|||As Herald's Horn enters the battlefield, choose a creature type.$Creature spells you cast of the chosen type cost {1} less to cast.$At the beginning of your upkeep, look at the top card of your library. If it's a creature card of the chosen type, you may reveal it and put it into your hand.|
 Jousting Dummy|Jumpstart|470|C|{2}|Artifact Creature - Scarecrow Knight|2|1|{3}: Jousting Dummy gets +1/+0 until end of turn.|
@@ -38041,9 +38166,11 @@ Myr Sire|Jumpstart|475|C|{2}|Artifact Creature - Myr|1|1|When Myr Sire dies, cre
 Perilous Myr|Jumpstart|476|U|{2}|Artifact Creature - Myr|1|1|When Perilous Myr dies, it deals 2 damage to any target.|
 Pirate's Cutlass|Jumpstart|477|C|{3}|Artifact - Equipment|||When Pirate's Cutlass enters the battlefield, attach it to target Pirate you control.$Equipped creature gets +2/+1.$Equip {2}|
 Prophetic Prism|Jumpstart|478|C|{2}|Artifact|||When Prophetic Prism enters the battlefield, draw a card.${1}, {T}: Add one mana of any color.|
+Rogue's Gloves|Jumpstart|479|U|{2}|Artifact - Equipment|||Whenever equipped creature deals combat damage to a player, you may draw a card.$Equip {2}|
 Roving Keep|Jumpstart|480|C|{7}|Artifact Creature - Wall|5|7|Defender${7}: Roving Keep gets +2/+0 and gains trample until end of turn. It can attack this turn as though it didn't have defender.|
 Runed Servitor|Jumpstart|481|C|{2}|Artifact Creature - Construct|2|2|When Runed Servitor dies, each player draws a card.|
 Scarecrone|Jumpstart|482|R|{3}|Artifact Creature - Scarecrow|1|2|{1}, Sacrifice a Scarecrow: Draw a card.${4}, {T}: Return target artifact creature card from your graveyard to the battlefield.|
+Scroll of Avacyn|Jumpstart|483|C|{1}|Artifact|||{1}, Sacrifice Scroll of Avacyn: Draw a card. If you control an Angel, you gain 5 life.|
 Scuttlemutt|Jumpstart|484|U|{3}|Artifact Creature - Scarecrow|2|2|{T}: Add one mana of any color.${T}: Target creature becomes the color or colors of your choice until end of turn.|
 Signpost Scarecrow|Jumpstart|485|C|{4}|Artifact Creature - Scarecrow|2|4|Vigilance${2}: Add one mana of any color.|
 Skittering Surveyor|Jumpstart|486|C|{3}|Artifact Creature - Construct|1|2|When Skittering Surveyor enters the battlefield, you may search your library for a basic land card, reveal it, put it into your hand, then shuffle your library.|

From cc4e4b95a2e9bbe5b1c15a21871889155213d3dc Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sun, 21 Jun 2020 09:54:54 -0400
Subject: [PATCH 440/586] Implemented Corsair Captain

---
 .../src/mage/cards/c/CorsairCaptain.java      | 50 +++++++++++++++++++
 Mage.Sets/src/mage/sets/Jumpstart.java        |  1 +
 2 files changed, 51 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/c/CorsairCaptain.java

diff --git a/Mage.Sets/src/mage/cards/c/CorsairCaptain.java b/Mage.Sets/src/mage/cards/c/CorsairCaptain.java
new file mode 100644
index 0000000000..f23412279b
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/c/CorsairCaptain.java
@@ -0,0 +1,50 @@
+package mage.cards.c;
+
+import mage.MageInt;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.effects.common.CreateTokenEffect;
+import mage.abilities.effects.common.continuous.BoostControlledEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+import mage.filter.common.FilterCreaturePermanent;
+import mage.game.permanent.token.TreasureToken;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class CorsairCaptain extends CardImpl {
+
+    private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(SubType.PIRATE, "Pirates");
+
+    public CorsairCaptain(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}");
+
+        this.subtype.add(SubType.HUMAN);
+        this.subtype.add(SubType.PIRATE);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(2);
+
+        // When Corsair Captain enters the battlefield, create a treasure token.
+        this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new TreasureToken())));
+
+        // Other Pirates you control get +1/+1.
+        this.addAbility(new SimpleStaticAbility(
+                new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield, filter, true)
+        ));
+    }
+
+    private CorsairCaptain(final CorsairCaptain card) {
+        super(card);
+    }
+
+    @Override
+    public CorsairCaptain copy() {
+        return new CorsairCaptain(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index 69eeb2f7a3..db656a586d 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -108,6 +108,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Commune with Dinosaurs", 384, Rarity.COMMON, mage.cards.c.CommuneWithDinosaurs.class));
         cards.add(new SetCardInfo("Corpse Hauler", 219, Rarity.COMMON, mage.cards.c.CorpseHauler.class));
         cards.add(new SetCardInfo("Corpse Traders", 220, Rarity.UNCOMMON, mage.cards.c.CorpseTraders.class));
+        cards.add(new SetCardInfo("Corsair Captain", 11, Rarity.RARE, mage.cards.c.CorsairCaptain.class));
         cards.add(new SetCardInfo("Cradle of Vitality", 98, Rarity.RARE, mage.cards.c.CradleOfVitality.class));
         cards.add(new SetCardInfo("Craterhoof Behemoth", 385, Rarity.MYTHIC, mage.cards.c.CraterhoofBehemoth.class));
         cards.add(new SetCardInfo("Crookclaw Transmuter", 145, Rarity.COMMON, mage.cards.c.CrookclawTransmuter.class));

From 170745fbd08d0ab8e0ed84790a481ae0f8e477fb Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sun, 21 Jun 2020 10:00:49 -0400
Subject: [PATCH 441/586] Implemented Stone Haven Pilgrim

---
 .../src/mage/cards/s/StoneHavenPilgrim.java   | 56 +++++++++++++++++++
 Mage.Sets/src/mage/sets/Jumpstart.java        |  1 +
 2 files changed, 57 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/s/StoneHavenPilgrim.java

diff --git a/Mage.Sets/src/mage/cards/s/StoneHavenPilgrim.java b/Mage.Sets/src/mage/cards/s/StoneHavenPilgrim.java
new file mode 100644
index 0000000000..f1f2685ffb
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/StoneHavenPilgrim.java
@@ -0,0 +1,56 @@
+package mage.cards.s;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.AttacksTriggeredAbility;
+import mage.abilities.condition.Condition;
+import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
+import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
+import mage.abilities.effects.common.continuous.BoostSourceEffect;
+import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
+import mage.abilities.keyword.LifelinkAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+import mage.filter.StaticFilters;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class StoneHavenPilgrim extends CardImpl {
+
+    private static final Condition condition
+            = new PermanentsOnTheBattlefieldCondition(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT);
+
+    public StoneHavenPilgrim(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}");
+
+        this.subtype.add(SubType.KOR);
+        this.subtype.add(SubType.CLERIC);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(2);
+
+        // Whenever Stone Haven Pilgrim attacks, if you control an artifact or enchantment, Stone Haven Pilgrim gets +1/+1 and gains lifelink until end of turn.
+        Ability ability = new ConditionalInterveningIfTriggeredAbility(
+                new AttacksTriggeredAbility(
+                        new BoostSourceEffect(1, 1, Duration.EndOfTurn), false
+                ), condition, "Whenever {this} attacks, if you control an artifact or enchantment, " +
+                "{this} gets +1/+1 and gains lifelink until end of turn."
+        );
+        ability.addEffect(new GainAbilitySourceEffect(LifelinkAbility.getInstance(), Duration.EndOfTurn));
+        this.addAbility(ability);
+    }
+
+    private StoneHavenPilgrim(final StoneHavenPilgrim card) {
+        super(card);
+    }
+
+    @Override
+    public StoneHavenPilgrim copy() {
+        return new StoneHavenPilgrim(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index db656a586d..32c795fbf6 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -393,6 +393,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Spitting Earth", 364, Rarity.COMMON, mage.cards.s.SpittingEarth.class));
         cards.add(new SetCardInfo("Sporemound", 433, Rarity.COMMON, mage.cards.s.Sporemound.class));
         cards.add(new SetCardInfo("Stab Wound", 281, Rarity.UNCOMMON, mage.cards.s.StabWound.class));
+        cards.add(new SetCardInfo("Stone Haven Pilgrim", 6, Rarity.UNCOMMON, mage.cards.s.StoneHavenPilgrim.class));
         cards.add(new SetCardInfo("Storm Sculptor", 179, Rarity.COMMON, mage.cards.s.StormSculptor.class));
         cards.add(new SetCardInfo("Supply Runners", 7, Rarity.UNCOMMON, mage.cards.s.SupplyRunners.class));
         cards.add(new SetCardInfo("Suspicious Bookcase", 487, Rarity.UNCOMMON, mage.cards.s.SuspiciousBookcase.class));

From bb08d51e9e24ff7f7db7a7a37252381c095fcf74 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sun, 21 Jun 2020 10:09:55 -0400
Subject: [PATCH 442/586] Implemented Chained Brute

---
 Mage.Sets/src/mage/cards/c/ChainedBrute.java | 56 ++++++++++++++++++++
 Mage.Sets/src/mage/sets/Jumpstart.java       |  1 +
 2 files changed, 57 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/c/ChainedBrute.java

diff --git a/Mage.Sets/src/mage/cards/c/ChainedBrute.java b/Mage.Sets/src/mage/cards/c/ChainedBrute.java
new file mode 100644
index 0000000000..6d3134d511
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/c/ChainedBrute.java
@@ -0,0 +1,56 @@
+package mage.cards.c;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.ActivateIfConditionActivatedAbility;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.condition.common.MyTurnCondition;
+import mage.abilities.costs.common.SacrificeTargetCost;
+import mage.abilities.costs.mana.GenericManaCost;
+import mage.abilities.effects.common.DontUntapInControllersUntapStepSourceEffect;
+import mage.abilities.effects.common.UntapSourceEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.Zone;
+import mage.filter.StaticFilters;
+import mage.target.common.TargetControlledPermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class ChainedBrute extends CardImpl {
+
+    public ChainedBrute(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}");
+
+        this.subtype.add(SubType.DEVIL);
+        this.power = new MageInt(4);
+        this.toughness = new MageInt(3);
+
+        // Chained Brute doesn't untap during your untap step.
+        this.addAbility(new SimpleStaticAbility(new DontUntapInControllersUntapStepSourceEffect()));
+
+        // {1}, Sacrifice another creature: Untap Chained Brute. Activate this ability only during your turn.
+        Ability ability = new ActivateIfConditionActivatedAbility(
+                Zone.BATTLEFIELD, new UntapSourceEffect(),
+                new GenericManaCost(1), MyTurnCondition.instance
+        );
+        ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(
+                StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE
+        )));
+        this.addAbility(ability);
+    }
+
+    private ChainedBrute(final ChainedBrute card) {
+        super(card);
+    }
+
+    @Override
+    public ChainedBrute copy() {
+        return new ChainedBrute(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index 32c795fbf6..e6be59d09f 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -94,6 +94,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Celestial Mantle", 96, Rarity.RARE, mage.cards.c.CelestialMantle.class));
         cards.add(new SetCardInfo("Cemetery Recruitment", 217, Rarity.COMMON, mage.cards.c.CemeteryRecruitment.class));
         cards.add(new SetCardInfo("Chain Lightning", 302, Rarity.UNCOMMON, mage.cards.c.ChainLightning.class));
+        cards.add(new SetCardInfo("Chained Brute", 19, Rarity.UNCOMMON, mage.cards.c.ChainedBrute.class));
         cards.add(new SetCardInfo("Chamber Sentry", 461, Rarity.RARE, mage.cards.c.ChamberSentry.class));
         cards.add(new SetCardInfo("Champion of Lambholt", 383, Rarity.RARE, mage.cards.c.ChampionOfLambholt.class));
         cards.add(new SetCardInfo("Charmbreaker Devils", 303, Rarity.RARE, mage.cards.c.CharmbreakerDevils.class));

From 6c8d0046a7cf014c40821944346c76f529c8ace6 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sun, 21 Jun 2020 10:15:09 -0400
Subject: [PATCH 443/586] Implemented Immolating Gyre

---
 .../src/mage/cards/i/ImmolatingGyre.java      | 46 +++++++++++++++++++
 Mage.Sets/src/mage/sets/Jumpstart.java        |  1 +
 2 files changed, 47 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/i/ImmolatingGyre.java

diff --git a/Mage.Sets/src/mage/cards/i/ImmolatingGyre.java b/Mage.Sets/src/mage/cards/i/ImmolatingGyre.java
new file mode 100644
index 0000000000..eaccc74c36
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/i/ImmolatingGyre.java
@@ -0,0 +1,46 @@
+package mage.cards.i;
+
+import mage.abilities.dynamicvalue.DynamicValue;
+import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount;
+import mage.abilities.effects.common.DamageAllEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.TargetController;
+import mage.filter.FilterPermanent;
+import mage.filter.StaticFilters;
+import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class ImmolatingGyre extends CardImpl {
+
+    private static final DynamicValue xValue = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_INSTANT_AND_SORCERY);
+    private static final FilterPermanent filter = new FilterCreatureOrPlaneswalkerPermanent();
+
+    static {
+        filter.add(TargetController.NOT_YOU.getControllerPredicate());
+    }
+
+    public ImmolatingGyre(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{R}{R}");
+
+        // Immolating Gyre deals X damage to each creature and planeswalker you don't control, where X is the number of instant and sorcery cards in your graveyard.
+        this.getSpellAbility().addEffect(new DamageAllEffect(xValue, filter).setText(
+                "{this} deals X damage to each creature and planeswalker you don't control, " +
+                        "where X is the number of instant and sorcery cards in your graveyard"
+        ));
+    }
+
+    private ImmolatingGyre(final ImmolatingGyre card) {
+        super(card);
+    }
+
+    @Override
+    public ImmolatingGyre copy() {
+        return new ImmolatingGyre(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index e6be59d09f..62c1b2f342 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -212,6 +212,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Homing Lightning", 335, Rarity.UNCOMMON, mage.cards.h.HomingLightning.class));
         cards.add(new SetCardInfo("Hungry Flames", 336, Rarity.COMMON, mage.cards.h.HungryFlames.class));
         cards.add(new SetCardInfo("Hunter's Insight", 402, Rarity.UNCOMMON, mage.cards.h.HuntersInsight.class));
+        cards.add(new SetCardInfo("Immolating Gyre", 20, Rarity.MYTHIC, mage.cards.i.ImmolatingGyre.class));
         cards.add(new SetCardInfo("Indomitable Will", 109, Rarity.COMMON, mage.cards.i.IndomitableWill.class));
         cards.add(new SetCardInfo("Inferno Hellion", 337, Rarity.UNCOMMON, mage.cards.i.InfernoHellion.class));
         cards.add(new SetCardInfo("Initiate's Companion", 403, Rarity.COMMON, mage.cards.i.InitiatesCompanion.class));

From 3b6a1289a4ec3c2817ba9ce10e6d88ccd66d992d Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sun, 21 Jun 2020 10:20:17 -0400
Subject: [PATCH 444/586] Implemented Spiteful Prankster

---
 .../src/mage/cards/s/SpitefulPrankster.java   | 56 +++++++++++++++++++
 Mage.Sets/src/mage/sets/Jumpstart.java        |  1 +
 2 files changed, 57 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/s/SpitefulPrankster.java

diff --git a/Mage.Sets/src/mage/cards/s/SpitefulPrankster.java b/Mage.Sets/src/mage/cards/s/SpitefulPrankster.java
new file mode 100644
index 0000000000..b0e11ae8e3
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/SpitefulPrankster.java
@@ -0,0 +1,56 @@
+package mage.cards.s;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.DiesCreatureTriggeredAbility;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.condition.common.MyTurnCondition;
+import mage.abilities.decorator.ConditionalContinuousEffect;
+import mage.abilities.effects.common.DamageTargetEffect;
+import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
+import mage.abilities.hint.common.MyTurnHint;
+import mage.abilities.keyword.FirstStrikeAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+import mage.target.common.TargetPlayerOrPlaneswalker;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class SpitefulPrankster extends CardImpl {
+
+    public SpitefulPrankster(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}");
+
+        this.subtype.add(SubType.DEVIL);
+        this.power = new MageInt(3);
+        this.toughness = new MageInt(2);
+
+        // As long as it's your turn, Spiteful Prankster has first strike.
+        this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect(
+                new GainAbilitySourceEffect(FirstStrikeAbility.getInstance(), Duration.WhileOnBattlefield),
+                MyTurnCondition.instance, "As long as it's your turn, {this} has first strike."
+        )).addHint(MyTurnHint.instance));
+
+        // Whenever another creature dies, Spiteful Prankster deals 1 damage to target player or planeswalker.
+        Ability ability = new DiesCreatureTriggeredAbility(
+                new DamageTargetEffect(1), false, true
+        );
+        ability.addTarget(new TargetPlayerOrPlaneswalker());
+        this.addAbility(ability);
+    }
+
+    private SpitefulPrankster(final SpitefulPrankster card) {
+        super(card);
+    }
+
+    @Override
+    public SpitefulPrankster copy() {
+        return new SpitefulPrankster(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index 62c1b2f342..b38db8550d 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -392,6 +392,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Soul Salvage", 280, Rarity.COMMON, mage.cards.s.SoulSalvage.class));
         cards.add(new SetCardInfo("Soul of the Harvest", 432, Rarity.RARE, mage.cards.s.SoulOfTheHarvest.class));
         cards.add(new SetCardInfo("Spectral Sailor", 178, Rarity.UNCOMMON, mage.cards.s.SpectralSailor.class));
+        cards.add(new SetCardInfo("Spiteful Prankster", 26, Rarity.UNCOMMON, mage.cards.s.SpitefulPrankster.class));
         cards.add(new SetCardInfo("Spitting Earth", 364, Rarity.COMMON, mage.cards.s.SpittingEarth.class));
         cards.add(new SetCardInfo("Sporemound", 433, Rarity.COMMON, mage.cards.s.Sporemound.class));
         cards.add(new SetCardInfo("Stab Wound", 281, Rarity.UNCOMMON, mage.cards.s.StabWound.class));

From b670ec516e2d362c57ef2a36e91c52883d243870 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sun, 21 Jun 2020 11:21:43 -0400
Subject: [PATCH 445/586] Implemented Steel-Plume Marshal

---
 .../src/mage/cards/s/SteelPlumeMarshal.java   | 58 +++++++++++++++++++
 Mage.Sets/src/mage/sets/Jumpstart.java        |  1 +
 2 files changed, 59 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/s/SteelPlumeMarshal.java

diff --git a/Mage.Sets/src/mage/cards/s/SteelPlumeMarshal.java b/Mage.Sets/src/mage/cards/s/SteelPlumeMarshal.java
new file mode 100644
index 0000000000..0bb3a3dcc3
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/SteelPlumeMarshal.java
@@ -0,0 +1,58 @@
+package mage.cards.s;
+
+import mage.MageInt;
+import mage.abilities.common.AttacksTriggeredAbility;
+import mage.abilities.effects.common.continuous.BoostAllEffect;
+import mage.abilities.keyword.FlyingAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+import mage.constants.TargetController;
+import mage.filter.common.FilterCreaturePermanent;
+import mage.filter.predicate.mageobject.AbilityPredicate;
+import mage.filter.predicate.permanent.AttackingPredicate;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class SteelPlumeMarshal extends CardImpl {
+
+    private static final FilterCreaturePermanent filter
+            = new FilterCreaturePermanent("attacking creatures you control with flying");
+
+    static {
+        filter.add(AttackingPredicate.instance);
+        filter.add(new AbilityPredicate(FlyingAbility.class));
+        filter.add(TargetController.YOU.getControllerPredicate());
+    }
+
+    public SteelPlumeMarshal(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}{W}");
+
+        this.subtype.add(SubType.BIRD);
+        this.subtype.add(SubType.SOLDIER);
+        this.power = new MageInt(3);
+        this.toughness = new MageInt(3);
+
+        // Flying
+        this.addAbility(FlyingAbility.getInstance());
+
+        // Whenever Steel-Plume Marshal attacks, other attacking creatures you control with flying get +2/+2 until end of turn.
+        this.addAbility(new AttacksTriggeredAbility(new BoostAllEffect(
+                2, 2, Duration.EndOfTurn, filter, true
+        ), false));
+    }
+
+    private SteelPlumeMarshal(final SteelPlumeMarshal card) {
+        super(card);
+    }
+
+    @Override
+    public SteelPlumeMarshal copy() {
+        return new SteelPlumeMarshal(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index b38db8550d..cedf51fe57 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -396,6 +396,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Spitting Earth", 364, Rarity.COMMON, mage.cards.s.SpittingEarth.class));
         cards.add(new SetCardInfo("Sporemound", 433, Rarity.COMMON, mage.cards.s.Sporemound.class));
         cards.add(new SetCardInfo("Stab Wound", 281, Rarity.UNCOMMON, mage.cards.s.StabWound.class));
+        cards.add(new SetCardInfo("Steel-Plume Marshal", 5, Rarity.RARE, mage.cards.s.SteelPlumeMarshal.class));
         cards.add(new SetCardInfo("Stone Haven Pilgrim", 6, Rarity.UNCOMMON, mage.cards.s.StoneHavenPilgrim.class));
         cards.add(new SetCardInfo("Storm Sculptor", 179, Rarity.COMMON, mage.cards.s.StormSculptor.class));
         cards.add(new SetCardInfo("Supply Runners", 7, Rarity.UNCOMMON, mage.cards.s.SupplyRunners.class));

From 9e8a5131ae1436cbdf4314a8e0c0e9576c4cb4de Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Sun, 21 Jun 2020 21:22:19 +0400
Subject: [PATCH 446/586] Test helper for #6684 (uncomment and run
 checkMissingCardData)

---
 .../java/mage/verify/VerifyCardDataTest.java  | 25 ++++++++++++++++---
 1 file changed, 21 insertions(+), 4 deletions(-)

diff --git a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java
index 90be9b1bb7..7661bc9f8e 100644
--- a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java
+++ b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java
@@ -1,16 +1,15 @@
 package mage.verify;
 
 import mage.ObjectColor;
+import mage.abilities.Ability;
+import mage.abilities.effects.CostModificationEffect;
 import mage.abilities.keyword.MultikickerAbility;
 import mage.cards.*;
 import mage.cards.basiclands.BasicLand;
 import mage.cards.repository.CardInfo;
 import mage.cards.repository.CardRepository;
 import mage.cards.repository.CardScanner;
-import mage.constants.CardType;
-import mage.constants.Rarity;
-import mage.constants.SubType;
-import mage.constants.SuperType;
+import mage.constants.*;
 import mage.game.command.Plane;
 import mage.game.draft.RateCard;
 import mage.game.permanent.token.Token;
@@ -518,6 +517,24 @@ public class VerifyCardDataTest {
                 // 3. check that getMana works without NPE errors (it uses getNetMana with empty game param for AI score calcs)
                 // https://github.com/magefree/mage/issues/6300
                 card.getMana();
+
+                // 4. check cost modification effects must be ALL instead STACK zone
+                for (Ability ability : card.getAbilities()) {
+                    // uncomment to get playable problems, see https://github.com/magefree/mage/issues/6684
+                    if (ability.getAllEffects().stream().noneMatch(e -> e instanceof CostModificationEffect)) {
+                        continue;
+                    }
+
+                    // must change zone to ALL and check conditions/filters (no spell filters, only cards)
+                    if (ability.getZone() == Zone.STACK) {
+                        //errorsList.add("error, cost modification effect must be in ALL zone instead " + ability.getZone() + " - " + card.getName() + " - " + ability.toString());
+                    }
+
+                    // must check conditions/filters (no spell filters, only cards)
+                    if (ability.getZone() == Zone.BATTLEFIELD) {
+                        //warningsList.add("warning, must check cost modification filters/conditions " + card.getName() + " - " + ability.toString());
+                    }
+                }
             }
         }
 

From 028ea0e04c26240a590a83445773980ca19c8630 Mon Sep 17 00:00:00 2001
From: 18ths <13023067+18ths@users.noreply.github.com>
Date: Sun, 21 Jun 2020 20:00:53 +0200
Subject: [PATCH 447/586] added test for copy anthem effect issue

---
 .../test/rollback/CopyAnthemEffectTest.java   | 38 +++++++++++++++++++
 1 file changed, 38 insertions(+)
 create mode 100644 Mage.Tests/src/test/java/org/mage/test/rollback/CopyAnthemEffectTest.java

diff --git a/Mage.Tests/src/test/java/org/mage/test/rollback/CopyAnthemEffectTest.java b/Mage.Tests/src/test/java/org/mage/test/rollback/CopyAnthemEffectTest.java
new file mode 100644
index 0000000000..787559a83c
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/rollback/CopyAnthemEffectTest.java
@@ -0,0 +1,38 @@
+package org.mage.test.rollback;
+
+import mage.constants.PhaseStep;
+import mage.constants.Zone;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestPlayerBase;
+
+public class CopyAnthemEffectTest extends CardTestPlayerBase {
+
+    // Addresses issue #6618 - Creatures get +1/+1 when we cancel the cast of a spell
+
+    // Creatures of one player get +1/+1 every time someone cancel the cast of a spell.
+    // Looks like the repeating effect was Force Of Virtue's static ability
+    // There was also a Mirrormade cast to copy Force of Virtue
+
+    // Further investigation shown that the problem could be also reduced to any anthem effect copied by other permanent, then rollbacking
+    @Test
+    public void copyAnthemEffect() {
+        addCard(Zone.BATTLEFIELD, playerA, "Runeclaw Bear"); // 2/2 vanilla creature
+        addCard(Zone.BATTLEFIELD, playerA, "Glorious Anthem"); // creatures you control have +1/+1
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 3);
+
+        addCard(Zone.HAND, playerA, "Copy Enchantment");
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Copy Enchantment");
+        setChoice(playerA, "Yes");
+        setChoice(playerA, "Glorious Anthem");
+
+        rollbackTurns(3, PhaseStep.UPKEEP, playerA, 0);
+
+        setStopAt(3, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPowerToughness(playerA, "Runeclaw Bear", 4, 4);
+    }
+
+}

From d74c2c8355c63b12994c1a3668c7a94f208d5252 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sun, 21 Jun 2020 22:14:03 -0400
Subject: [PATCH 448/586] Implemented Tinybones, Trinket Thief

---
 .../mage/cards/t/TinybonesTrinketThief.java   | 128 ++++++++++++++++++
 Mage.Sets/src/mage/sets/Jumpstart.java        |   1 +
 .../common/LoseLifeOpponentsEffect.java       |   2 +-
 3 files changed, 130 insertions(+), 1 deletion(-)
 create mode 100644 Mage.Sets/src/mage/cards/t/TinybonesTrinketThief.java

diff --git a/Mage.Sets/src/mage/cards/t/TinybonesTrinketThief.java b/Mage.Sets/src/mage/cards/t/TinybonesTrinketThief.java
new file mode 100644
index 0000000000..8c5cfdfd5b
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/t/TinybonesTrinketThief.java
@@ -0,0 +1,128 @@
+package mage.cards.t;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.BeginningOfEndStepTriggeredAbility;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.condition.Condition;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.DrawCardSourceControllerEffect;
+import mage.abilities.effects.common.LoseLifeSourceControllerEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.*;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+import mage.players.Player;
+import mage.watchers.Watcher;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class TinybonesTrinketThief extends CardImpl {
+
+    public TinybonesTrinketThief(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}");
+
+        this.addSuperType(SuperType.LEGENDARY);
+        this.subtype.add(SubType.SKELETON);
+        this.subtype.add(SubType.ROGUE);
+        this.power = new MageInt(1);
+        this.toughness = new MageInt(2);
+
+        // At the beginning of each end step, if an opponent discarded a card this turn, you draw a card and you lose 1 life.
+        Ability ability = new ConditionalInterveningIfTriggeredAbility(
+                new BeginningOfEndStepTriggeredAbility(
+                        new DrawCardSourceControllerEffect(1),
+                        TargetController.EACH_PLAYER, false
+                ), TinybonesTrinketThiefCondition.instance, "At the beginning of each end step, " +
+                "if an opponent discarded a card this turn, you draw a card and you lose 1 life."
+        );
+        ability.addEffect(new LoseLifeSourceControllerEffect(1));
+        this.addAbility(ability, new TinybonesTrinketThiefWatcher());
+
+        // {4}{B}{B}: Each opponent with no cards in hand loses 10 life.
+        this.addAbility(new SimpleActivatedAbility(new TinybonesTrinketThiefEffect(), new ManaCostsImpl("{4}{B}{B}")));
+    }
+
+    private TinybonesTrinketThief(final TinybonesTrinketThief card) {
+        super(card);
+    }
+
+    @Override
+    public TinybonesTrinketThief copy() {
+        return new TinybonesTrinketThief(this);
+    }
+}
+
+enum TinybonesTrinketThiefCondition implements Condition {
+    instance;
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        TinybonesTrinketThiefWatcher watcher = game.getState().getWatcher(TinybonesTrinketThiefWatcher.class);
+        return watcher != null && watcher.checkPlayer(source.getControllerId());
+    }
+}
+
+class TinybonesTrinketThiefWatcher extends Watcher {
+
+    private final Set<UUID> playerSet = new HashSet<>();
+
+    TinybonesTrinketThiefWatcher() {
+        super(WatcherScope.GAME);
+    }
+
+    @Override
+    public void watch(GameEvent event, Game game) {
+        if (event.getType() == GameEvent.EventType.DISCARDED_CARD) {
+            playerSet.addAll(game.getOpponents(event.getPlayerId()));
+        }
+    }
+
+    @Override
+    public void reset() {
+        playerSet.clear();
+        super.reset();
+    }
+
+    boolean checkPlayer(UUID playerId) {
+        return playerSet.contains(playerId);
+    }
+}
+
+class TinybonesTrinketThiefEffect extends OneShotEffect {
+
+    TinybonesTrinketThiefEffect() {
+        super(Outcome.Benefit);
+        staticText = "each opponent with no cards in hand loses 10 life";
+    }
+
+    private TinybonesTrinketThiefEffect(final TinybonesTrinketThiefEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public TinybonesTrinketThiefEffect copy() {
+        return new TinybonesTrinketThiefEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        for (UUID playerId : game.getOpponents(source.getControllerId())) {
+            Player player = game.getPlayer(source.getControllerId());
+            if (player != null && player.getHand().isEmpty()) {
+                player.loseLife(10, game, false);
+            }
+        }
+        return true;
+    }
+}
+// tiny bones comin out my
+// tiny bones comin out my mouth
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index cedf51fe57..344f54847e 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -426,6 +426,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Thundering Spineback", 437, Rarity.UNCOMMON, mage.cards.t.ThunderingSpineback.class));
         cards.add(new SetCardInfo("Tibalt's Rager", 366, Rarity.UNCOMMON, mage.cards.t.TibaltsRager.class));
         cards.add(new SetCardInfo("Time to Feed", 438, Rarity.COMMON, mage.cards.t.TimeToFeed.class));
+        cards.add(new SetCardInfo("Tinybones, Trinket Thief", 17, Rarity.MYTHIC, mage.cards.t.TinybonesTrinketThief.class));
         cards.add(new SetCardInfo("Tithebearer Giant", 284, Rarity.COMMON, mage.cards.t.TithebearerGiant.class));
         cards.add(new SetCardInfo("Torch Fiend", 367, Rarity.COMMON, mage.cards.t.TorchFiend.class));
         cards.add(new SetCardInfo("Towering Titan", 31, Rarity.MYTHIC, mage.cards.t.ToweringTitan.class));
diff --git a/Mage/src/main/java/mage/abilities/effects/common/LoseLifeOpponentsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/LoseLifeOpponentsEffect.java
index ead8433dc7..eaa3c26e12 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/LoseLifeOpponentsEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/LoseLifeOpponentsEffect.java
@@ -20,7 +20,7 @@ public class LoseLifeOpponentsEffect extends OneShotEffect {
 
     private DynamicValue amount;
 
-    public LoseLifeOpponentsEffect(int amount) {
+    publicLoseLifeOpponentsEffect(int amount) {
         this(StaticValue.get(amount));
     }
 

From 7caf4f61cb90f16adb5b956e5fe6e8df2233f835 Mon Sep 17 00:00:00 2001
From: arcox <10953229+arcox@users.noreply.github.com>
Date: Mon, 22 Jun 2020 02:28:31 +0000
Subject: [PATCH 449/586] Implement Demonic Embrace from Core 2021 (#6692)

---
 .../src/mage/cards/d/DemonicEmbrace.java      | 108 ++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |   1 +
 2 files changed, 109 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/d/DemonicEmbrace.java

diff --git a/Mage.Sets/src/mage/cards/d/DemonicEmbrace.java b/Mage.Sets/src/mage/cards/d/DemonicEmbrace.java
new file mode 100644
index 0000000000..a206bc9f76
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/d/DemonicEmbrace.java
@@ -0,0 +1,108 @@
+package mage.cards.d;
+
+import java.util.UUID;
+
+import mage.abilities.costs.Cost;
+import mage.abilities.costs.Costs;
+import mage.abilities.costs.CostsImpl;
+import mage.abilities.costs.common.DiscardCardCost;
+import mage.abilities.costs.common.PayLifeCost;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.effects.AsThoughEffectImpl;
+import mage.constants.*;
+import mage.game.Game;
+import mage.players.Player;
+import mage.target.common.TargetCreaturePermanent;
+import mage.abilities.Ability;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.effects.Effect;
+import mage.abilities.effects.common.AttachEffect;
+import mage.abilities.effects.common.continuous.AddCardSubtypeAttachedEffect;
+import mage.abilities.effects.common.continuous.BoostEnchantedEffect;
+import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect;
+import mage.target.TargetPermanent;
+import mage.abilities.keyword.EnchantAbility;
+import mage.abilities.keyword.FlyingAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+
+/**
+ * @author arcox
+ */
+public final class DemonicEmbrace extends CardImpl {
+
+    public DemonicEmbrace(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}{B}");
+
+        this.subtype.add(SubType.AURA);
+
+        // Enchant creature
+        TargetPermanent auraTarget = new TargetCreaturePermanent();
+        this.getSpellAbility().addTarget(auraTarget);
+        this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature));
+        Ability ability = new EnchantAbility(auraTarget.getTargetName());
+        this.addAbility(ability);
+
+        // Enchanted creature gets +3/+1, has flying, and is a Demon in addition to its other types.
+        ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(3, 1, Duration.WhileOnBattlefield));
+        Effect effect = new GainAbilityAttachedEffect(FlyingAbility.getInstance(), AttachmentType.AURA);
+        effect.setText(", has flying");
+        ability.addEffect(effect);
+        effect = new AddCardSubtypeAttachedEffect(SubType.DEMON, Duration.WhileOnBattlefield, AttachmentType.AURA);
+        effect.setText(", and is a Demon in addition to its other types");
+        ability.addEffect(effect);
+        this.addAbility(ability);
+
+        // You may cast Demonic Embrace from your graveyard by paying 3 life and discarding a card in addition to paying its other costs.
+        this.addAbility(new SimpleStaticAbility(Zone.ALL, new DemonicEmbracePlayEffect()));
+    }
+
+    public DemonicEmbrace(final DemonicEmbrace card) {
+        super(card);
+    }
+
+    @Override
+    public DemonicEmbrace copy() {
+        return new DemonicEmbrace(this);
+    }
+}
+
+
+class DemonicEmbracePlayEffect extends AsThoughEffectImpl {
+
+    public DemonicEmbracePlayEffect() {
+        super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfGame, Outcome.Benefit);
+        staticText = "You may cast {this} from your graveyard by paying 3 life and discarding a card in addition to paying its other costs";
+    }
+
+    public DemonicEmbracePlayEffect(final DemonicEmbracePlayEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        return true;
+    }
+
+    @Override
+    public DemonicEmbracePlayEffect copy() {
+        return new DemonicEmbracePlayEffect(this);
+    }
+
+    @Override
+    public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) {
+        if (sourceId.equals(source.getSourceId()) && source.isControlledBy(affectedControllerId)) {
+            if (game.getState().getZone(source.getSourceId()) == Zone.GRAVEYARD) {
+                Player player = game.getPlayer(affectedControllerId);
+                if (player != null) {
+                    Costs<Cost> costs = new CostsImpl<>();
+                    costs.add(new PayLifeCost(3));
+                    costs.add(new DiscardCardCost());
+                    player.setCastSourceIdWithAlternateMana(sourceId, new ManaCostsImpl<>("{1}{B}{B}"), costs);
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 9753ea0531..ce4281b2b5 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -84,6 +84,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Daybreak Charger", 14, Rarity.COMMON, mage.cards.d.DaybreakCharger.class));
         cards.add(new SetCardInfo("Deathbloom Thallid", 94, Rarity.COMMON, mage.cards.d.DeathbloomThallid.class));
         cards.add(new SetCardInfo("Defiant Strike", 15, Rarity.COMMON, mage.cards.d.DefiantStrike.class));
+        cards.add(new SetCardInfo("Demonic Embrace", 95, Rarity.RARE, mage.cards.d.DemonicEmbrace.class));
         cards.add(new SetCardInfo("Destructive Tampering", 141, Rarity.COMMON, mage.cards.d.DestructiveTampering.class));
         cards.add(new SetCardInfo("Dire Fleet Warmonger", 217, Rarity.UNCOMMON, mage.cards.d.DireFleetWarmonger.class));
         cards.add(new SetCardInfo("Discontinuity", 48, Rarity.MYTHIC, mage.cards.d.Discontinuity.class));

From 6754636f86d36b90d59af60359013303d849ed70 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Sun, 21 Jun 2020 22:39:09 -0400
Subject: [PATCH 450/586] fixed an error

---
 .../mage/abilities/effects/common/LoseLifeOpponentsEffect.java  | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Mage/src/main/java/mage/abilities/effects/common/LoseLifeOpponentsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/LoseLifeOpponentsEffect.java
index eaa3c26e12..ead8433dc7 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/LoseLifeOpponentsEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/LoseLifeOpponentsEffect.java
@@ -20,7 +20,7 @@ public class LoseLifeOpponentsEffect extends OneShotEffect {
 
     private DynamicValue amount;
 
-    publicLoseLifeOpponentsEffect(int amount) {
+    public LoseLifeOpponentsEffect(int amount) {
         this(StaticValue.get(amount));
     }
 

From cd624b2158ed00dca7f676e26bb8134a67864b84 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Mon, 22 Jun 2020 08:34:53 +0400
Subject: [PATCH 451/586] * Special mana payments like convoke/delve - fixed
 that it can't be used to cast card from graveyard (example: Hogaak, Arisen
 Necropolis, see #6680);

---
 .../cards/abilities/keywords/ConvokeTest.java |  48 ++++++
 .../java/org/mage/test/player/TestPlayer.java |   8 +-
 .../base/impl/CardTestPlayerAPIImpl.java      |  16 +-
 .../main/java/mage/players/PlayerImpl.java    | 137 ++++++++----------
 4 files changed, 129 insertions(+), 80 deletions(-)

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ConvokeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ConvokeTest.java
index d492972341..58b3ebb4da 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ConvokeTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ConvokeTest.java
@@ -295,4 +295,52 @@ public class ConvokeTest extends CardTestPlayerBaseWithAIHelps {
         execute();
         assertAllCommandsUsed();
     }
+
+    @Test
+    public void test_Other_CastFromGraveayrd_Convoke() {
+        // https://github.com/magefree/mage/issues/6680
+
+        // {5}{B/G}{B/G}
+        // You can't spend mana to cast this spell.
+        // Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature's color.)
+        // Delve (Each card you exile from your graveyard while casting this spell pays for {1}.)
+        // You may cast Hogaak, Arisen Necropolis from your graveyard.
+        addCard(Zone.GRAVEYARD, playerA, "Hogaak, Arisen Necropolis", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 7);
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Hogaak, Arisen Necropolis");
+        addTarget(playerA, "Balduvian Bears", 7); // convoke pay
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerA, "Hogaak, Arisen Necropolis", 1);
+    }
+
+    @Test
+    public void test_Other_CastFromGraveayrd_ConvokeAndDelve() {
+        // https://github.com/magefree/mage/issues/6680
+
+        // {5}{B/G}{B/G}
+        // You can't spend mana to cast this spell.
+        // Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature's color.)
+        // Delve (Each card you exile from your graveyard while casting this spell pays for {1}.)
+        // You may cast Hogaak, Arisen Necropolis from your graveyard.
+        addCard(Zone.GRAVEYARD, playerA, "Hogaak, Arisen Necropolis", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 2); // convoke (you can't pay normal mana here)
+        addCard(Zone.GRAVEYARD, playerA, "Balduvian Bears", 5); // delve
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Hogaak, Arisen Necropolis");
+        addTarget(playerA, "Balduvian Bears", 2); // convoke pay
+        setChoice(playerA, "Balduvian Bears", 5); // delve pay
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerA, "Hogaak, Arisen Necropolis", 1);
+    }
 }
\ No newline at end of file
diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
index 171038de3a..e4cf1bae82 100644
--- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
+++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
@@ -1711,7 +1711,7 @@ public class TestPlayer implements Player {
                 printAbilities(game, computerPlayer.getPlayable(game, true));
                 printEnd();
             }
-            Assert.fail("Missing " + choiceType + " def for"
+            Assert.fail("Missing " + choiceType.toUpperCase(Locale.ENGLISH) + " def for"
                     + " turn " + game.getTurnNum()
                     + ", step " + (game.getStep() != null ? game.getStep().getType().name() : "not started")
                     + ", " + this.getName()
@@ -2663,17 +2663,17 @@ public class TestPlayer implements Player {
     public boolean putCardsOnTopOfLibrary(Card card, Game game, Ability source, boolean anyOrder) {
         return computerPlayer.putCardsOnTopOfLibrary(card, game, source, anyOrder);
     }
-    
+
     @Override
     public boolean shuffleCardsToLibrary(Cards cards, Game game, Ability source) {
         return computerPlayer.shuffleCardsToLibrary(cards, game, source);
     }
-    
+
     @Override
     public boolean shuffleCardsToLibrary(Card card, Game game, Ability source) {
         return computerPlayer.shuffleCardsToLibrary(card, game, source);
     }
-    
+
     @Override
     public void setCastSourceIdWithAlternateMana(UUID sourceId, ManaCosts manaCosts, Costs costs) {
         computerPlayer.setCastSourceIdWithAlternateMana(sourceId, manaCosts, costs);
diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
index f3afb9b960..97940fff69 100644
--- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
+++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
@@ -1735,7 +1735,13 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      */
     // TODO: mode options doesn't work here (see BrutalExpulsionTest)
     public void addTarget(TestPlayer player, String target) {
-        player.addTarget(target);
+        addTarget(player, target, 1);
+    }
+
+    public void addTarget(TestPlayer player, String target, int timesToChoose) {
+        for (int i = 0; i < timesToChoose; i++) {
+            player.addTarget(target);
+        }
     }
 
     /**
@@ -1745,7 +1751,13 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * @param targetPlayer
      */
     public void addTarget(TestPlayer player, TestPlayer targetPlayer) {
-        player.addTarget("targetPlayer=" + targetPlayer.getName());
+        addTarget(player, targetPlayer, 1);
+    }
+
+    public void addTarget(TestPlayer player, TestPlayer targetPlayer, int timesToChoose) {
+        for (int i = 0; i < timesToChoose; i++) {
+            player.addTarget("targetPlayer=" + targetPlayer.getName());
+        }
     }
 
     /**
diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java
index cdd42ea188..19343ddc0d 100644
--- a/Mage/src/main/java/mage/players/PlayerImpl.java
+++ b/Mage/src/main/java/mage/players/PlayerImpl.java
@@ -69,6 +69,7 @@ import org.apache.log4j.Logger;
 import java.io.Serializable;
 import java.util.*;
 import java.util.Map.Entry;
+import java.util.stream.Collectors;
 
 public abstract class PlayerImpl implements Player, Serializable {
 
@@ -613,9 +614,9 @@ public abstract class PlayerImpl implements Player, Serializable {
                     && this.hasOpponent(sourceControllerId, game)
                     && game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, null, sourceControllerId, game) == null
                     && abilities.stream()
-                            .filter(HexproofBaseAbility.class::isInstance)
-                            .map(HexproofBaseAbility.class::cast)
-                            .anyMatch(ability -> ability.checkObject(source, game))) {
+                    .filter(HexproofBaseAbility.class::isInstance)
+                    .map(HexproofBaseAbility.class::cast)
+                    .anyMatch(ability -> ability.checkObject(source, game))) {
                 return false;
             }
 
@@ -655,7 +656,7 @@ public abstract class PlayerImpl implements Player, Serializable {
                 game.informPlayers(getLogName() + " discards down to "
                         + this.maxHandSize
                         + (this.maxHandSize == 1
-                                ? " hand card" : " hand cards"));
+                        ? " hand card" : " hand cards"));
             }
             discard(hand.size() - this.maxHandSize, false, null, game);
         }
@@ -804,7 +805,7 @@ public abstract class PlayerImpl implements Player, Serializable {
         }
         GameEvent gameEvent = GameEvent.getEvent(GameEvent.EventType.DISCARD_CARD,
                 card.getId(), source == null
-                ? null : source.getSourceId(), playerId);
+                        ? null : source.getSourceId(), playerId);
         gameEvent.setFlag(source != null); // event from effect or from cost (source == null)
         if (game.replaceEvent(gameEvent, source)) {
             return false;
@@ -978,7 +979,7 @@ public abstract class PlayerImpl implements Player, Serializable {
         shuffleLibrary(source, game);
         return status;
     }
-    
+
     @Override
     public boolean shuffleCardsToLibrary(Card card, Game game, Ability source) {
         if (card == null) {
@@ -986,7 +987,7 @@ public abstract class PlayerImpl implements Player, Serializable {
         }
         return shuffleCardsToLibrary(new CardsImpl(card), game, source);
     }
-    
+
     @Override
     public boolean putCardOnTopXOfLibrary(Card card, Game game, Ability source, int xFromTheTop) {
         if (card.isOwnedBy(getId())) {
@@ -1841,9 +1842,9 @@ public abstract class PlayerImpl implements Player, Serializable {
     }
 
     private List<Permanent> getPermanentsThatCanBeUntapped(Game game,
-            List<Permanent> canBeUntapped,
-            RestrictionUntapNotMoreThanEffect handledEffect,
-            Map<Entry<RestrictionUntapNotMoreThanEffect, Set<Ability>>, Integer> notMoreThanEffectsUsage) {
+                                                           List<Permanent> canBeUntapped,
+                                                           RestrictionUntapNotMoreThanEffect handledEffect,
+                                                           Map<Entry<RestrictionUntapNotMoreThanEffect, Set<Ability>>, Integer> notMoreThanEffectsUsage) {
         List<Permanent> leftForUntap = new ArrayList<>();
         // select permanents that can still be untapped
         for (Permanent permanent : canBeUntapped) {
@@ -2552,7 +2553,7 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean searchLibrary(TargetCardInLibrary target, Ability source, Game game, UUID targetPlayerId,
-            boolean triggerEvents) {
+                                 boolean triggerEvents) {
         //20091005 - 701.14c
         Library searchedLibrary = null;
         String searchInfo = null;
@@ -2754,7 +2755,7 @@ public abstract class PlayerImpl implements Player, Serializable {
     /**
      * @param game
      * @param appliedEffects
-     * @param numSides Number of sides the dice has
+     * @param numSides       Number of sides the dice has
      * @return the number that the player rolled
      */
     @Override
@@ -2791,16 +2792,16 @@ public abstract class PlayerImpl implements Player, Serializable {
     /**
      * @param game
      * @param appliedEffects
-     * @param numberChaosSides The number of chaos sides the planar die
-     * currently has (normally 1 but can be 5)
+     * @param numberChaosSides  The number of chaos sides the planar die
+     *                          currently has (normally 1 but can be 5)
      * @param numberPlanarSides The number of chaos sides the planar die
-     * currently has (normally 1)
+     *                          currently has (normally 1)
      * @return the outcome that the player rolled. Either ChaosRoll, PlanarRoll
      * or NilRoll
      */
     @Override
     public PlanarDieRoll rollPlanarDie(Game game, List<UUID> appliedEffects, int numberChaosSides,
-            int numberPlanarSides) {
+                                       int numberPlanarSides) {
         int result = RandomUtil.nextInt(9) + 1;
         PlanarDieRoll roll = PlanarDieRoll.NIL_ROLL;
         if (numberChaosSides + numberPlanarSides > 9) {
@@ -2957,7 +2958,7 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     /**
      * @param ability
-     * @param available if null, it won't be checked if enough mana is available
+     * @param available    if null, it won't be checked if enough mana is available
      * @param sourceObject
      * @param game
      * @return
@@ -3105,6 +3106,19 @@ public abstract class PlayerImpl implements Player, Serializable {
     }
 
     protected ActivatedAbility findActivatedAbilityFromPlayable(Card card, ManaOptions manaAvailable, Ability ability, Game game) {
+
+        // special mana to pay spell cost
+        ManaOptions manaFull = manaAvailable.copy();
+        if (ability instanceof SpellAbility) {
+            for (AlternateManaPaymentAbility altAbility : card.getAbilities(game).stream()
+                    .filter(a -> a instanceof AlternateManaPaymentAbility)
+                    .map(a -> (AlternateManaPaymentAbility) a)
+                    .collect(Collectors.toList())) {
+                ManaOptions manaSpecial = altAbility.getManaOptions(ability, game, ability.getManaCostsToPay());
+                manaFull.addMana(manaSpecial);
+            }
+        }
+
         // replace alternative abilities by real play abilities (e.g. morph/facedown static ability by play land)
         if (ability instanceof ActivatedManaAbilityImpl) {
             // mana ability
@@ -3113,13 +3127,10 @@ public abstract class PlayerImpl implements Player, Serializable {
             }
         } else if (ability instanceof AlternativeSourceCosts) {
             // alternative cost must be replaced by real play ability
-            return findActivatedAbilityFromAlternativeSourceCost(card, manaAvailable, ability, game);
-        } else if (ability instanceof AlternateManaPaymentAbility) {
-            // alternative mana pay like convoke (tap creature to pay)
-            return findActivatedAbilityFromAlternateManaPaymentAbility(card, manaAvailable, (AlternateManaPaymentAbility) ability, game);
+            return findActivatedAbilityFromAlternativeSourceCost(card, manaFull, ability, game);
         } else if (ability instanceof ActivatedAbility) {
             // all other activated ability
-            if (canPlay((ActivatedAbility) ability, manaAvailable, card, game)) {
+            if (canPlay((ActivatedAbility) ability, manaFull, card, game)) {
                 return (ActivatedAbility) ability;
             }
         }
@@ -3151,20 +3162,6 @@ public abstract class PlayerImpl implements Player, Serializable {
         return null;
     }
 
-    protected ActivatedAbility findActivatedAbilityFromAlternateManaPaymentAbility(Card card, ManaOptions manaAvailable, AlternateManaPaymentAbility ability, Game game) {
-        // alternative mana payment allows to pay mana for spell ability
-        SpellAbility spellAbility = card.getSpellAbility();
-        if (spellAbility != null) {
-            ManaOptions manaSpecial = ability.getManaOptions(spellAbility, game, spellAbility.getManaCostsToPay());
-            ManaOptions manaFull = manaAvailable.copy();
-            manaFull.addMana(manaSpecial);
-            if (canPlay(spellAbility, manaFull, card, game)) {
-                return spellAbility;
-            }
-        }
-        return null;
-    }
-
     protected boolean canLandPlayAlternateSourceCostsAbility(Card sourceObject, ManaOptions available, Ability ability, Game game) {
         if (sourceObject != null && !(sourceObject instanceof Permanent)) {
             Ability sourceAbility = sourceObject.getAbilities().stream()
@@ -3197,15 +3194,6 @@ public abstract class PlayerImpl implements Player, Serializable {
         return false;
     }
 
-    private Abilities<ActivatedAbility> getActivatedOnly(Abilities<Ability> list) {
-        Abilities<ActivatedAbility> res = new AbilitiesImpl<>();
-        list.stream()
-                .filter(a -> a instanceof ActivatedAbility)
-                .map(a -> (ActivatedAbility) a)
-                .forEach(res::add);
-        return res;
-    }
-
     private void getPlayableFromCardAll(Game game, Zone fromZone, Card card, ManaOptions availableMana, List<ActivatedAbility> output) {
         if (fromZone == null || card == null) {
             return;
@@ -3214,24 +3202,25 @@ public abstract class PlayerImpl implements Player, Serializable {
         // BASIC abilities
         if (card instanceof SplitCard) {
             SplitCard splitCard = (SplitCard) card;
-            getPlayableFromCardSingle(game, fromZone, splitCard.getLeftHalfCard(), getActivatedOnly(splitCard.getLeftHalfCard().getAbilities(game)), availableMana, output);
-            getPlayableFromCardSingle(game, fromZone, splitCard.getRightHalfCard(), getActivatedOnly(splitCard.getRightHalfCard().getAbilities(game)), availableMana, output);
-            getPlayableFromCardSingle(game, fromZone, splitCard, getActivatedOnly(splitCard.getSharedAbilities(game)), availableMana, output);
+            getPlayableFromCardSingle(game, fromZone, splitCard.getLeftHalfCard(), splitCard.getLeftHalfCard().getAbilities(game), availableMana, output);
+            getPlayableFromCardSingle(game, fromZone, splitCard.getRightHalfCard(), splitCard.getRightHalfCard().getAbilities(game), availableMana, output);
+            getPlayableFromCardSingle(game, fromZone, splitCard, splitCard.getSharedAbilities(game), availableMana, output);
         } else if (card instanceof AdventureCard) {
             // adventure must use different card characteristics for different spells (main or adventure)
             AdventureCard adventureCard = (AdventureCard) card;
-            getPlayableFromCardSingle(game, fromZone, adventureCard.getSpellCard(), getActivatedOnly(adventureCard.getSpellCard().getAbilities(game)), availableMana, output);
-            getPlayableFromCardSingle(game, fromZone, adventureCard, getActivatedOnly(adventureCard.getSharedAbilities(game)), availableMana, output);
+            getPlayableFromCardSingle(game, fromZone, adventureCard.getSpellCard(), adventureCard.getSpellCard().getAbilities(game), availableMana, output);
+            getPlayableFromCardSingle(game, fromZone, adventureCard, adventureCard.getSharedAbilities(game), availableMana, output);
         } else {
-            getPlayableFromCardSingle(game, fromZone, card, getActivatedOnly(card.getAbilities(game)), availableMana, output);
+            getPlayableFromCardSingle(game, fromZone, card, card.getAbilities(game), availableMana, output);
         }
 
         // DYNAMIC ADDED abilities are adds in getAbilities(game)
     }
 
-    private void getPlayableFromCardSingle(Game game, Zone fromZone, Card card, Abilities<ActivatedAbility> candidateAbilities, ManaOptions availableMana, List<ActivatedAbility> output) {
+    private void getPlayableFromCardSingle(Game game, Zone fromZone, Card card, Abilities<Ability> candidateAbilities, ManaOptions availableMana, List<ActivatedAbility> output) {
         // check "can play" condition as affected controller (BUT play from not own hand zone must be checked as original controller)
-        for (ActivatedAbility ability : candidateAbilities.getActivatedAbilities(Zone.ALL)) {
+        // must check all abilities, not activated only
+        for (Ability ability : candidateAbilities) {
             boolean isPlaySpell = (ability instanceof SpellAbility);
             boolean isPlayLand = (ability instanceof PlayLandAbility);
 
@@ -3295,7 +3284,7 @@ public abstract class PlayerImpl implements Player, Serializable {
             }
 
             // from non hand mode (with affected controller)
-            if (canActivateAsHandZone) {
+            if (canActivateAsHandZone && ability.getControllerId() != this.getId()) {
                 UUID savedControllerId = ability.getControllerId();
                 ability.setControllerId(this.getId());
                 try {
@@ -3680,7 +3669,7 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean canPaySacrificeCost(Permanent permanent, UUID sourceId,
-            UUID controllerId, Game game
+                                       UUID controllerId, Game game
     ) {
         return sacrificeCostFilter == null || !sacrificeCostFilter.match(permanent, sourceId, controllerId, game);
     }
@@ -3833,8 +3822,8 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCards(Card card, Zone toZone,
-            Ability source, Game game,
-            boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
+                             Ability source, Game game,
+                             boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
     ) {
         Set<Card> cardList = new HashSet<>();
         if (card != null) {
@@ -3845,22 +3834,22 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCards(Cards cards, Zone toZone,
-            Ability source, Game game
+                             Ability source, Game game
     ) {
         return moveCards(cards.getCards(game), toZone, source, game);
     }
 
     @Override
     public boolean moveCards(Set<Card> cards, Zone toZone,
-            Ability source, Game game
+                             Ability source, Game game
     ) {
         return moveCards(cards, toZone, source, game, false, false, false, null);
     }
 
     @Override
     public boolean moveCards(Set<Card> cards, Zone toZone,
-            Ability source, Game game,
-            boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
+                             Ability source, Game game,
+                             boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
     ) {
         if (cards.isEmpty()) {
             return true;
@@ -3962,8 +3951,8 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCardsToExile(Card card, Ability source,
-            Game game, boolean withName, UUID exileId,
-            String exileZoneName
+                                    Game game, boolean withName, UUID exileId,
+                                    String exileZoneName
     ) {
         Set<Card> cards = new HashSet<>();
         cards.add(card);
@@ -3972,8 +3961,8 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCardsToExile(Set<Card> cards, Ability source,
-            Game game, boolean withName, UUID exileId,
-            String exileZoneName
+                                    Game game, boolean withName, UUID exileId,
+                                    String exileZoneName
     ) {
         if (cards.isEmpty()) {
             return true;
@@ -3989,14 +3978,14 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCardToHandWithInfo(Card card, UUID sourceId,
-            Game game
+                                          Game game
     ) {
         return this.moveCardToHandWithInfo(card, sourceId, game, true);
     }
 
     @Override
     public boolean moveCardToHandWithInfo(Card card, UUID sourceId,
-            Game game, boolean withName
+                                          Game game, boolean withName
     ) {
         boolean result = false;
         Zone fromZone = game.getState().getZone(card.getId());
@@ -4021,7 +4010,7 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public Set<Card> moveCardsToGraveyardWithInfo(Set<Card> allCards, Ability source,
-            Game game, Zone fromZone
+                                                  Game game, Zone fromZone
     ) {
         UUID sourceId = source == null ? null : source.getSourceId();
         Set<Card> movedCards = new LinkedHashSet<>();
@@ -4029,7 +4018,7 @@ public abstract class PlayerImpl implements Player, Serializable {
             // identify cards from one owner
             Cards cards = new CardsImpl();
             UUID ownerId = null;
-            for (Iterator<Card> it = allCards.iterator(); it.hasNext();) {
+            for (Iterator<Card> it = allCards.iterator(); it.hasNext(); ) {
                 Card card = it.next();
                 if (cards.isEmpty()) {
                     ownerId = card.getOwnerId();
@@ -4092,7 +4081,7 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCardToGraveyardWithInfo(Card card, UUID sourceId,
-            Game game, Zone fromZone
+                                               Game game, Zone fromZone
     ) {
         if (card == null) {
             return false;
@@ -4121,8 +4110,8 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCardToLibraryWithInfo(Card card, UUID sourceId,
-            Game game, Zone fromZone,
-            boolean toTop, boolean withName
+                                             Game game, Zone fromZone,
+                                             boolean toTop, boolean withName
     ) {
         if (card == null) {
             return false;
@@ -4187,7 +4176,7 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCardToExileWithInfo(Card card, UUID exileId, String exileName, UUID sourceId,
-            Game game, Zone fromZone, boolean withName) {
+                                           Game game, Zone fromZone, boolean withName) {
         if (card == null) {
             return false;
         }
@@ -4210,7 +4199,7 @@ public abstract class PlayerImpl implements Player, Serializable {
                 game.informPlayers(this.getLogName() + " moves " + (withName ? card.getLogName()
                         + (card.isCopy() ? " (Copy)" : "") : "a card face down") + ' '
                         + (fromZone != null ? "from " + fromZone.toString().toLowerCase(Locale.ENGLISH)
-                                + ' ' : "") + "to the exile zone");
+                        + ' ' : "") + "to the exile zone");
 
             }
             result = true;

From ec4101c1ecd4d78aecd7bfeedafc8dda170cb528 Mon Sep 17 00:00:00 2001
From: dmbrtd <67257492+dmbrtd@users.noreply.github.com>
Date: Mon, 22 Jun 2020 00:34:48 -0700
Subject: [PATCH 452/586] Fix Rise // Fall

Fall was incorrectly discarding land cards.
---
 Mage.Sets/src/mage/cards/r/RiseFall.java | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/Mage.Sets/src/mage/cards/r/RiseFall.java b/Mage.Sets/src/mage/cards/r/RiseFall.java
index e32809e280..f345dce851 100644
--- a/Mage.Sets/src/mage/cards/r/RiseFall.java
+++ b/Mage.Sets/src/mage/cards/r/RiseFall.java
@@ -9,6 +9,7 @@ import mage.constants.Outcome;
 import mage.constants.SpellAbilityType;
 import mage.constants.Zone;
 import mage.filter.FilterCard;
+import mage.filter.StaticFilters;
 import mage.filter.common.FilterCreatureCard;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
@@ -132,7 +133,7 @@ class FallEffect extends OneShotEffect {
             cards.add(card);
         }
         targetPlayer.revealCards(sourceObject.getIdName(), cards, game);
-        targetPlayer.discard(cards, source, game);
+        targetPlayer.discard(new CardsImpl(cards.getCards(StaticFilters.FILTER_CARD_NON_LAND, game)), source, game);
         return true;
     }
 }

From c8ddd62e3be82cce2da9d1c6c819fab3a53017b7 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Mon, 22 Jun 2020 15:38:36 +0200
Subject: [PATCH 453/586] * Reworked some card movement to player methods
 (#4866).

---
 .../src/mage/cards/d/DireFleetDaredevil.java  |  9 ++--
 .../src/mage/cards/d/DistantMemories.java     | 12 +++--
 .../src/mage/cards/t/ToshiroUmezawa.java      | 45 ++++++++++---------
 .../replacement/DiesReplacementEffect.java    |  2 +-
 4 files changed, 39 insertions(+), 29 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/d/DireFleetDaredevil.java b/Mage.Sets/src/mage/cards/d/DireFleetDaredevil.java
index eee9cd1434..664420628b 100644
--- a/Mage.Sets/src/mage/cards/d/DireFleetDaredevil.java
+++ b/Mage.Sets/src/mage/cards/d/DireFleetDaredevil.java
@@ -49,7 +49,9 @@ public final class DireFleetDaredevil extends CardImpl {
         // First strike
         this.addAbility(FirstStrikeAbility.getInstance());
 
-        // When this enters the battlefield, exile target instant or sorcery card from an opponent's graveyard. You may cast that card this turn and you may spend mana as though it were mana of any color. If that card would be put into a graveyard this turn, exile it instead.
+        // When Dire Fleet Daredevil enters the battlefield, exile target instant or sorcery card from an opponent's graveyard. 
+        // You may cast it this turn, and you may spend mana as though it were mana of any type to cast that spell. 
+        // If that spell would be put into a graveyard this turn, exile it instead.
         Ability ability = new EntersBattlefieldTriggeredAbility(new DireFleetDaredevilEffect());
         ability.addTarget(new TargetCardInOpponentsGraveyard(filter));
         this.addAbility(ability);
@@ -167,8 +169,9 @@ class DireFleetDaredevilReplacementEffect extends ReplacementEffectImpl {
     @Override
     public boolean replaceEvent(GameEvent event, Ability source, Game game) {
         Card card = game.getCard(event.getTargetId());
-        if (card != null) {
-            return card.moveToZone(Zone.EXILED, source.getSourceId(), game, false);
+        Player controller = game.getPlayer(source.getControllerId());
+        if (card != null && controller != null) {
+            return controller.moveCards(card, Zone.EXILED, source, game);
         }
         return false;
     }
diff --git a/Mage.Sets/src/mage/cards/d/DistantMemories.java b/Mage.Sets/src/mage/cards/d/DistantMemories.java
index 1869d6ba37..3833a540a9 100644
--- a/Mage.Sets/src/mage/cards/d/DistantMemories.java
+++ b/Mage.Sets/src/mage/cards/d/DistantMemories.java
@@ -65,7 +65,7 @@ class DistantMemoriesEffect extends OneShotEffect {
         if (controller.searchLibrary(target, source, game)) {
             Card card = controller.getLibrary().remove(target.getFirstTarget(), game);
             if (card != null) {
-                card.moveToZone(Zone.EXILED, source.getSourceId(), game, false);
+                controller.moveCards(card, Zone.EXILED, source, game);
                 controller.shuffleLibrary(source, game);
 
                 StringBuilder sb = new StringBuilder();
@@ -76,9 +76,13 @@ class DistantMemoriesEffect extends OneShotEffect {
                 Set<UUID> opponents = game.getOpponents(source.getControllerId());
                 for (UUID opponentUuid : opponents) {
                     Player opponent = game.getPlayer(opponentUuid);
-                    if (opponent != null
-                            && opponent.chooseUse(Outcome.Detriment, sb.toString(), source, game)) {
-                        putInHand = true;
+                    if (opponent != null) {
+                        if (opponent.chooseUse(Outcome.Detriment, sb.toString(), source, game)) {
+                            putInHand = true;
+                            game.informPlayers(opponent.getName() + " decides to put the selected card into the player's hand.");
+                        } else {
+                            game.informPlayers(opponent.getName() + " decides to leave the card in exile.");
+                        }
                     }
                 }
 
diff --git a/Mage.Sets/src/mage/cards/t/ToshiroUmezawa.java b/Mage.Sets/src/mage/cards/t/ToshiroUmezawa.java
index 6b9c5c8582..3d6c22d1a8 100644
--- a/Mage.Sets/src/mage/cards/t/ToshiroUmezawa.java
+++ b/Mage.Sets/src/mage/cards/t/ToshiroUmezawa.java
@@ -5,8 +5,10 @@ import mage.MageInt;
 import mage.MageObjectReference;
 import mage.abilities.Ability;
 import mage.abilities.common.DiesCreatureTriggeredAbility;
+import mage.abilities.effects.ContinuousEffect;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.ReplacementEffectImpl;
+import mage.abilities.effects.common.replacement.PutToGraveyardReplacementEffect;
 import mage.abilities.keyword.BushidoAbility;
 import mage.cards.Card;
 import mage.cards.CardImpl;
@@ -22,28 +24,29 @@ import mage.game.stack.Spell;
 import mage.game.stack.StackObject;
 import mage.players.Player;
 import mage.target.common.TargetCardInYourGraveyard;
+import mage.target.targetpointer.FixedTarget;
 
 /**
  *
  * @author LevelX2
  */
 public final class ToshiroUmezawa extends CardImpl {
-    
+
     private static final FilterCreaturePermanent filter
             = new FilterCreaturePermanent("a creature an opponent controls");
     private static final FilterCard filterInstant = new FilterCard("instant card from your graveyard");
-    
+
     static {
         filter.add(TargetController.OPPONENT.getControllerPredicate());
         filterInstant.add(CardType.INSTANT.getPredicate());
     }
-    
+
     public ToshiroUmezawa(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{B}");
         this.addSuperType(SuperType.LEGENDARY);
         this.subtype.add(SubType.HUMAN);
         this.subtype.add(SubType.SAMURAI);
-        
+
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
 
@@ -55,13 +58,13 @@ public final class ToshiroUmezawa extends CardImpl {
         Ability ability = new DiesCreatureTriggeredAbility(new ToshiroUmezawaEffect(), true, filter);
         ability.addTarget(new TargetCardInYourGraveyard(1, 1, filterInstant));
         this.addAbility(ability);
-        
+
     }
-    
+
     public ToshiroUmezawa(final ToshiroUmezawa card) {
         super(card);
     }
-    
+
     @Override
     public ToshiroUmezawa copy() {
         return new ToshiroUmezawa(this);
@@ -69,22 +72,22 @@ public final class ToshiroUmezawa extends CardImpl {
 }
 
 class ToshiroUmezawaEffect extends OneShotEffect {
-    
+
     public ToshiroUmezawaEffect() {
         super(Outcome.Benefit);
         this.staticText = "cast target instant card from your graveyard. "
                 + "If that card would be put into a graveyard this turn, exile it instead";
     }
-    
+
     public ToshiroUmezawaEffect(final ToshiroUmezawaEffect effect) {
         super(effect);
     }
-    
+
     @Override
     public ToshiroUmezawaEffect copy() {
         return new ToshiroUmezawaEffect(this);
     }
-    
+
     @Override
     public boolean apply(Game game, Ability source) {
         Player controller = game.getPlayer(source.getControllerId());
@@ -104,45 +107,45 @@ class ToshiroUmezawaEffect extends OneShotEffect {
 }
 
 class ToshiroUmezawaReplacementEffect extends ReplacementEffectImpl {
-    
+
     private final UUID cardId;
-    
+
     public ToshiroUmezawaReplacementEffect(UUID cardId) {
         super(Duration.EndOfTurn, Outcome.Exile);
         this.cardId = cardId;
     }
-    
+
     public ToshiroUmezawaReplacementEffect(final ToshiroUmezawaReplacementEffect effect) {
         super(effect);
         this.cardId = effect.cardId;
     }
-    
+
     @Override
     public ToshiroUmezawaReplacementEffect copy() {
         return new ToshiroUmezawaReplacementEffect(this);
     }
-    
+
     @Override
     public boolean replaceEvent(GameEvent event, Ability source, Game game) {
         UUID eventObject = event.getTargetId();
         StackObject stackObject = game.getStack().getStackObject(eventObject);
-        if (stackObject != null) {
+        Player controller = game.getPlayer(source.getControllerId());
+        if (stackObject != null && controller != null) {
             if (stackObject instanceof Spell) {
                 game.rememberLKI(stackObject.getId(), Zone.STACK, stackObject);
             }
             if (stackObject instanceof Card && eventObject.equals(cardId)) {
-                ((Card) stackObject).moveToExile(null, null, source.getSourceId(), game);
-                return true;
+                return controller.moveCards((Card) stackObject, Zone.EXILED, source, game);
             }
         }
         return false;
     }
-    
+
     @Override
     public boolean checksEventType(GameEvent event, Game game) {
         return event.getType() == EventType.ZONE_CHANGE;
     }
-    
+
     @Override
     public boolean applies(GameEvent event, Ability source, Game game) {
         ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
diff --git a/Mage/src/main/java/mage/abilities/effects/common/replacement/DiesReplacementEffect.java b/Mage/src/main/java/mage/abilities/effects/common/replacement/DiesReplacementEffect.java
index 12ece0c7b5..48c2d0a67f 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/replacement/DiesReplacementEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/replacement/DiesReplacementEffect.java
@@ -52,7 +52,7 @@ public class DiesReplacementEffect extends ReplacementEffectImpl {
         Permanent permanent = ((ZoneChangeEvent) event).getTarget();
         Player controller = game.getPlayer(source.getControllerId());
         if (controller != null && permanent != null) {
-            return controller.moveCardToExileWithInfo(permanent, null, "", source.getSourceId(), game, Zone.BATTLEFIELD, true);
+            return controller.moveCards(permanent, Zone.EXILED, source, game);
         }
         return false;
     }

From 3c653d8b19c023312509d9fa042b9c7b9375fee7 Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Mon, 22 Jun 2020 09:40:39 -0500
Subject: [PATCH 454/586] - First part of fix #6660

---
 Mage.Sets/src/mage/cards/c/ChokingVines.java  | 12 +++++-
 .../BecomesBlockedSourceTriggeredAbility.java | 43 +++++++++++++++++++
 ...hantedCreatureBlockedTriggeredAbility.java |  3 +-
 3 files changed, 54 insertions(+), 4 deletions(-)
 create mode 100644 Mage/src/main/java/mage/abilities/common/BecomesBlockedSourceTriggeredAbility.java

diff --git a/Mage.Sets/src/mage/cards/c/ChokingVines.java b/Mage.Sets/src/mage/cards/c/ChokingVines.java
index 91ab935276..24b284da90 100644
--- a/Mage.Sets/src/mage/cards/c/ChokingVines.java
+++ b/Mage.Sets/src/mage/cards/c/ChokingVines.java
@@ -20,6 +20,8 @@ import mage.target.common.TargetCreaturePermanent;
 import mage.target.targetadjustment.TargetAdjuster;
 
 import static mage.filter.StaticFilters.FILTER_ATTACKING_CREATURES;
+import mage.game.events.GameEvent;
+import mage.game.permanent.Permanent;
 
 /**
  * @author arcox
@@ -30,7 +32,8 @@ public final class ChokingVines extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{X}{G}");
 
         // Cast only during the declare blockers step.
-        this.addAbility(new CastOnlyDuringPhaseStepSourceAbility(null, PhaseStep.DECLARE_BLOCKERS, null, "Cast this spell only during the declare blockers step"));
+        this.addAbility(new CastOnlyDuringPhaseStepSourceAbility(null,
+                PhaseStep.DECLARE_BLOCKERS, null, "Cast this spell only during the declare blockers step"));
 
         // X target attacking creatures become blocked. Choking Vines deals 1 damage to each of those creatures.
         this.getSpellAbility().addEffect(new ChokingVinesEffect());
@@ -85,7 +88,12 @@ class ChokingVinesEffect extends OneShotEffect {
                 for (UUID id : target.getTargets()) {
                     CombatGroup combatGroup = game.getCombat().findGroup(id);
                     if (combatGroup != null) {
-                        combatGroup.setBlocked(true, game);
+                        combatGroup.setBlocked(true); // non-banded creatures
+                        combatGroup.setBlocked(true, game); // this only works for banded creatures and needs to be checked out
+                        Permanent attacker = game.getPermanent(id);
+                        if (attacker != null) {
+                            game.fireEvent(GameEvent.getEvent(GameEvent.EventType.CREATURE_BLOCKED, attacker.getId(), null));
+                        }
                     }
                 }
             }
diff --git a/Mage/src/main/java/mage/abilities/common/BecomesBlockedSourceTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesBlockedSourceTriggeredAbility.java
new file mode 100644
index 0000000000..8fc710f1f5
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/common/BecomesBlockedSourceTriggeredAbility.java
@@ -0,0 +1,43 @@
+
+package mage.abilities.common;
+
+import mage.abilities.TriggeredAbilityImpl;
+import mage.abilities.effects.Effect;
+import mage.constants.Zone;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+
+/**
+ *
+ * @author North
+ */
+public class BecomesBlockedSourceTriggeredAbility extends TriggeredAbilityImpl {
+
+    public BecomesBlockedSourceTriggeredAbility(Effect effect, boolean optional) {
+        super(Zone.BATTLEFIELD, effect, optional);
+    }
+
+    public BecomesBlockedSourceTriggeredAbility(final BecomesBlockedSourceTriggeredAbility ability) {
+        super(ability);
+    }
+
+    @Override
+    public boolean checkEventType(GameEvent event, Game game) {
+        return event.getType() == GameEvent.EventType.CREATURE_BLOCKED;
+    }
+
+    @Override
+    public boolean checkTrigger(GameEvent event, Game game) {
+        return event.getTargetId().equals(this.getSourceId());
+    }
+
+    @Override
+    public String getRule() {
+        return "Whenever {this} becomes blocked, " + super.getRule();
+    }
+
+    @Override
+    public BecomesBlockedSourceTriggeredAbility copy() {
+        return new BecomesBlockedSourceTriggeredAbility(this);
+    }
+}
diff --git a/Mage/src/main/java/mage/abilities/common/EnchantedCreatureBlockedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/EnchantedCreatureBlockedTriggeredAbility.java
index 5f907b63a3..a55686155c 100644
--- a/Mage/src/main/java/mage/abilities/common/EnchantedCreatureBlockedTriggeredAbility.java
+++ b/Mage/src/main/java/mage/abilities/common/EnchantedCreatureBlockedTriggeredAbility.java
@@ -1,4 +1,3 @@
-
 package mage.abilities.common;
 
 import mage.abilities.TriggeredAbilityImpl;
@@ -43,7 +42,7 @@ public class EnchantedCreatureBlockedTriggeredAbility extends TriggeredAbilityIm
 
     @Override
     public String getRule() {
-        return "Whenever enchanted creature becomes blocked by a creature, " + super.getRule();
+        return "Whenever enchanted creature becomes blocked, " + super.getRule();
     }
 
     @Override

From 22bbbf656fc4b6a4af5f9cb240f59cba8c017268 Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Mon, 22 Jun 2020 09:41:22 -0500
Subject: [PATCH 455/586] - Second part of fix #6660

---
 Mage.Sets/src/mage/cards/a/AlleyGrifters.java |  4 ++--
 Mage.Sets/src/mage/cards/a/AsajjVentress.java |  4 ++--
 .../src/mage/cards/c/ChamberedNautilus.java   |  4 ++--
 .../src/mage/cards/c/CopyEnchantment.java     | 15 -------------
 .../src/mage/cards/c/CorruptOfficial.java     | 21 ++++++++++---------
 .../src/mage/cards/d/DeeprootWarrior.java     |  4 ++--
 .../src/mage/cards/d/DeepwoodTantiv.java      |  4 ++--
 .../src/mage/cards/d/DeepwoodWolverine.java   |  4 ++--
 Mage.Sets/src/mage/cards/d/Drelnoch.java      |  4 ++--
 Mage.Sets/src/mage/cards/d/Duskworker.java    |  4 ++--
 .../src/mage/cards/d/DwarvenBerserker.java    |  4 ++--
 .../src/mage/cards/e/ElvishBerserker.java     |  4 ++--
 Mage.Sets/src/mage/cards/f/FireJuggler.java   |  4 ++--
 Mage.Sets/src/mage/cards/g/GangOfElk.java     |  4 ++--
 .../src/mage/cards/g/GoblinSwineRider.java    |  4 ++--
 Mage.Sets/src/mage/cards/g/Groffskithur.java  |  4 ++--
 .../src/mage/cards/g/GustcloakCavalier.java   |  4 ++--
 .../src/mage/cards/g/GustcloakHarrier.java    |  4 ++--
 .../src/mage/cards/g/GustcloakRunner.java     |  4 ++--
 .../src/mage/cards/g/GustcloakSentinel.java   |  4 ++--
 .../src/mage/cards/g/GustcloakSkirmisher.java |  4 ++--
 Mage.Sets/src/mage/cards/i/IchorclawMyr.java  |  4 ++--
 .../src/mage/cards/i/IgnobleSoldier.java      |  4 ++--
 Mage.Sets/src/mage/cards/j/JohtullWurm.java   |  4 ++--
 Mage.Sets/src/mage/cards/j/JungleWurm.java    |  4 ++--
 .../src/mage/cards/k/KarplusanWolverine.java  |  4 ++--
 .../src/mage/cards/k/KheruSpellsnatcher.java  |  4 +++-
 .../src/mage/cards/l/LaccolithGrunt.java      |  4 ++--
 .../src/mage/cards/l/LaccolithTitan.java      |  4 ++--
 .../src/mage/cards/l/LaccolithWarrior.java    |  4 ++--
 .../src/mage/cards/l/LaccolithWhelp.java      |  4 ++--
 Mage.Sets/src/mage/cards/l/LeeryFogbeast.java |  4 ++--
 .../src/mage/cards/l/LimDulsPaladin.java      |  4 ++--
 .../src/mage/cards/n/NorwoodWarrior.java      |  4 ++--
 Mage.Sets/src/mage/cards/p/PlagueWight.java   |  4 ++--
 Mage.Sets/src/mage/cards/r/RabidElephant.java |  4 ++--
 Mage.Sets/src/mage/cards/r/RazorclawBear.java |  4 ++--
 Mage.Sets/src/mage/cards/s/SacredPrey.java    |  4 ++--
 Mage.Sets/src/mage/cards/s/SaprazzanHeir.java |  4 ++--
 .../src/mage/cards/s/SaprazzanRaider.java     |  4 ++--
 .../src/mage/cards/s/SilkenfistFighter.java   |  4 ++--
 .../src/mage/cards/s/SilkenfistOrder.java     |  4 ++--
 Mage.Sets/src/mage/cards/s/SlashingTiger.java |  4 ++--
 Mage.Sets/src/mage/cards/s/SlithStrider.java  |  4 ++--
 Mage.Sets/src/mage/cards/s/SnortingGahr.java  |  4 ++--
 Mage.Sets/src/mage/cards/s/SparringGolem.java |  4 ++--
 .../src/mage/cards/s/StormsurgeKraken.java    |  4 ++--
 .../src/mage/cards/t/TrainedCheetah.java      |  4 ++--
 Mage.Sets/src/mage/cards/v/VedalkenGhoul.java |  4 ++--
 .../src/mage/cards/w/WoebringerDemon.java     |  6 ++++--
 50 files changed, 110 insertions(+), 120 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/a/AlleyGrifters.java b/Mage.Sets/src/mage/cards/a/AlleyGrifters.java
index 88483704b9..bb0bbe0f54 100644
--- a/Mage.Sets/src/mage/cards/a/AlleyGrifters.java
+++ b/Mage.Sets/src/mage/cards/a/AlleyGrifters.java
@@ -4,7 +4,7 @@ package mage.cards.a;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.BecomesBlockedByCreatureTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -29,7 +29,7 @@ public final class AlleyGrifters extends CardImpl {
         this.toughness = new MageInt(2);
 
         // Whenever Alley Grifters becomes blocked, defending player discards a card.
-        this.addAbility(new BecomesBlockedByCreatureTriggeredAbility(new AlleyGriftersDiscardEffect(), false));
+        this.addAbility(new BecomesBlockedSourceTriggeredAbility(new AlleyGriftersDiscardEffect(), false));
     }
 
     public AlleyGrifters(final AlleyGrifters card) {
diff --git a/Mage.Sets/src/mage/cards/a/AsajjVentress.java b/Mage.Sets/src/mage/cards/a/AsajjVentress.java
index 3e2aa60f81..4cf2118aab 100644
--- a/Mage.Sets/src/mage/cards/a/AsajjVentress.java
+++ b/Mage.Sets/src/mage/cards/a/AsajjVentress.java
@@ -5,7 +5,7 @@ import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.AttacksTriggeredAbility;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.condition.common.HateCondition;
 import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
 import mage.abilities.dynamicvalue.common.BlockedCreatureCount;
@@ -42,7 +42,7 @@ public final class AsajjVentress extends CardImpl {
         BlockedCreatureCount value = new BlockedCreatureCount();
         Effect effect = new BoostSourceEffect(value, value, Duration.EndOfTurn, true);
         effect.setText("she gets +1/+1 for each creature blocking her until end of turn");
-        this.addAbility(new BecomesBlockedTriggeredAbility(effect, false));
+        this.addAbility(new BecomesBlockedSourceTriggeredAbility(effect, false));
 
         // <i>Hate</i> &mdash; Whenever Asajj Ventress attacks, if an opponent lost life from a source other than combat damage this turn, target creature blocks this turn if able.
         Ability ability = new ConditionalInterveningIfTriggeredAbility(
diff --git a/Mage.Sets/src/mage/cards/c/ChamberedNautilus.java b/Mage.Sets/src/mage/cards/c/ChamberedNautilus.java
index e3b917be6c..a4c20b19e1 100644
--- a/Mage.Sets/src/mage/cards/c/ChamberedNautilus.java
+++ b/Mage.Sets/src/mage/cards/c/ChamberedNautilus.java
@@ -3,7 +3,7 @@ package mage.cards.c;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -24,7 +24,7 @@ public final class ChamberedNautilus extends CardImpl {
         this.toughness = new MageInt(2);
 
         // Whenever Chambered Nautilus becomes blocked, you may draw a card.
-        this.addAbility(new BecomesBlockedTriggeredAbility(new DrawCardSourceControllerEffect(1), true));
+        this.addAbility(new BecomesBlockedSourceTriggeredAbility(new DrawCardSourceControllerEffect(1), true));
     }
 
     public ChamberedNautilus(final ChamberedNautilus card) {
diff --git a/Mage.Sets/src/mage/cards/c/CopyEnchantment.java b/Mage.Sets/src/mage/cards/c/CopyEnchantment.java
index a17d5da526..ed3202936d 100644
--- a/Mage.Sets/src/mage/cards/c/CopyEnchantment.java
+++ b/Mage.Sets/src/mage/cards/c/CopyEnchantment.java
@@ -3,26 +3,12 @@ package mage.cards.c;
 
 import java.util.UUID;
 
-import mage.MageObject;
-import mage.abilities.Ability;
-import mage.abilities.SpellAbility;
 import mage.abilities.common.EntersBattlefieldAbility;
-import mage.abilities.effects.Effect;
-import mage.abilities.effects.common.AttachEffect;
 import mage.abilities.effects.common.CopyPermanentEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.SubType;
-import mage.constants.Outcome;
-import mage.filter.FilterPermanent;
 import mage.filter.StaticFilters;
-import mage.game.Game;
-import mage.game.permanent.Permanent;
-import mage.game.permanent.PermanentCard;
-import mage.players.Player;
-import mage.target.Target;
-import mage.util.functions.EmptyApplyToPermanent;
 
 /**
  *
@@ -34,7 +20,6 @@ public final class CopyEnchantment extends CardImpl {
         super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{U}");
 
         // You may have Copy Enchantment enter the battlefield as a copy of any enchantment on the battlefield.
-        //this.addAbility(new EntersBattlefieldAbility(new CopyEnchantmentEffect(new FilterEnchantmentPermanent("any enchantment")), true));
         this.addAbility(new EntersBattlefieldAbility(new CopyPermanentEffect(StaticFilters.FILTER_ENCHANTMENT_PERMANENT), true));
     }
 
diff --git a/Mage.Sets/src/mage/cards/c/CorruptOfficial.java b/Mage.Sets/src/mage/cards/c/CorruptOfficial.java
index 4c8aed08ec..c5cdd88794 100644
--- a/Mage.Sets/src/mage/cards/c/CorruptOfficial.java
+++ b/Mage.Sets/src/mage/cards/c/CorruptOfficial.java
@@ -1,10 +1,9 @@
-
 package mage.cards.c;
 
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.BecomesBlockedByCreatureTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.OneShotEffect;
@@ -16,6 +15,7 @@ import mage.constants.SubType;
 import mage.constants.Outcome;
 import mage.constants.Zone;
 import mage.game.Game;
+import mage.game.combat.Combat;
 import mage.game.permanent.Permanent;
 import mage.players.Player;
 
@@ -26,7 +26,7 @@ import mage.players.Player;
 public final class CorruptOfficial extends CardImpl {
 
     public CorruptOfficial(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{B}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}");
         this.subtype.add(SubType.HUMAN);
         this.subtype.add(SubType.MINION);
         this.power = new MageInt(3);
@@ -34,9 +34,9 @@ public final class CorruptOfficial extends CardImpl {
 
         // {2}{B}: Regenerate Corrupt Official.
         this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateSourceEffect(), new ManaCostsImpl("{2}{B}")));
-        
+
         // Whenever Corrupt Official becomes blocked, defending player discards a card at random.
-        this.addAbility(new BecomesBlockedByCreatureTriggeredAbility(new CorruptOfficialDiscardEffect(), false));
+        this.addAbility(new BecomesBlockedSourceTriggeredAbility(new CorruptOfficialDiscardEffect(), false));
     }
 
     public CorruptOfficial(final CorruptOfficial card) {
@@ -67,11 +67,12 @@ class CorruptOfficialDiscardEffect extends OneShotEffect {
 
     @Override
     public boolean apply(Game game, Ability source) {
-        Permanent blockingCreature = game.getPermanent(getTargetPointer().getFirst(game, source));
-        if (blockingCreature != null) {
-            Player opponent = game.getPlayer(blockingCreature.getControllerId());
-            if (opponent != null) {
-                opponent.discard(1, true, source, game);
+        Permanent corruptOfficial = game.getPermanent(source.getSourceId());
+        if (corruptOfficial != null) {
+            Combat combat = game.getCombat();
+            Player defendingPlayer = game.getPlayer(combat.getDefendingPlayerId(corruptOfficial.getId(), game));
+            if (defendingPlayer != null) {
+                defendingPlayer.discard(1, true, source, game);
                 return true;
             }
         }
diff --git a/Mage.Sets/src/mage/cards/d/DeeprootWarrior.java b/Mage.Sets/src/mage/cards/d/DeeprootWarrior.java
index 42fea81ea0..e55b432804 100644
--- a/Mage.Sets/src/mage/cards/d/DeeprootWarrior.java
+++ b/Mage.Sets/src/mage/cards/d/DeeprootWarrior.java
@@ -3,7 +3,7 @@ package mage.cards.d;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.continuous.BoostSourceEffect;
 import mage.cards.CardImpl;
@@ -29,7 +29,7 @@ public final class DeeprootWarrior extends CardImpl {
         // Whenever Deeproot Warrior becomes blocked, it gets +1/+1 until end of turn.
         Effect effect = new BoostSourceEffect(1, 1, Duration.EndOfTurn);
         effect.setText("it gets +1/+1 until end of turn");
-        this.addAbility(new BecomesBlockedTriggeredAbility(effect, false));
+        this.addAbility(new BecomesBlockedSourceTriggeredAbility(effect, false));
     }
 
     public DeeprootWarrior(final DeeprootWarrior card) {
diff --git a/Mage.Sets/src/mage/cards/d/DeepwoodTantiv.java b/Mage.Sets/src/mage/cards/d/DeepwoodTantiv.java
index b8f265102f..a1e0b9d32a 100644
--- a/Mage.Sets/src/mage/cards/d/DeepwoodTantiv.java
+++ b/Mage.Sets/src/mage/cards/d/DeepwoodTantiv.java
@@ -3,7 +3,7 @@ package mage.cards.d;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.common.GainLifeEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -23,7 +23,7 @@ public final class DeepwoodTantiv extends CardImpl {
         this.toughness = new MageInt(4);
 
         // Whenever Deepwood Tantiv becomes blocked, you gain 2 life.
-        this.addAbility(new BecomesBlockedTriggeredAbility(new GainLifeEffect(2), false));
+        this.addAbility(new BecomesBlockedSourceTriggeredAbility(new GainLifeEffect(2), false));
     }
 
     public DeepwoodTantiv(final DeepwoodTantiv card) {
diff --git a/Mage.Sets/src/mage/cards/d/DeepwoodWolverine.java b/Mage.Sets/src/mage/cards/d/DeepwoodWolverine.java
index f7e364dcff..7ed887cf9d 100644
--- a/Mage.Sets/src/mage/cards/d/DeepwoodWolverine.java
+++ b/Mage.Sets/src/mage/cards/d/DeepwoodWolverine.java
@@ -3,7 +3,7 @@ package mage.cards.d;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.continuous.BoostSourceEffect;
 import mage.cards.CardImpl;
@@ -27,7 +27,7 @@ public final class DeepwoodWolverine extends CardImpl {
         // Whenever Deepwood Wolverine becomes blocked, it gets +2/+0 until end of turn.
         Effect effect = new BoostSourceEffect(2, 0, Duration.EndOfTurn);
         effect.setText("it gets +2/+0 until end of turn");
-        this.addAbility(new BecomesBlockedTriggeredAbility(effect, false));
+        this.addAbility(new BecomesBlockedSourceTriggeredAbility(effect, false));
     }
 
     public DeepwoodWolverine(final DeepwoodWolverine card) {
diff --git a/Mage.Sets/src/mage/cards/d/Drelnoch.java b/Mage.Sets/src/mage/cards/d/Drelnoch.java
index 21b7ae3e2d..c82c191d86 100644
--- a/Mage.Sets/src/mage/cards/d/Drelnoch.java
+++ b/Mage.Sets/src/mage/cards/d/Drelnoch.java
@@ -3,7 +3,7 @@ package mage.cards.d;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -24,7 +24,7 @@ public final class Drelnoch extends CardImpl {
         this.toughness = new MageInt(3);
 
         // Whenever Drelnoch becomes blocked, you may draw two cards.
-        this.addAbility(new BecomesBlockedTriggeredAbility(new DrawCardSourceControllerEffect(2), true));
+        this.addAbility(new BecomesBlockedSourceTriggeredAbility(new DrawCardSourceControllerEffect(2), true));
     }
 
     public Drelnoch(final Drelnoch card) {
diff --git a/Mage.Sets/src/mage/cards/d/Duskworker.java b/Mage.Sets/src/mage/cards/d/Duskworker.java
index 4dbd6d6713..dade86eb09 100644
--- a/Mage.Sets/src/mage/cards/d/Duskworker.java
+++ b/Mage.Sets/src/mage/cards/d/Duskworker.java
@@ -3,7 +3,7 @@ package mage.cards.d;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.mana.GenericManaCost;
 import mage.abilities.effects.common.RegenerateSourceEffect;
@@ -26,7 +26,7 @@ public final class Duskworker extends CardImpl {
         this.subtype.add(SubType.CONSTRUCT);
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
-        this.addAbility(new BecomesBlockedTriggeredAbility(new RegenerateSourceEffect(), false));
+        this.addAbility(new BecomesBlockedSourceTriggeredAbility(new RegenerateSourceEffect(), false));
         this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, 0, Duration.EndOfTurn), new GenericManaCost(3)));
     }
 
diff --git a/Mage.Sets/src/mage/cards/d/DwarvenBerserker.java b/Mage.Sets/src/mage/cards/d/DwarvenBerserker.java
index 998ff1a509..21fe183f8c 100644
--- a/Mage.Sets/src/mage/cards/d/DwarvenBerserker.java
+++ b/Mage.Sets/src/mage/cards/d/DwarvenBerserker.java
@@ -4,7 +4,7 @@ package mage.cards.d;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.continuous.BoostSourceEffect;
 import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
@@ -31,7 +31,7 @@ public final class DwarvenBerserker extends CardImpl {
         // Whenever Dwarven Berserker becomes blocked, it gets +3/+0 and gains trample until end of turn.
         Effect effect = new BoostSourceEffect(3, 0, Duration.EndOfTurn);
         effect.setText("it gets +3/+0");
-        Ability ability = new BecomesBlockedTriggeredAbility(effect, false);
+        Ability ability = new BecomesBlockedSourceTriggeredAbility(effect, false);
         effect = new GainAbilitySourceEffect(TrampleAbility.getInstance(), Duration.EndOfTurn);
         effect.setText("and gains trample until end of turn");
         ability.addEffect(effect);
diff --git a/Mage.Sets/src/mage/cards/e/ElvishBerserker.java b/Mage.Sets/src/mage/cards/e/ElvishBerserker.java
index 48aa62ef48..a85d7e9e76 100644
--- a/Mage.Sets/src/mage/cards/e/ElvishBerserker.java
+++ b/Mage.Sets/src/mage/cards/e/ElvishBerserker.java
@@ -3,7 +3,7 @@ package mage.cards.e;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.dynamicvalue.common.BlockedCreatureCount;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.continuous.BoostSourceEffect;
@@ -30,7 +30,7 @@ public final class ElvishBerserker extends CardImpl {
         BlockedCreatureCount value = new BlockedCreatureCount();
         Effect effect = new BoostSourceEffect(value, value, Duration.EndOfTurn, true);
         effect.setText("it gets +1/+1 until end of turn for each creature blocking it");
-        this.addAbility(new BecomesBlockedTriggeredAbility(effect, false));
+        this.addAbility(new BecomesBlockedSourceTriggeredAbility(effect, false));
     }
 
     public ElvishBerserker(final ElvishBerserker card) {
diff --git a/Mage.Sets/src/mage/cards/f/FireJuggler.java b/Mage.Sets/src/mage/cards/f/FireJuggler.java
index 87cd678002..d2ede0bfae 100644
--- a/Mage.Sets/src/mage/cards/f/FireJuggler.java
+++ b/Mage.Sets/src/mage/cards/f/FireJuggler.java
@@ -3,7 +3,7 @@ package mage.cards.f;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.common.DamageAllEffect;
 import mage.abilities.effects.common.DoIfClashWonEffect;
 import mage.cards.CardImpl;
@@ -30,7 +30,7 @@ public final class FireJuggler extends CardImpl {
         // Whenever Fire Juggler becomes blocked, clash with an opponent. If you win, Fire Juggler deals 4 damage to each creature blocking it.
         FilterPermanent filter = new FilterPermanent("each creature blocking it");
         filter.add(new BlockingAttackerIdPredicate(this.getId()));
-        this.addAbility(new BecomesBlockedTriggeredAbility(new DoIfClashWonEffect(new DamageAllEffect(4,filter)),false));
+        this.addAbility(new BecomesBlockedSourceTriggeredAbility(new DoIfClashWonEffect(new DamageAllEffect(4,filter)),false));
     }
 
     public FireJuggler(final FireJuggler card) {
diff --git a/Mage.Sets/src/mage/cards/g/GangOfElk.java b/Mage.Sets/src/mage/cards/g/GangOfElk.java
index 3a9ce2c366..5372dfaedc 100644
--- a/Mage.Sets/src/mage/cards/g/GangOfElk.java
+++ b/Mage.Sets/src/mage/cards/g/GangOfElk.java
@@ -3,7 +3,7 @@ package mage.cards.g;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.dynamicvalue.DynamicValue;
 import mage.abilities.dynamicvalue.MultipliedValue;
 import mage.abilities.dynamicvalue.common.BlockedCreatureCount;
@@ -33,7 +33,7 @@ public final class GangOfElk extends CardImpl {
         DynamicValue value = new MultipliedValue(new BlockedCreatureCount(), 2);
         Effect effect = new BoostSourceEffect(value, value, Duration.EndOfTurn, true);
         effect.setText("it gets +2/+2 until end of turn for each creature blocking it");
-        this.addAbility(new BecomesBlockedTriggeredAbility(effect, false));
+        this.addAbility(new BecomesBlockedSourceTriggeredAbility(effect, false));
     }
 
     public GangOfElk(final GangOfElk card) {
diff --git a/Mage.Sets/src/mage/cards/g/GoblinSwineRider.java b/Mage.Sets/src/mage/cards/g/GoblinSwineRider.java
index 44cd1c855b..97d586821b 100644
--- a/Mage.Sets/src/mage/cards/g/GoblinSwineRider.java
+++ b/Mage.Sets/src/mage/cards/g/GoblinSwineRider.java
@@ -3,7 +3,7 @@ package mage.cards.g;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.common.DamageAllEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -24,7 +24,7 @@ public final class GoblinSwineRider extends CardImpl {
         this.toughness = new MageInt(1);
 
         // Whenever Goblin Swine-Rider becomes blocked, it deals 2 damage to each attacking creature and each blocking creature.
-        this.addAbility(new BecomesBlockedTriggeredAbility(new DamageAllEffect(2, "it", new FilterAttackingOrBlockingCreature("attacking creature and each blocking creature")), false));
+        this.addAbility(new BecomesBlockedSourceTriggeredAbility(new DamageAllEffect(2, "it", new FilterAttackingOrBlockingCreature("attacking creature and each blocking creature")), false));
     }
 
     public GoblinSwineRider(final GoblinSwineRider card) {
diff --git a/Mage.Sets/src/mage/cards/g/Groffskithur.java b/Mage.Sets/src/mage/cards/g/Groffskithur.java
index 3a5bd1e998..e11f64f130 100644
--- a/Mage.Sets/src/mage/cards/g/Groffskithur.java
+++ b/Mage.Sets/src/mage/cards/g/Groffskithur.java
@@ -4,7 +4,7 @@ package mage.cards.g;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.common.ReturnToHandTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -33,7 +33,7 @@ public final class Groffskithur extends CardImpl {
         this.toughness = new MageInt(3);
 
         // Whenever Groffskithur becomes blocked, you may return target card named Groffskithur from your graveyard to your hand.
-        Ability ability = new BecomesBlockedTriggeredAbility(new ReturnToHandTargetEffect(), true);
+        Ability ability = new BecomesBlockedSourceTriggeredAbility(new ReturnToHandTargetEffect(), true);
         ability.addTarget(new TargetCardInYourGraveyard(filter));
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/g/GustcloakCavalier.java b/Mage.Sets/src/mage/cards/g/GustcloakCavalier.java
index b8049a75f3..335994260c 100644
--- a/Mage.Sets/src/mage/cards/g/GustcloakCavalier.java
+++ b/Mage.Sets/src/mage/cards/g/GustcloakCavalier.java
@@ -5,7 +5,7 @@ import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.AttacksTriggeredAbility;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.RemoveFromCombatSourceEffect;
 import mage.abilities.effects.common.TapTargetEffect;
@@ -39,7 +39,7 @@ public final class GustcloakCavalier extends CardImpl {
         this.addAbility(ability);
         
         // Whenever Gustcloak Cavalier becomes blocked, you may untap Gustcloak Cavalier and remove it from combat.
-        Ability ability2 = new BecomesBlockedTriggeredAbility(new UntapSourceEffect(), true);
+        Ability ability2 = new BecomesBlockedSourceTriggeredAbility(new UntapSourceEffect(), true);
         Effect effect = new RemoveFromCombatSourceEffect();
         effect.setText("and remove it from combat");
         ability2.addEffect(effect);
diff --git a/Mage.Sets/src/mage/cards/g/GustcloakHarrier.java b/Mage.Sets/src/mage/cards/g/GustcloakHarrier.java
index fbc6a66b81..51c59a0704 100644
--- a/Mage.Sets/src/mage/cards/g/GustcloakHarrier.java
+++ b/Mage.Sets/src/mage/cards/g/GustcloakHarrier.java
@@ -4,7 +4,7 @@ package mage.cards.g;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.RemoveFromCombatSourceEffect;
 import mage.abilities.effects.common.UntapSourceEffect;
@@ -31,7 +31,7 @@ public final class GustcloakHarrier extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
         
         // Whenever Gustcloak Harrier becomes blocked, you may untap it and remove it from combat.
-        Ability ability = new BecomesBlockedTriggeredAbility(new UntapSourceEffect(), true);
+        Ability ability = new BecomesBlockedSourceTriggeredAbility(new UntapSourceEffect(), true);
         Effect effect = new RemoveFromCombatSourceEffect();
         effect.setText("and remove it from combat");
         ability.addEffect(effect);
diff --git a/Mage.Sets/src/mage/cards/g/GustcloakRunner.java b/Mage.Sets/src/mage/cards/g/GustcloakRunner.java
index d7364a2336..924a806fbf 100644
--- a/Mage.Sets/src/mage/cards/g/GustcloakRunner.java
+++ b/Mage.Sets/src/mage/cards/g/GustcloakRunner.java
@@ -4,7 +4,7 @@ package mage.cards.g;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.RemoveFromCombatSourceEffect;
 import mage.abilities.effects.common.UntapSourceEffect;
@@ -27,7 +27,7 @@ public final class GustcloakRunner extends CardImpl {
         this.toughness = new MageInt(1);
 
         // Whenever Gustcloak Runner becomes blocked, you may untap it and remove it from combat.
-        Ability ability = new BecomesBlockedTriggeredAbility(new UntapSourceEffect(), true);
+        Ability ability = new BecomesBlockedSourceTriggeredAbility(new UntapSourceEffect(), true);
         Effect effect = new RemoveFromCombatSourceEffect();
         effect.setText("and remove it from combat");
         ability.addEffect(effect);
diff --git a/Mage.Sets/src/mage/cards/g/GustcloakSentinel.java b/Mage.Sets/src/mage/cards/g/GustcloakSentinel.java
index 1b7a699fac..276ce6da8c 100644
--- a/Mage.Sets/src/mage/cards/g/GustcloakSentinel.java
+++ b/Mage.Sets/src/mage/cards/g/GustcloakSentinel.java
@@ -4,7 +4,7 @@ package mage.cards.g;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.RemoveFromCombatSourceEffect;
 import mage.abilities.effects.common.UntapSourceEffect;
@@ -28,7 +28,7 @@ public final class GustcloakSentinel extends CardImpl {
         this.toughness = new MageInt(3);
 
         // Whenever Gustcloak Sentinel becomes blocked, you may untap it and remove it from combat.
-        Ability ability = new BecomesBlockedTriggeredAbility(new UntapSourceEffect(), true);
+        Ability ability = new BecomesBlockedSourceTriggeredAbility(new UntapSourceEffect(), true);
         Effect effect = new RemoveFromCombatSourceEffect();
         effect.setText("and remove it from combat");
         ability.addEffect(effect);
diff --git a/Mage.Sets/src/mage/cards/g/GustcloakSkirmisher.java b/Mage.Sets/src/mage/cards/g/GustcloakSkirmisher.java
index 5520f5979a..732e2cd3e0 100644
--- a/Mage.Sets/src/mage/cards/g/GustcloakSkirmisher.java
+++ b/Mage.Sets/src/mage/cards/g/GustcloakSkirmisher.java
@@ -4,7 +4,7 @@ package mage.cards.g;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.RemoveFromCombatSourceEffect;
 import mage.abilities.effects.common.UntapSourceEffect;
@@ -31,7 +31,7 @@ public final class GustcloakSkirmisher extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
         
         // Whenever Gustcloak Skirmisher becomes blocked, you may untap it and remove it from combat.
-        Ability ability = new BecomesBlockedTriggeredAbility(new UntapSourceEffect(), true);
+        Ability ability = new BecomesBlockedSourceTriggeredAbility(new UntapSourceEffect(), true);
         Effect effect = new RemoveFromCombatSourceEffect();
         effect.setText("and remove it from combat");
         ability.addEffect(effect);
diff --git a/Mage.Sets/src/mage/cards/i/IchorclawMyr.java b/Mage.Sets/src/mage/cards/i/IchorclawMyr.java
index 51e0308bf3..388a5e989e 100644
--- a/Mage.Sets/src/mage/cards/i/IchorclawMyr.java
+++ b/Mage.Sets/src/mage/cards/i/IchorclawMyr.java
@@ -3,7 +3,7 @@ package mage.cards.i;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.common.continuous.BoostSourceEffect;
 import mage.abilities.keyword.InfectAbility;
 import mage.cards.CardImpl;
@@ -27,7 +27,7 @@ public final class IchorclawMyr extends CardImpl {
 
         this.addAbility(InfectAbility.getInstance());
         // Whenever Ichorclaw Myr becomes blocked, it gets +2/+2 until end of turn.
-        this.addAbility(new BecomesBlockedTriggeredAbility(new BoostSourceEffect(2, 2, Duration.EndOfTurn), false));
+        this.addAbility(new BecomesBlockedSourceTriggeredAbility(new BoostSourceEffect(2, 2, Duration.EndOfTurn), false));
     }
 
     public IchorclawMyr(final IchorclawMyr card) {
diff --git a/Mage.Sets/src/mage/cards/i/IgnobleSoldier.java b/Mage.Sets/src/mage/cards/i/IgnobleSoldier.java
index 26e90533fb..859bc1abaf 100644
--- a/Mage.Sets/src/mage/cards/i/IgnobleSoldier.java
+++ b/Mage.Sets/src/mage/cards/i/IgnobleSoldier.java
@@ -3,7 +3,7 @@ package mage.cards.i;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.PreventCombatDamageBySourceEffect;
 import mage.cards.CardImpl;
@@ -28,7 +28,7 @@ public final class IgnobleSoldier extends CardImpl {
         // Whenever Ignoble Soldier becomes blocked, prevent all combat damage that would be dealt by it this turn.
         Effect effect = new PreventCombatDamageBySourceEffect(Duration.EndOfTurn);
         effect.setText("prevent all combat damage that would be dealt by it this turn");
-        this.addAbility(new BecomesBlockedTriggeredAbility(effect, false));
+        this.addAbility(new BecomesBlockedSourceTriggeredAbility(effect, false));
     }
 
     public IgnobleSoldier(final IgnobleSoldier card) {
diff --git a/Mage.Sets/src/mage/cards/j/JohtullWurm.java b/Mage.Sets/src/mage/cards/j/JohtullWurm.java
index 5b0bb8b2f0..42c171fcce 100644
--- a/Mage.Sets/src/mage/cards/j/JohtullWurm.java
+++ b/Mage.Sets/src/mage/cards/j/JohtullWurm.java
@@ -3,7 +3,7 @@ package mage.cards.j;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.dynamicvalue.DynamicValue;
 import mage.abilities.dynamicvalue.MultipliedValue;
 import mage.abilities.dynamicvalue.common.BlockedCreatureCount;
@@ -31,7 +31,7 @@ public final class JohtullWurm extends CardImpl {
         DynamicValue blockedCreatureCount = new BlockedCreatureCount("each creature blocking it beyond the first", true);
         Effect effect = new BoostSourceEffect(new MultipliedValue(blockedCreatureCount, -2), new MultipliedValue(blockedCreatureCount, -1), Duration.EndOfTurn, true);
         effect.setText("it gets -2/-1 until end of turn for each creature blocking it beyond the first");
-        this.addAbility(new BecomesBlockedTriggeredAbility(effect, false));
+        this.addAbility(new BecomesBlockedSourceTriggeredAbility(effect, false));
     }
 
     public JohtullWurm(final JohtullWurm card) {
diff --git a/Mage.Sets/src/mage/cards/j/JungleWurm.java b/Mage.Sets/src/mage/cards/j/JungleWurm.java
index abb6b248d6..5d885826e7 100644
--- a/Mage.Sets/src/mage/cards/j/JungleWurm.java
+++ b/Mage.Sets/src/mage/cards/j/JungleWurm.java
@@ -3,7 +3,7 @@ package mage.cards.j;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.dynamicvalue.DynamicValue;
 import mage.abilities.dynamicvalue.MultipliedValue;
 import mage.abilities.dynamicvalue.common.BlockedCreatureCount;
@@ -32,7 +32,7 @@ public final class JungleWurm extends CardImpl {
         DynamicValue value = new MultipliedValue(blockedCreatureCount, -1);
         Effect effect = new BoostSourceEffect(value, value, Duration.EndOfTurn, true);
         effect.setText("it gets -1/-1 until end of turn for each creature blocking it beyond the first");
-        this.addAbility(new BecomesBlockedTriggeredAbility(effect, false));
+        this.addAbility(new BecomesBlockedSourceTriggeredAbility(effect, false));
     }
 
     public JungleWurm(final JungleWurm card) {
diff --git a/Mage.Sets/src/mage/cards/k/KarplusanWolverine.java b/Mage.Sets/src/mage/cards/k/KarplusanWolverine.java
index daa070e71a..d6e8cec8d3 100644
--- a/Mage.Sets/src/mage/cards/k/KarplusanWolverine.java
+++ b/Mage.Sets/src/mage/cards/k/KarplusanWolverine.java
@@ -4,7 +4,7 @@ package mage.cards.k;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.common.DamageTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -28,7 +28,7 @@ public final class KarplusanWolverine extends CardImpl {
         this.toughness = new MageInt(1);
 
         // Whenever Karplusan Wolverine becomes blocked, you may have it deal 1 damage to any target.
-        Ability ability = new BecomesBlockedTriggeredAbility(new DamageTargetEffect(1), true);
+        Ability ability = new BecomesBlockedSourceTriggeredAbility(new DamageTargetEffect(1), true);
         ability.addTarget(new TargetAnyTarget());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/k/KheruSpellsnatcher.java b/Mage.Sets/src/mage/cards/k/KheruSpellsnatcher.java
index da5b92c4fc..7a53d2f08e 100644
--- a/Mage.Sets/src/mage/cards/k/KheruSpellsnatcher.java
+++ b/Mage.Sets/src/mage/cards/k/KheruSpellsnatcher.java
@@ -54,7 +54,9 @@ class KheruSpellsnatcherEffect extends OneShotEffect {
 
     KheruSpellsnatcherEffect() {
         super(Outcome.Benefit);
-        this.staticText = "counter target spell. If that spell is countered this way, exile it instead of putting it into its owner's graveyard. You may cast that card without paying its mana cost as long as it remains exiled";
+        this.staticText = "counter target spell. If that spell is countered this way, "
+                + "exile it instead of putting it into its owner's graveyard. "
+                + "You may cast that card without paying its mana cost as long as it remains exiled";
     }
 
     KheruSpellsnatcherEffect(final KheruSpellsnatcherEffect effect) {
diff --git a/Mage.Sets/src/mage/cards/l/LaccolithGrunt.java b/Mage.Sets/src/mage/cards/l/LaccolithGrunt.java
index 6c0570f65a..98c271a2e8 100644
--- a/Mage.Sets/src/mage/cards/l/LaccolithGrunt.java
+++ b/Mage.Sets/src/mage/cards/l/LaccolithGrunt.java
@@ -4,7 +4,7 @@ package mage.cards.l;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.continuous.AssignNoCombatDamageSourceEffect;
 import mage.cards.CardImpl;
@@ -32,7 +32,7 @@ public final class LaccolithGrunt extends CardImpl {
         this.toughness = new MageInt(2);
 
         // Whenever Laccolith Grunt becomes blocked, you may have it deal damage equal to its power to target creature. If you do, Laccolith Grunt assigns no combat damage this turn.
-        Ability ability = new BecomesBlockedTriggeredAbility(new LaccolithEffect().setText("you may have it deal damage equal to its power to target creature"), true);
+        Ability ability = new BecomesBlockedSourceTriggeredAbility(new LaccolithEffect().setText("you may have it deal damage equal to its power to target creature"), true);
         ability.addEffect(new AssignNoCombatDamageSourceEffect(Duration.EndOfTurn, true));
         ability.addTarget(new TargetCreaturePermanent());
         this.addAbility(ability);
diff --git a/Mage.Sets/src/mage/cards/l/LaccolithTitan.java b/Mage.Sets/src/mage/cards/l/LaccolithTitan.java
index 2af5b5a8e3..886255e887 100644
--- a/Mage.Sets/src/mage/cards/l/LaccolithTitan.java
+++ b/Mage.Sets/src/mage/cards/l/LaccolithTitan.java
@@ -4,7 +4,7 @@ package mage.cards.l;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.continuous.AssignNoCombatDamageSourceEffect;
 import mage.cards.CardImpl;
@@ -32,7 +32,7 @@ public final class LaccolithTitan extends CardImpl {
         this.toughness = new MageInt(6);
 
         // Whenever Laccolith Grunt becomes blocked, you may have it deal damage equal to its power to target creature. If you do, Laccolith Grunt assigns no combat damage this turn.
-        Ability ability = new BecomesBlockedTriggeredAbility(new LaccolithEffect().setText("you may have it deal damage equal to its power to target creature"), true);
+        Ability ability = new BecomesBlockedSourceTriggeredAbility(new LaccolithEffect().setText("you may have it deal damage equal to its power to target creature"), true);
         ability.addEffect(new AssignNoCombatDamageSourceEffect(Duration.EndOfTurn, true));
         ability.addTarget(new TargetCreaturePermanent());
         this.addAbility(ability);
diff --git a/Mage.Sets/src/mage/cards/l/LaccolithWarrior.java b/Mage.Sets/src/mage/cards/l/LaccolithWarrior.java
index 435b53cb71..cb65c938df 100644
--- a/Mage.Sets/src/mage/cards/l/LaccolithWarrior.java
+++ b/Mage.Sets/src/mage/cards/l/LaccolithWarrior.java
@@ -4,7 +4,7 @@ package mage.cards.l;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.continuous.AssignNoCombatDamageSourceEffect;
 import mage.cards.CardImpl;
@@ -33,7 +33,7 @@ public final class LaccolithWarrior extends CardImpl {
         this.toughness = new MageInt(3);
 
         // Whenever Laccolith Grunt becomes blocked, you may have it deal damage equal to its power to target creature. If you do, Laccolith Grunt assigns no combat damage this turn.
-        Ability ability = new BecomesBlockedTriggeredAbility(new LaccolithEffect().setText("you may have it deal damage equal to its power to target creature"), true);
+        Ability ability = new BecomesBlockedSourceTriggeredAbility(new LaccolithEffect().setText("you may have it deal damage equal to its power to target creature"), true);
         ability.addEffect(new AssignNoCombatDamageSourceEffect(Duration.EndOfTurn, true));
         ability.addTarget(new TargetCreaturePermanent());
         this.addAbility(ability);
diff --git a/Mage.Sets/src/mage/cards/l/LaccolithWhelp.java b/Mage.Sets/src/mage/cards/l/LaccolithWhelp.java
index 19472a6399..fe6d96c22a 100644
--- a/Mage.Sets/src/mage/cards/l/LaccolithWhelp.java
+++ b/Mage.Sets/src/mage/cards/l/LaccolithWhelp.java
@@ -4,7 +4,7 @@ package mage.cards.l;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.continuous.AssignNoCombatDamageSourceEffect;
 import mage.cards.CardImpl;
@@ -32,7 +32,7 @@ public final class LaccolithWhelp extends CardImpl {
         this.toughness = new MageInt(1);
 
         // Whenever Laccolith Grunt becomes blocked, you may have it deal damage equal to its power to target creature. If you do, Laccolith Grunt assigns no combat damage this turn.
-        Ability ability = new BecomesBlockedTriggeredAbility(new LaccolithEffect().setText("you may have it deal damage equal to its power to target creature"), true);
+        Ability ability = new BecomesBlockedSourceTriggeredAbility(new LaccolithEffect().setText("you may have it deal damage equal to its power to target creature"), true);
         ability.addEffect(new AssignNoCombatDamageSourceEffect(Duration.EndOfTurn, true));
         ability.addTarget(new TargetCreaturePermanent());
         this.addAbility(ability);
diff --git a/Mage.Sets/src/mage/cards/l/LeeryFogbeast.java b/Mage.Sets/src/mage/cards/l/LeeryFogbeast.java
index 6e9ac41b3a..9cef5b0184 100644
--- a/Mage.Sets/src/mage/cards/l/LeeryFogbeast.java
+++ b/Mage.Sets/src/mage/cards/l/LeeryFogbeast.java
@@ -3,7 +3,7 @@ package mage.cards.l;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.common.PreventAllDamageByAllPermanentsEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -25,7 +25,7 @@ public final class LeeryFogbeast extends CardImpl {
         this.toughness = new MageInt(2);
 
         // Whenever Leery Fogbeast becomes blocked, prevent all combat damage that would be dealt this turn.
-        this.addAbility(new BecomesBlockedTriggeredAbility(new PreventAllDamageByAllPermanentsEffect(Duration.EndOfTurn, true), false));
+        this.addAbility(new BecomesBlockedSourceTriggeredAbility(new PreventAllDamageByAllPermanentsEffect(Duration.EndOfTurn, true), false));
     }
 
     public LeeryFogbeast(final LeeryFogbeast card) {
diff --git a/Mage.Sets/src/mage/cards/l/LimDulsPaladin.java b/Mage.Sets/src/mage/cards/l/LimDulsPaladin.java
index 27a88b67f1..d2187c28db 100644
--- a/Mage.Sets/src/mage/cards/l/LimDulsPaladin.java
+++ b/Mage.Sets/src/mage/cards/l/LimDulsPaladin.java
@@ -5,7 +5,7 @@ import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.AttacksAndIsNotBlockedTriggeredAbility;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
 import mage.abilities.costs.common.DiscardTargetCost;
 import mage.abilities.effects.Effect;
@@ -44,7 +44,7 @@ public final class LimDulsPaladin extends CardImpl {
         // At the beginning of your upkeep, you may discard a card. If you don't, sacrifice Lim-Dul's Paladin and draw a card.
         this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new LimDulsPaladinEffect(), TargetController.YOU, false));
         // Whenever Lim-Dul's Paladin becomes blocked, it gets +6/+3 until end of turn.
-        this.addAbility(new BecomesBlockedTriggeredAbility(new BoostSourceEffect(6, 3, Duration.EndOfTurn), false));
+        this.addAbility(new BecomesBlockedSourceTriggeredAbility(new BoostSourceEffect(6, 3, Duration.EndOfTurn), false));
         // Whenever Lim-Dul's Paladin attacks and isn't blocked, it assigns no combat damage to defending player this turn and that player loses 4 life.
         Effect effect = new AssignNoCombatDamageSourceEffect(Duration.EndOfTurn);
         effect.setText("it assigns no combat damage this turn");
diff --git a/Mage.Sets/src/mage/cards/n/NorwoodWarrior.java b/Mage.Sets/src/mage/cards/n/NorwoodWarrior.java
index 5dcf344868..d45ecb8603 100644
--- a/Mage.Sets/src/mage/cards/n/NorwoodWarrior.java
+++ b/Mage.Sets/src/mage/cards/n/NorwoodWarrior.java
@@ -3,7 +3,7 @@ package mage.cards.n;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.continuous.BoostSourceEffect;
 import mage.cards.CardImpl;
@@ -28,7 +28,7 @@ public final class NorwoodWarrior extends CardImpl {
         // Whenever Norwood Warrior becomes blocked, it gets +1/+1 until end of turn.
         Effect effect = new BoostSourceEffect(1, 1, Duration.EndOfTurn);
         effect.setText("it gets +1/+1 until end of turn");
-        this.addAbility(new BecomesBlockedTriggeredAbility(effect, false));
+        this.addAbility(new BecomesBlockedSourceTriggeredAbility(effect, false));
     }
 
     public NorwoodWarrior(final NorwoodWarrior card) {
diff --git a/Mage.Sets/src/mage/cards/p/PlagueWight.java b/Mage.Sets/src/mage/cards/p/PlagueWight.java
index 433b85b01d..aa6551c174 100644
--- a/Mage.Sets/src/mage/cards/p/PlagueWight.java
+++ b/Mage.Sets/src/mage/cards/p/PlagueWight.java
@@ -2,7 +2,7 @@ package mage.cards.p;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.continuous.BoostAllEffect;
 import mage.cards.CardImpl;
@@ -32,7 +32,7 @@ public final class PlagueWight extends CardImpl {
         this.toughness = new MageInt(1);
 
         // Whenever Plague Wight becomes blocked, each creature blocking it gets -1/-1 until end of turn.
-        this.addAbility(new BecomesBlockedTriggeredAbility(new PlagueWightEffect(), false));
+        this.addAbility(new BecomesBlockedSourceTriggeredAbility(new PlagueWightEffect(), false));
     }
 
     private PlagueWight(final PlagueWight card) {
diff --git a/Mage.Sets/src/mage/cards/r/RabidElephant.java b/Mage.Sets/src/mage/cards/r/RabidElephant.java
index 58f0770275..95a69e47ec 100644
--- a/Mage.Sets/src/mage/cards/r/RabidElephant.java
+++ b/Mage.Sets/src/mage/cards/r/RabidElephant.java
@@ -3,7 +3,7 @@ package mage.cards.r;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.dynamicvalue.DynamicValue;
 import mage.abilities.dynamicvalue.MultipliedValue;
 import mage.abilities.dynamicvalue.common.BlockedCreatureCount;
@@ -32,7 +32,7 @@ public final class RabidElephant extends CardImpl {
         DynamicValue value = new MultipliedValue(new BlockedCreatureCount(), 2);
         Effect effect = new BoostSourceEffect(value, value, Duration.EndOfTurn, true);
         effect.setText("it gets +2/+2 until end of turn for each creature blocking it");
-        this.addAbility(new BecomesBlockedTriggeredAbility(effect, false));
+        this.addAbility(new BecomesBlockedSourceTriggeredAbility(effect, false));
     }
 
     public RabidElephant(final RabidElephant card) {
diff --git a/Mage.Sets/src/mage/cards/r/RazorclawBear.java b/Mage.Sets/src/mage/cards/r/RazorclawBear.java
index 74057a10b8..1fe1cd2af4 100644
--- a/Mage.Sets/src/mage/cards/r/RazorclawBear.java
+++ b/Mage.Sets/src/mage/cards/r/RazorclawBear.java
@@ -3,7 +3,7 @@ package mage.cards.r;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.continuous.BoostSourceEffect;
 import mage.cards.CardImpl;
@@ -27,7 +27,7 @@ public final class RazorclawBear extends CardImpl {
         // Whenever Razorclaw Bear becomes blocked, it gets +2/+2 until end of turn.
         Effect effect = new BoostSourceEffect(2, 2, Duration.EndOfTurn);
         effect.setText("it gets +2/+2 until end of turn");
-        this.addAbility(new BecomesBlockedTriggeredAbility(effect, false));
+        this.addAbility(new BecomesBlockedSourceTriggeredAbility(effect, false));
     }
 
     public RazorclawBear(final RazorclawBear card) {
diff --git a/Mage.Sets/src/mage/cards/s/SacredPrey.java b/Mage.Sets/src/mage/cards/s/SacredPrey.java
index fe1ff1407a..ce4c023b4d 100644
--- a/Mage.Sets/src/mage/cards/s/SacredPrey.java
+++ b/Mage.Sets/src/mage/cards/s/SacredPrey.java
@@ -3,7 +3,7 @@ package mage.cards.s;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.common.GainLifeEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -23,7 +23,7 @@ public final class SacredPrey extends CardImpl {
         this.toughness = new MageInt(1);
 
         // Whenever Sacred Prey becomes blocked, you gain 1 life.
-        this.addAbility(new BecomesBlockedTriggeredAbility(new GainLifeEffect(1), false));
+        this.addAbility(new BecomesBlockedSourceTriggeredAbility(new GainLifeEffect(1), false));
     }
 
     public SacredPrey(final SacredPrey card) {
diff --git a/Mage.Sets/src/mage/cards/s/SaprazzanHeir.java b/Mage.Sets/src/mage/cards/s/SaprazzanHeir.java
index 3fea5cefbb..111f4df99c 100644
--- a/Mage.Sets/src/mage/cards/s/SaprazzanHeir.java
+++ b/Mage.Sets/src/mage/cards/s/SaprazzanHeir.java
@@ -3,7 +3,7 @@ package mage.cards.s;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -23,7 +23,7 @@ public final class SaprazzanHeir extends CardImpl {
         this.toughness = new MageInt(1);
 
         // Whenever Saprazzan Heir becomes blocked, you may draw three cards.
-        this.addAbility(new BecomesBlockedTriggeredAbility(new DrawCardSourceControllerEffect(3), true));
+        this.addAbility(new BecomesBlockedSourceTriggeredAbility(new DrawCardSourceControllerEffect(3), true));
     }
 
     public SaprazzanHeir(final SaprazzanHeir card) {
diff --git a/Mage.Sets/src/mage/cards/s/SaprazzanRaider.java b/Mage.Sets/src/mage/cards/s/SaprazzanRaider.java
index 50ab00e49d..cb0d63682f 100644
--- a/Mage.Sets/src/mage/cards/s/SaprazzanRaider.java
+++ b/Mage.Sets/src/mage/cards/s/SaprazzanRaider.java
@@ -3,7 +3,7 @@ package mage.cards.s;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.common.ReturnToHandSourceEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -23,7 +23,7 @@ public final class SaprazzanRaider extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Saprazzan Raider becomes blocked, return it to its owner's hand.
-        this.addAbility(new BecomesBlockedTriggeredAbility(new ReturnToHandSourceEffect(), false));
+        this.addAbility(new BecomesBlockedSourceTriggeredAbility(new ReturnToHandSourceEffect(), false));
     }
 
     public SaprazzanRaider(final SaprazzanRaider card) {
diff --git a/Mage.Sets/src/mage/cards/s/SilkenfistFighter.java b/Mage.Sets/src/mage/cards/s/SilkenfistFighter.java
index 4d8c1d64cb..bd7c4a5227 100644
--- a/Mage.Sets/src/mage/cards/s/SilkenfistFighter.java
+++ b/Mage.Sets/src/mage/cards/s/SilkenfistFighter.java
@@ -3,7 +3,7 @@ package mage.cards.s;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.UntapSourceEffect;
 import mage.cards.CardImpl;
@@ -27,7 +27,7 @@ public final class SilkenfistFighter extends CardImpl {
         // Whenever Silkenfist Fighter becomes blocked, untap it.
         Effect effect = new UntapSourceEffect();
         effect.setText("untap it");
-        this.addAbility(new BecomesBlockedTriggeredAbility(effect, false));
+        this.addAbility(new BecomesBlockedSourceTriggeredAbility(effect, false));
     }
 
     public SilkenfistFighter(final SilkenfistFighter card) {
diff --git a/Mage.Sets/src/mage/cards/s/SilkenfistOrder.java b/Mage.Sets/src/mage/cards/s/SilkenfistOrder.java
index 1d6b9ff506..5f76079d5f 100644
--- a/Mage.Sets/src/mage/cards/s/SilkenfistOrder.java
+++ b/Mage.Sets/src/mage/cards/s/SilkenfistOrder.java
@@ -3,7 +3,7 @@ package mage.cards.s;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.UntapSourceEffect;
 import mage.cards.CardImpl;
@@ -27,7 +27,7 @@ public final class SilkenfistOrder extends CardImpl {
         // Whenever Silkenfist Order becomes blocked, untap it.
         Effect effect = new UntapSourceEffect();
         effect.setText("untap it");
-        this.addAbility(new BecomesBlockedTriggeredAbility(effect, false));
+        this.addAbility(new BecomesBlockedSourceTriggeredAbility(effect, false));
     }
 
     public SilkenfistOrder(final SilkenfistOrder card) {
diff --git a/Mage.Sets/src/mage/cards/s/SlashingTiger.java b/Mage.Sets/src/mage/cards/s/SlashingTiger.java
index 6f40799b4b..a0a963a28d 100644
--- a/Mage.Sets/src/mage/cards/s/SlashingTiger.java
+++ b/Mage.Sets/src/mage/cards/s/SlashingTiger.java
@@ -3,7 +3,7 @@ package mage.cards.s;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.common.continuous.BoostSourceEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -24,7 +24,7 @@ public final class SlashingTiger extends CardImpl {
         this.toughness = new MageInt(3);
 
         // Whenever Slashing Tiger becomes blocked, it gets +2/+2 until end of turn.
-        this.addAbility(new BecomesBlockedTriggeredAbility(new BoostSourceEffect(2, 2, Duration.EndOfTurn), false));
+        this.addAbility(new BecomesBlockedSourceTriggeredAbility(new BoostSourceEffect(2, 2, Duration.EndOfTurn), false));
     }
 
     public SlashingTiger(final SlashingTiger card) {
diff --git a/Mage.Sets/src/mage/cards/s/SlithStrider.java b/Mage.Sets/src/mage/cards/s/SlithStrider.java
index b1b9474e73..73845b1475 100644
--- a/Mage.Sets/src/mage/cards/s/SlithStrider.java
+++ b/Mage.Sets/src/mage/cards/s/SlithStrider.java
@@ -3,7 +3,7 @@ package mage.cards.s;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.abilities.effects.common.counter.AddCountersSourceEffect;
@@ -25,7 +25,7 @@ public final class SlithStrider extends CardImpl {
 
         this.power = new MageInt(1);
         this.toughness = new MageInt(1);
-        this.addAbility(new BecomesBlockedTriggeredAbility(new DrawCardSourceControllerEffect(1), false));
+        this.addAbility(new BecomesBlockedSourceTriggeredAbility(new DrawCardSourceControllerEffect(1), false));
         this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(1)), false));
     }
 
diff --git a/Mage.Sets/src/mage/cards/s/SnortingGahr.java b/Mage.Sets/src/mage/cards/s/SnortingGahr.java
index 6a972e4a19..d830c18f83 100644
--- a/Mage.Sets/src/mage/cards/s/SnortingGahr.java
+++ b/Mage.Sets/src/mage/cards/s/SnortingGahr.java
@@ -3,7 +3,7 @@ package mage.cards.s;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.continuous.BoostSourceEffect;
 import mage.cards.CardImpl;
@@ -28,7 +28,7 @@ public final class SnortingGahr extends CardImpl {
         // Whenever Snorting Gahr becomes blocked, it gets +2/+2 until end of turn.
         Effect effect = new BoostSourceEffect(2, 2, Duration.EndOfTurn);
         effect.setText("it gets +2/+2 until end of turn");
-        this.addAbility(new BecomesBlockedTriggeredAbility(effect, false));
+        this.addAbility(new BecomesBlockedSourceTriggeredAbility(effect, false));
     }
 
     public SnortingGahr(final SnortingGahr card) {
diff --git a/Mage.Sets/src/mage/cards/s/SparringGolem.java b/Mage.Sets/src/mage/cards/s/SparringGolem.java
index 8bf7eb23cb..191eb03673 100644
--- a/Mage.Sets/src/mage/cards/s/SparringGolem.java
+++ b/Mage.Sets/src/mage/cards/s/SparringGolem.java
@@ -3,7 +3,7 @@ package mage.cards.s;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.dynamicvalue.common.BlockedCreatureCount;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.continuous.BoostSourceEffect;
@@ -29,7 +29,7 @@ public final class SparringGolem extends CardImpl {
         BlockedCreatureCount value = new BlockedCreatureCount();
         Effect effect = new BoostSourceEffect(value, value, Duration.EndOfTurn, true);
         effect.setText("it gets +1/+1 until end of turn for each creature blocking it");
-        this.addAbility(new BecomesBlockedTriggeredAbility(effect, false));
+        this.addAbility(new BecomesBlockedSourceTriggeredAbility(effect, false));
     }
 
     public SparringGolem(final SparringGolem card) {
diff --git a/Mage.Sets/src/mage/cards/s/StormsurgeKraken.java b/Mage.Sets/src/mage/cards/s/StormsurgeKraken.java
index f28520f181..50b685b935 100644
--- a/Mage.Sets/src/mage/cards/s/StormsurgeKraken.java
+++ b/Mage.Sets/src/mage/cards/s/StormsurgeKraken.java
@@ -4,7 +4,7 @@ package mage.cards.s;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.abilityword.LieutenantAbility;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.ContinuousEffect;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
@@ -32,7 +32,7 @@ public final class StormsurgeKraken extends CardImpl {
         this.addAbility(HexproofAbility.getInstance());
         
         // Lieutenant - As long as you control your commander, Stormsurge Kraken gets +2/+2 and has "Whenever Stormsurge Kraken becomes blocked, you may draw two cards."
-        ContinuousEffect effect = new GainAbilitySourceEffect(new BecomesBlockedTriggeredAbility(new DrawCardSourceControllerEffect(2), true), Duration.WhileOnBattlefield);
+        ContinuousEffect effect = new GainAbilitySourceEffect(new BecomesBlockedSourceTriggeredAbility(new DrawCardSourceControllerEffect(2), true), Duration.WhileOnBattlefield);
         effect.setText("and has \"Whenever Stormsurge Kraken becomes blocked, you may draw two cards.\"");
         this.addAbility(new LieutenantAbility(effect));
     }
diff --git a/Mage.Sets/src/mage/cards/t/TrainedCheetah.java b/Mage.Sets/src/mage/cards/t/TrainedCheetah.java
index df354300b1..4b0d1ae315 100644
--- a/Mage.Sets/src/mage/cards/t/TrainedCheetah.java
+++ b/Mage.Sets/src/mage/cards/t/TrainedCheetah.java
@@ -3,7 +3,7 @@ package mage.cards.t;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.common.continuous.BoostSourceEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -24,7 +24,7 @@ public final class TrainedCheetah extends CardImpl {
         this.toughness = new MageInt(2);
 
         // Whenever Trained Cheetah becomes blocked, it gets +1/+1 until end of turn.
-        this.addAbility(new BecomesBlockedTriggeredAbility(new BoostSourceEffect(1, 1, Duration.EndOfTurn), false));
+        this.addAbility(new BecomesBlockedSourceTriggeredAbility(new BoostSourceEffect(1, 1, Duration.EndOfTurn), false));
     }
 
     public TrainedCheetah(final TrainedCheetah card) {
diff --git a/Mage.Sets/src/mage/cards/v/VedalkenGhoul.java b/Mage.Sets/src/mage/cards/v/VedalkenGhoul.java
index b8b15b2d2e..9911b85d8e 100644
--- a/Mage.Sets/src/mage/cards/v/VedalkenGhoul.java
+++ b/Mage.Sets/src/mage/cards/v/VedalkenGhoul.java
@@ -3,7 +3,7 @@ package mage.cards.v;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.common.LoseLifeDefendingPlayerEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -25,7 +25,7 @@ public final class VedalkenGhoul extends CardImpl {
         this.toughness = new MageInt(1);
 
         // Whenever Vedalken Ghoul becomes blocked, defending player loses 4 life.
-        this.addAbility(new BecomesBlockedTriggeredAbility(new LoseLifeDefendingPlayerEffect(4, true), false));
+        this.addAbility(new BecomesBlockedSourceTriggeredAbility(new LoseLifeDefendingPlayerEffect(4, true), false));
 
     }
 
diff --git a/Mage.Sets/src/mage/cards/w/WoebringerDemon.java b/Mage.Sets/src/mage/cards/w/WoebringerDemon.java
index 875ec964ae..d61f395196 100644
--- a/Mage.Sets/src/mage/cards/w/WoebringerDemon.java
+++ b/Mage.Sets/src/mage/cards/w/WoebringerDemon.java
@@ -35,8 +35,10 @@ public final class WoebringerDemon extends CardImpl {
         // Flying
         this.addAbility(FlyingAbility.getInstance());
 
-        // At the beginning of each player's upkeep, that player sacrifices a creature. If the player can't, sacrifice Woebringer Demon.
-        this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new WoebringerDemonEffect(), TargetController.ANY, false, true));
+        // At the beginning of each player's upkeep, that player sacrifices a creature. 
+        // If the player can't, sacrifice Woebringer Demon.
+        this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, 
+                new WoebringerDemonEffect(), TargetController.ANY, false, true));
     }
 
     public WoebringerDemon(final WoebringerDemon card) {

From cb7c787c37e546d5fac5da2433720d2ea321e4ca Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Mon, 22 Jun 2020 09:45:55 -0500
Subject: [PATCH 456/586] - Little fix Toshiro Umezawa

---
 Mage.Sets/src/mage/cards/t/ToshiroUmezawa.java | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/t/ToshiroUmezawa.java b/Mage.Sets/src/mage/cards/t/ToshiroUmezawa.java
index 3d6c22d1a8..4cdb286fa2 100644
--- a/Mage.Sets/src/mage/cards/t/ToshiroUmezawa.java
+++ b/Mage.Sets/src/mage/cards/t/ToshiroUmezawa.java
@@ -5,10 +5,8 @@ import mage.MageInt;
 import mage.MageObjectReference;
 import mage.abilities.Ability;
 import mage.abilities.common.DiesCreatureTriggeredAbility;
-import mage.abilities.effects.ContinuousEffect;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.ReplacementEffectImpl;
-import mage.abilities.effects.common.replacement.PutToGraveyardReplacementEffect;
 import mage.abilities.keyword.BushidoAbility;
 import mage.cards.Card;
 import mage.cards.CardImpl;
@@ -24,7 +22,6 @@ import mage.game.stack.Spell;
 import mage.game.stack.StackObject;
 import mage.players.Player;
 import mage.target.common.TargetCardInYourGraveyard;
-import mage.target.targetpointer.FixedTarget;
 
 /**
  *

From 16adeadd8e7eddc728ddb2fd94a1ce2619a53a87 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Mon, 22 Jun 2020 17:44:59 +0200
Subject: [PATCH 457/586] * Oathbreaker format - Fixed endless loop on match
 init (#6695).

---
 .../src/mage/game/OathbreakerFreeForAll.java                    | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Mage.Server.Plugins/Mage.Game.OathbreakerFreeForAll/src/mage/game/OathbreakerFreeForAll.java b/Mage.Server.Plugins/Mage.Game.OathbreakerFreeForAll/src/mage/game/OathbreakerFreeForAll.java
index 7cbb8eb5b0..9c517d0361 100644
--- a/Mage.Server.Plugins/Mage.Game.OathbreakerFreeForAll/src/mage/game/OathbreakerFreeForAll.java
+++ b/Mage.Server.Plugins/Mage.Game.OathbreakerFreeForAll/src/mage/game/OathbreakerFreeForAll.java
@@ -122,7 +122,7 @@ public class OathbreakerFreeForAll extends GameCommanderImpl {
         if (player != null) {
             Set<UUID> commanders = this.playerOathbreakers.getOrDefault(player.getId(), new HashSet<>());
             Set<UUID> spells = this.playerSignatureSpells.getOrDefault(player.getId(), new HashSet<>());
-            for (UUID commanderId : getCommandersIds(player, commanderCardType)) {
+            for (UUID commanderId : super.getCommandersIds(player, commanderCardType)) {
                 switch (commanderCardType) {
                     case ANY:
                         res.add(commanderId);

From 8667d2a92304445abbdcf1a3b3fa1b61bcc3c058 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Mon, 22 Jun 2020 20:20:33 +0400
Subject: [PATCH 458/586] * Special mana payments like convoke/delve - fixed
 that it can't be used to cast card from command zone (example: Tasigur, the
 Golden Fang, see #6680);

---
 .../mage/cards/t/TasigurTheGoldenFang.java    |   2 +-
 .../PayDelveFromCommandZoneTest.java          |  35 ++++++
 .../main/java/mage/players/PlayerImpl.java    | 100 ++++++++++--------
 Mage/src/main/java/mage/util/CardUtil.java    |   9 ++
 4 files changed, 98 insertions(+), 48 deletions(-)
 create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/PayDelveFromCommandZoneTest.java

diff --git a/Mage.Sets/src/mage/cards/t/TasigurTheGoldenFang.java b/Mage.Sets/src/mage/cards/t/TasigurTheGoldenFang.java
index 814b97225b..af9604a194 100644
--- a/Mage.Sets/src/mage/cards/t/TasigurTheGoldenFang.java
+++ b/Mage.Sets/src/mage/cards/t/TasigurTheGoldenFang.java
@@ -1,4 +1,3 @@
-
 package mage.cards.t;
 
 import mage.MageInt;
@@ -37,6 +36,7 @@ public final class TasigurTheGoldenFang extends CardImpl {
 
         // Delve
         this.addAbility(new DelveAbility());
+
         // {2}{G/U}{G/U}: Put the top two cards of your library into your graveyard, then return a nonland card of an opponent's choice from your graveyard to your hand.
         Ability ability = new SimpleActivatedAbility(new PutTopCardOfLibraryIntoGraveControllerEffect(2), new ManaCostsImpl("{2}{G/U}{G/U}"));
         ability.addEffect(new TasigurTheGoldenFangEffect());
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/PayDelveFromCommandZoneTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/PayDelveFromCommandZoneTest.java
new file mode 100644
index 0000000000..797e35260a
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/PayDelveFromCommandZoneTest.java
@@ -0,0 +1,35 @@
+package org.mage.test.cards.cost.modification;
+
+import mage.constants.PhaseStep;
+import mage.constants.Zone;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestCommander4Players;
+
+/**
+ * @author JayDi85
+ */
+public class PayDelveFromCommandZoneTest extends CardTestCommander4Players {
+
+    // Player order: A -> D -> C -> B
+    @Test
+    public void test_Other_CastFromCommand_Delve() {
+        // https://github.com/magefree/mage/issues/6698
+        // Having this problem with Tasigur, the Golden Fang. I can't attempt to use delve to cast him from command zone.
+
+        // {5}{B} creature
+        // Delve (Each card you exile from your graveyard while casting this spell pays for {1}.)
+        addCard(Zone.COMMAND, playerA, "Tasigur, the Golden Fang", 1);
+        addCard(Zone.GRAVEYARD, playerA, "Balduvian Bears", 5); // delve
+        addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Tasigur, the Golden Fang");
+        setChoice(playerA, "Balduvian Bears", 5); // delve pay
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerA, "Tasigur, the Golden Fang", 1);
+    }
+}
diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java
index 19343ddc0d..5ac203e08e 100644
--- a/Mage/src/main/java/mage/players/PlayerImpl.java
+++ b/Mage/src/main/java/mage/players/PlayerImpl.java
@@ -3105,12 +3105,12 @@ public abstract class PlayerImpl implements Player, Serializable {
         return false;
     }
 
-    protected ActivatedAbility findActivatedAbilityFromPlayable(Card card, ManaOptions manaAvailable, Ability ability, Game game) {
+    protected ActivatedAbility findActivatedAbilityFromPlayable(MageObject object, ManaOptions manaAvailable, Ability ability, Game game) {
 
         // special mana to pay spell cost
         ManaOptions manaFull = manaAvailable.copy();
         if (ability instanceof SpellAbility) {
-            for (AlternateManaPaymentAbility altAbility : card.getAbilities(game).stream()
+            for (AlternateManaPaymentAbility altAbility : CardUtil.getAbilities(object, game).stream()
                     .filter(a -> a instanceof AlternateManaPaymentAbility)
                     .map(a -> (AlternateManaPaymentAbility) a)
                     .collect(Collectors.toList())) {
@@ -3127,10 +3127,10 @@ public abstract class PlayerImpl implements Player, Serializable {
             }
         } else if (ability instanceof AlternativeSourceCosts) {
             // alternative cost must be replaced by real play ability
-            return findActivatedAbilityFromAlternativeSourceCost(card, manaFull, ability, game);
+            return findActivatedAbilityFromAlternativeSourceCost(object, manaFull, ability, game);
         } else if (ability instanceof ActivatedAbility) {
             // all other activated ability
-            if (canPlay((ActivatedAbility) ability, manaFull, card, game)) {
+            if (canPlay((ActivatedAbility) ability, manaFull, object, game)) {
                 return (ActivatedAbility) ability;
             }
         }
@@ -3139,30 +3139,33 @@ public abstract class PlayerImpl implements Player, Serializable {
         return null;
     }
 
-    protected ActivatedAbility findActivatedAbilityFromAlternativeSourceCost(Card card, ManaOptions manaAvailable, Ability ability, Game game) {
+    protected ActivatedAbility findActivatedAbilityFromAlternativeSourceCost(MageObject object, ManaOptions manaAvailable, Ability ability, Game game) {
         // alternative cost must be replaced by real play ability
         if (ability instanceof AlternativeSourceCosts) {
             AlternativeSourceCosts altAbility = (AlternativeSourceCosts) ability;
-            if (card.isLand()) {
+            if (object.isLand()) {
                 // land
                 // morph ability is static, so it must be replaced with play land ability (playLand search and try to use face down first)
-                if (canLandPlayAlternateSourceCostsAbility(card, manaAvailable, ability, game)) { // e.g. Land with Morph
-                    Optional<Ability> landAbility = card.getAbilities(game).stream().filter(a -> a instanceof PlayLandAbility).findFirst();
-                    if (landAbility.isPresent()) {
-                        return (ActivatedAbility) landAbility.get();
+                if (canLandPlayAlternateSourceCostsAbility(object, manaAvailable, ability, game)) { // e.g. Land with Morph
+                    Ability landAbility = CardUtil.getAbilities(object, game).stream().filter(a -> a instanceof PlayLandAbility).findFirst().orElse(null);
+                    if (landAbility != null) {
+                        return (PlayLandAbility) landAbility;
                     }
                 }
             } else {
                 // creature and other
-                if (altAbility.isAvailable(card.getSpellAbility(), game)) {
-                    return card.getSpellAbility();
+                if (object instanceof Card) {
+                    SpellAbility spellAbility = ((Card) object).getSpellAbility();
+                    if (altAbility.isAvailable(spellAbility, game)) {
+                        return spellAbility;
+                    }
                 }
             }
         }
         return null;
     }
 
-    protected boolean canLandPlayAlternateSourceCostsAbility(Card sourceObject, ManaOptions available, Ability ability, Game game) {
+    protected boolean canLandPlayAlternateSourceCostsAbility(MageObject sourceObject, ManaOptions available, Ability ability, Game game) {
         if (sourceObject != null && !(sourceObject instanceof Permanent)) {
             Ability sourceAbility = sourceObject.getAbilities().stream()
                     .filter(landAbility -> landAbility.getAbilityType() == AbilityType.PLAY_LAND)
@@ -3194,30 +3197,33 @@ public abstract class PlayerImpl implements Player, Serializable {
         return false;
     }
 
-    private void getPlayableFromCardAll(Game game, Zone fromZone, Card card, ManaOptions availableMana, List<ActivatedAbility> output) {
-        if (fromZone == null || card == null) {
+    private void getPlayableFromObjectAll(Game game, Zone fromZone, MageObject object, ManaOptions availableMana, List<ActivatedAbility> output) {
+        if (fromZone == null || object == null) {
             return;
         }
 
         // BASIC abilities
-        if (card instanceof SplitCard) {
-            SplitCard splitCard = (SplitCard) card;
-            getPlayableFromCardSingle(game, fromZone, splitCard.getLeftHalfCard(), splitCard.getLeftHalfCard().getAbilities(game), availableMana, output);
-            getPlayableFromCardSingle(game, fromZone, splitCard.getRightHalfCard(), splitCard.getRightHalfCard().getAbilities(game), availableMana, output);
-            getPlayableFromCardSingle(game, fromZone, splitCard, splitCard.getSharedAbilities(game), availableMana, output);
-        } else if (card instanceof AdventureCard) {
+        if (object instanceof SplitCard) {
+            SplitCard splitCard = (SplitCard) object;
+            getPlayableFromObjectSingle(game, fromZone, splitCard.getLeftHalfCard(), splitCard.getLeftHalfCard().getAbilities(game), availableMana, output);
+            getPlayableFromObjectSingle(game, fromZone, splitCard.getRightHalfCard(), splitCard.getRightHalfCard().getAbilities(game), availableMana, output);
+            getPlayableFromObjectSingle(game, fromZone, splitCard, splitCard.getSharedAbilities(game), availableMana, output);
+        } else if (object instanceof AdventureCard) {
             // adventure must use different card characteristics for different spells (main or adventure)
-            AdventureCard adventureCard = (AdventureCard) card;
-            getPlayableFromCardSingle(game, fromZone, adventureCard.getSpellCard(), adventureCard.getSpellCard().getAbilities(game), availableMana, output);
-            getPlayableFromCardSingle(game, fromZone, adventureCard, adventureCard.getSharedAbilities(game), availableMana, output);
+            AdventureCard adventureCard = (AdventureCard) object;
+            getPlayableFromObjectSingle(game, fromZone, adventureCard.getSpellCard(), adventureCard.getSpellCard().getAbilities(game), availableMana, output);
+            getPlayableFromObjectSingle(game, fromZone, adventureCard, adventureCard.getSharedAbilities(game), availableMana, output);
+        } else if (object instanceof Card) {
+            getPlayableFromObjectSingle(game, fromZone, object, ((Card) object).getAbilities(game), availableMana, output);
         } else {
-            getPlayableFromCardSingle(game, fromZone, card, card.getAbilities(game), availableMana, output);
+            // other things like StackObject or CommandObject
+            getPlayableFromObjectSingle(game, fromZone, object, object.getAbilities(), availableMana, output);
         }
 
         // DYNAMIC ADDED abilities are adds in getAbilities(game)
     }
 
-    private void getPlayableFromCardSingle(Game game, Zone fromZone, Card card, Abilities<Ability> candidateAbilities, ManaOptions availableMana, List<ActivatedAbility> output) {
+    private void getPlayableFromObjectSingle(Game game, Zone fromZone, MageObject object, Abilities<Ability> candidateAbilities, ManaOptions availableMana, List<ActivatedAbility> output) {
         // check "can play" condition as affected controller (BUT play from not own hand zone must be checked as original controller)
         // must check all abilities, not activated only
         for (Ability ability : candidateAbilities) {
@@ -3247,7 +3253,7 @@ public abstract class PlayerImpl implements Player, Serializable {
             MageObjectReference permittingObject;
             if (isPlaySpell || isPlayLand) {
                 // play hand from non hand zone
-                permittingObject = game.getContinuousEffects().asThough(card.getId(),
+                permittingObject = game.getContinuousEffects().asThough(object.getId(),
                         AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, ability, this.getId(), game);
             } else {
                 // other abilities from direct zones
@@ -3277,7 +3283,7 @@ public abstract class PlayerImpl implements Player, Serializable {
             }
 
             // direct mode (with original controller)
-            ActivatedAbility playAbility = findActivatedAbilityFromPlayable(card, availableMana, ability, game);
+            ActivatedAbility playAbility = findActivatedAbilityFromPlayable(object, availableMana, ability, game);
             if (playAbility != null && !output.contains(playAbility)) {
                 output.add(playAbility);
                 continue;
@@ -3288,7 +3294,7 @@ public abstract class PlayerImpl implements Player, Serializable {
                 UUID savedControllerId = ability.getControllerId();
                 ability.setControllerId(this.getId());
                 try {
-                    playAbility = findActivatedAbilityFromPlayable(card, availableMana, ability, game);
+                    playAbility = findActivatedAbilityFromPlayable(object, availableMana, ability, game);
                     if (playAbility != null && !output.contains(playAbility)) {
                         output.add(playAbility);
                     }
@@ -3359,14 +3365,14 @@ public abstract class PlayerImpl implements Player, Serializable {
 
             if (fromAll || fromZone == Zone.GRAVEYARD) {
                 for (Card card : graveyard.getCards(game)) {
-                    getPlayableFromCardAll(game, Zone.GRAVEYARD, card, availableMana, playable);
+                    getPlayableFromObjectAll(game, Zone.GRAVEYARD, card, availableMana, playable);
                 }
             }
 
             if (fromAll || fromZone == Zone.EXILED) {
                 for (ExileZone exile : game.getExile().getExileZones()) {
                     for (Card card : exile.getCards(game)) {
-                        getPlayableFromCardAll(game, Zone.EXILED, card, availableMana, playable);
+                        getPlayableFromObjectAll(game, Zone.EXILED, card, availableMana, playable);
                     }
                 }
             }
@@ -3376,7 +3382,7 @@ public abstract class PlayerImpl implements Player, Serializable {
                 for (Cards revealedCards : game.getState().getRevealed().values()) {
                     for (Card card : revealedCards.getCards(game)) {
                         // revealed cards can be from any zones
-                        getPlayableFromCardAll(game, game.getState().getZone(card.getId()), card, availableMana, playable);
+                        getPlayableFromObjectAll(game, game.getState().getZone(card.getId()), card, availableMana, playable);
                     }
                 }
             }
@@ -3385,7 +3391,7 @@ public abstract class PlayerImpl implements Player, Serializable {
             if (fromAll || fromZone == Zone.OUTSIDE) {
                 for (Cards companionCards : game.getState().getCompanion().values()) {
                     for (Card card : companionCards.getCards(game)) {
-                        getPlayableFromCardAll(game, Zone.OUTSIDE, card, availableMana, playable);
+                        getPlayableFromObjectAll(game, Zone.OUTSIDE, card, availableMana, playable);
                     }
                 }
             }
@@ -3397,7 +3403,7 @@ public abstract class PlayerImpl implements Player, Serializable {
                     if (player != null && player.getLibrary().hasCards()) {
                         Card card = player.getLibrary().getFromTop(game);
                         if (card != null) {
-                            getPlayableFromCardAll(game, Zone.LIBRARY, card, availableMana, playable);
+                            getPlayableFromObjectAll(game, Zone.LIBRARY, card, availableMana, playable);
                         }
                     }
                 }
@@ -3411,9 +3417,9 @@ public abstract class PlayerImpl implements Player, Serializable {
             if (fromAll || fromZone == Zone.BATTLEFIELD) {
                 for (Permanent permanent : game.getBattlefield().getAllActivePermanents()) {
                     boolean canUseActivated = permanent.canUseActivatedAbilities(game);
-                    List<ActivatedAbility> battlePlayable = new ArrayList<>();
-                    getPlayableFromCardAll(game, Zone.BATTLEFIELD, permanent, availableMana, battlePlayable);
-                    for (ActivatedAbility ability : battlePlayable) {
+                    List<ActivatedAbility> currentPlayable = new ArrayList<>();
+                    getPlayableFromObjectAll(game, Zone.BATTLEFIELD, permanent, availableMana, currentPlayable);
+                    for (ActivatedAbility ability : currentPlayable) {
                         if (ability instanceof SpecialAction || canUseActivated) {
                             activatedUnique.putIfAbsent(ability.toString(), ability);
                             activatedAll.add(ability);
@@ -3425,11 +3431,11 @@ public abstract class PlayerImpl implements Player, Serializable {
             // activated abilities from stack objects
             if (fromAll || fromZone == Zone.STACK) {
                 for (StackObject stackObject : game.getState().getStack()) {
-                    for (ActivatedAbility ability : stackObject.getAbilities().getActivatedAbilities(Zone.STACK)) {
-                        if (canPlay(ability, availableMana, game.getObject(ability.getSourceId()), game)) {
-                            activatedUnique.put(ability.toString(), ability);
-                            activatedAll.add(ability);
-                        }
+                    List<ActivatedAbility> currentPlayable = new ArrayList<>();
+                    getPlayableFromObjectAll(game, Zone.STACK, stackObject, availableMana, currentPlayable);
+                    for (ActivatedAbility ability : currentPlayable) {
+                        activatedUnique.put(ability.toString(), ability);
+                        activatedAll.add(ability);
                     }
                 }
             }
@@ -3437,11 +3443,11 @@ public abstract class PlayerImpl implements Player, Serializable {
             // activated abilities from objects in the command zone (emblems or commanders)
             if (fromAll || fromZone == Zone.COMMAND) {
                 for (CommandObject commandObject : game.getState().getCommand()) {
-                    for (ActivatedAbility ability : commandObject.getAbilities().getActivatedAbilities(Zone.COMMAND)) {
-                        if (canPlay(ability, availableMana, game.getObject(ability.getSourceId()), game)) {
-                            activatedUnique.put(ability.toString(), ability);
-                            activatedAll.add(ability);
-                        }
+                    List<ActivatedAbility> currentPlayable = new ArrayList<>();
+                    getPlayableFromObjectAll(game, Zone.COMMAND, commandObject, availableMana, currentPlayable);
+                    for (ActivatedAbility ability : currentPlayable) {
+                        activatedUnique.put(ability.toString(), ability);
+                        activatedAll.add(ability);
                     }
                 }
             }
diff --git a/Mage/src/main/java/mage/util/CardUtil.java b/Mage/src/main/java/mage/util/CardUtil.java
index 1d1b72f079..6836eab716 100644
--- a/Mage/src/main/java/mage/util/CardUtil.java
+++ b/Mage/src/main/java/mage/util/CardUtil.java
@@ -2,6 +2,7 @@ package mage.util;
 
 import mage.MageObject;
 import mage.Mana;
+import mage.abilities.Abilities;
 import mage.abilities.Ability;
 import mage.abilities.SpellAbility;
 import mage.abilities.costs.VariableCost;
@@ -791,4 +792,12 @@ public final class CardUtil {
         }
         return false;
     }
+
+    public static Abilities<Ability> getAbilities(MageObject object, Game game) {
+        if (object instanceof Card) {
+            return ((Card) object).getAbilities(game);
+        } else {
+            return object.getAbilities();
+        }
+    }
 }

From 426f207aac700f0d510a952393d046eec1904f75 Mon Sep 17 00:00:00 2001
From: John Hitchings <hitch17@gmail.com>
Date: Mon, 22 Jun 2020 12:25:18 -0700
Subject: [PATCH 459/586] add final jumpstart card pool and additional comments
 on JumpstartPoolGenerator

---
 .../jumpstart/JumpstartPoolGenerator.java     |   13 +
 .../main/resources/jumpstart/jumpstart.txt    | 1231 ++++++++++++++++-
 2 files changed, 1224 insertions(+), 20 deletions(-)

diff --git a/Mage/src/main/java/mage/game/jumpstart/JumpstartPoolGenerator.java b/Mage/src/main/java/mage/game/jumpstart/JumpstartPoolGenerator.java
index 9039e28e07..e69f0252ce 100644
--- a/Mage/src/main/java/mage/game/jumpstart/JumpstartPoolGenerator.java
+++ b/Mage/src/main/java/mage/game/jumpstart/JumpstartPoolGenerator.java
@@ -81,6 +81,19 @@ public class JumpstartPoolGenerator {
     }
   }
 
+  /* Notes
+   *
+   * 1) the pools generated by this method are for how the prerelease will be played (see https://mtg.gamepedia.com/Jumpstart#Events)
+   *    In order to support the 4 pack version, xmage would need to support editing multiple decks and some Duo format
+   *    similar to https://mtg.gamepedia.com/Duo_Standard
+   *
+   * 2) this treats all packs to have similar chance to open (packs will actually be opened at a particular rarity). This
+   *    could be implemented if you know the various ratios between packs by knowing the rarities from this source:
+   *    https://mtg.gamepedia.com/Jumpstart#Themes_and_mechanics
+   *
+   * 3) this does not attempt to add an additional rare 1/3 of the time as described here:
+   *    https://mtg.gamepedia.com/Jumpstart#Marketing
+   */
   public static Set<Card> generatePool() {
     try {
       DeckCardLists list = new DeckCardLists();
diff --git a/Mage/src/main/resources/jumpstart/jumpstart.txt b/Mage/src/main/resources/jumpstart/jumpstart.txt
index e9e6ddb0d6..73df97d972 100644
--- a/Mage/src/main/resources/jumpstart/jumpstart.txt
+++ b/Mage/src/main/resources/jumpstart/jumpstart.txt
@@ -1,23 +1,1214 @@
-# Jumpstart Decklists
-# https://magic.wizards.com/en/articles/archive/feature/jumpstart-decklists-2020-06-18
-# quantity setCode cardNum cardName
+# Archaeology (1)
+1 JMP 471 Juggernaut
+1 JMP 474 Meteor Golem
+1 JMP 458 Ancestral Statue
+1 JMP 150 Erratic Visionary
+1 JMP 183 Thirst for Knowledge
+1 JMP 462 Chromatic Sphere
+1 JMP 468 Hedron Archive
+1 JMP 491 Buried Ruin
+5 JMP 50 Island
+1 JMP 14 Scholar of the Lost Trove
+1 JMP 456 Aether Spellbomb
+2 JMP 9 Archaeomender
+1 JMP 32 Lightning-Core Excavator
+1 JMP 36 Thriving Isle
+1 JMP 49 Island
 
-# Red Cards
-3 ELD 137 Rimrock Knight
-4 ELD 115 Bonecrusher Giant
-2 ELD 120 Embercleave
-8 ELD 262 Mountain
+# Archaeology (2)
+1 JMP 471 Juggernaut
+1 JMP 474 Meteor Golem
+1 JMP 458 Ancestral Statue
+1 JMP 150 Erratic Visionary
+1 JMP 183 Thirst for Knowledge
+1 JMP 464 Dreamstone Hedron
+1 JMP 488 Terrarion
+1 JMP 491 Buried Ruin
+5 JMP 50 Island
+1 JMP 14 Scholar of the Lost Trove
+1 JMP 456 Aether Spellbomb
+2 JMP 9 Archaeomender
+1 JMP 32 Lightning-Core Excavator
+1 JMP 36 Thriving Isle
+1 JMP 49 Island
 
-# Green Cards
-3 GRN 141 Pelt Collector
-3 WAR 171 Paradise Druid
-3 ELD 165 Lovestruck Beast
-3 ELD 171 Questing Beast
-8 ELD 266 Forest
+# Archaeology (3)
+1 JMP 176 Sharding Sphinx
+1 JMP 187 Vedalken Archmage
+1 JMP 471 Juggernaut
+1 JMP 474 Meteor Golem
+1 JMP 458 Ancestral Statue
+1 M21 235 Prismite
+1 JMP 183 Thirst for Knowledge
+1 JMP 468 Hedron Archive
+1 JMP 491 Buried Ruin
+5 JMP 50 Island
+1 JMP 456 Aether Spellbomb
+2 JMP 9 Archaeomender
+1 JMP 32 Lightning-Core Excavator
+1 JMP 36 Thriving Isle
+1 JMP 49 Island
 
-# Black Cards
-2 THB 123 Woe Strider
-4 ELD 81 Cauldron Familiar
-3 ELD 237 Witch's Oven
-3 IKO 220 Fiend Artisan
-8 ELD 258 Swamp
+# Archaeology (4)
+1 JMP 482 Scarecrone
+1 JMP 471 Juggernaut
+1 JMP 474 Meteor Golem
+1 JMP 484 Scuttlemutt
+1 JMP 470 Jousting Dummy
+1 JMP 485 Signpost Scarecrow
+1 JMP 183 Thirst for Knowledge
+1 M21 236 Short Sword
+1 JMP 491 Buried Ruin
+5 JMP 50 Island
+1 JMP 456 Aether Spellbomb
+2 JMP 9 Archaeomender
+1 JMP 32 Lightning-Core Excavator
+1 JMP 36 Thriving Isle
+1 JMP 49 Island
+
+# Basri
+1 M21 12 Concordia Pegasus
+1 JMP 114 Knight of the Tusk
+1 M21 24 Legion's Judgment
+1 M21 19 Feat of Resistance
+1 M21 17 Faith's Fetters
+7 JMP 45 Plains
+1 JMP 35 Thriving Heath
+1 M21 280 Basri Ket
+1 M21 9 Basri's Lieutenant
+1 M21 41 Tempered Veteran
+1 M21 37 Siege Striker
+1 M21 10 Basri's Solidarity
+1 M21 39 Staunch Shieldmate
+1 M21 287 Basri's Acolyte
+
+# Cats (1)
+1 JMP 407 Keeper of Fables
+1 JMP 403 Initiate's Companion
+1 JMP 419 Pouncing Cheetah
+1 JMP 397 Feral Prowler
+1 JMP 392 Enlarge
+1 JMP 412 Nature's Way
+1 JMP 386 Crushing Canopy
+1 JMP 396 Feral Invocation
+6 JMP 74 Forest
+1 JMP 34 Thriving Grove
+1 JMP 74 Forest
+1 M21 374 Feline Sovereign
+1 M21 175 Canopy Stalker
+1 M21 202 Sabertooth Mauler
+1 M21 196 Pridemalkin
+
+# Cats (2)
+1 JMP 407 Keeper of Fables
+1 JMP 403 Initiate's Companion
+1 JMP 418 Penumbra Bobcat
+1 JMP 397 Feral Prowler
+1 JMP 392 Enlarge
+1 JMP 412 Nature's Way
+1 JMP 386 Crushing Canopy
+1 JMP 410 Lurking Predators
+1 JMP 396 Feral Invocation
+6 JMP 74 Forest
+1 JMP 34 Thriving Grove
+1 JMP 74 Forest
+1 M21 374 Feline Sovereign
+1 M21 175 Canopy Stalker
+1 M21 196 Pridemalkin
+
+# Chandra
+1 JMP 356 Pyroclastic Elemental
+1 JMP 372 Young Pyromancer
+1 JMP 315 Fanatical Firebrand
+1 JMP 317 Flames of the Firebrand
+1 JMP 355 Pillar of Flame
+1 JMP 336 Hungry Flames
+1 M21 165 Thrill of Possibility
+7 JMP 64 Mountain
+1 JMP 33 Thriving Bluff
+1 M21 135 Chandra, Heart of Fire
+1 M21 136 Chandra's Incinerator
+1 M21 138 Chandra's Pyreling
+2 M21 303 Chandra's Magmutt
+
+# Dinosaurs (1)
+1 JMP 399 Ghalta, Primal Hunger
+1 JMP 388 Drover of the Mighty
+1 JMP 416 Orazca Frillback
+1 M21 209 Thrashing Brontodon
+1 M21 176 Colossal Dreadmaw
+1 JMP 427 Savage Stomp
+1 JMP 384 Commune with Dinosaurs
+1 M21 177 Cultivate
+1 JMP 386 Crushing Canopy
+1 M21 205 Setessan Training
+6 JMP 74 Forest
+1 JMP 34 Thriving Grove
+1 JMP 73 Forest
+1 M21 194 Ornery Dilophosaur
+1 M21 178 Drowsing Tyrannodon
+
+# Dinosaurs (2)
+1 JMP 429 Selvala, Heart of the Wilds
+1 JMP 388 Drover of the Mighty
+1 JMP 437 Thundering Spineback
+1 JMP 416 Orazca Frillback
+1 M21 176 Colossal Dreadmaw
+1 JMP 427 Savage Stomp
+1 JMP 384 Commune with Dinosaurs
+1 JMP 386 Crushing Canopy
+1 JMP 414 New Horizons
+6 JMP 74 Forest
+1 JMP 34 Thriving Grove
+1 JMP 73 Forest
+1 M21 308 Garruk's Uprising
+1 M21 194 Ornery Dilophosaur
+1 M21 178 Drowsing Tyrannodon
+
+# Dinosaurs (3)
+1 JMP 399 Ghalta, Primal Hunger
+1 JMP 388 Drover of the Mighty
+1 JMP 437 Thundering Spineback
+1 JMP 416 Orazca Frillback
+1 M21 176 Colossal Dreadmaw
+1 JMP 427 Savage Stomp
+1 JMP 384 Commune with Dinosaurs
+1 M21 177 Cultivate
+1 JMP 386 Crushing Canopy
+1 M21 210 Titanic Growth
+6 JMP 74 Forest
+1 JMP 34 Thriving Grove
+1 JMP 73 Forest
+1 M21 194 Ornery Dilophosaur
+1 M21 178 Drowsing Tyrannodon
+
+# Dinosaurs (4)
+1 JMP 388 Drover of the Mighty
+1 JMP 416 Orazca Frillback
+1 M21 209 Thrashing Brontodon
+1 M21 176 Colossal Dreadmaw
+1 JMP 427 Savage Stomp
+1 JMP 384 Commune with Dinosaurs
+1 JMP 414 New Horizons
+6 JMP 74 Forest
+1 JMP 423 Rampaging Brontodon
+1 JMP 34 Thriving Grove
+1 JMP 73 Forest
+1 M21 377 Primal Might
+1 M21 308 Garruk's Uprising
+1 M21 194 Ornery Dilophosaur
+1 M21 178 Drowsing Tyrannodon
+
+# Discarding 1
+1 JMP 233 Fell Specter
+1 JMP 269 Ravenous Chupacabra
+1 JMP 227 Entomber Exarch
+1 JMP 214 Burglar Rat
+1 JMP 266 Phyrexian Rager
+1 JMP 279 Slate Street Ruffian
+2 JMP 287 Wight of Precinct Six
+1 JMP 200 Assassin's Strike
+1 M21 115 Mind Rot
+1 JMP 222 Death's Approach
+7 JMP 57 Swamp
+1 JMP 17 Tinybones, Trinket Thief
+1 JMP 37 Thriving Moor
+
+# Discarding 2
+1 JMP 251 Liliana's Reaver
+1 JMP 259 Nyxathid
+1 JMP 233 Fell Specter
+1 JMP 269 Ravenous Chupacabra
+1 JMP 227 Entomber Exarch
+1 JMP 214 Burglar Rat
+1 JMP 266 Phyrexian Rager
+1 JMP 279 Slate Street Ruffian
+2 JMP 287 Wight of Precinct Six
+1 JMP 200 Assassin's Strike
+1 JMP 222 Death's Approach
+7 JMP 57 Swamp
+1 JMP 37 Thriving Moor
+
+# Dogs (1)
+1 JMP 81 Affa Guard Hound
+1 JMP 94 Cathar's Companion
+1 M21 19 Feat of Resistance
+1 JMP 125 Pacifism
+6 JMP 45 Plains
+1 JMP 4 Release the Dogs
+1 JMP 7 Supply Runners
+1 JMP 8 Trusty Retriever
+1 JMP 35 Thriving Heath
+1 JMP 45 Plains
+1 M21 29 Pack Leader
+1 M21 36 Selfless Savior
+1 M21 35 Secure the Scene
+1 M21 2 Alpine Watchdog
+1 M21 30 Rambunctious Mutt
+
+# Dogs (2)
+1 JMP 113 Isamaru, Hound of Konda
+1 JMP 81 Affa Guard Hound
+1 JMP 94 Cathar's Companion
+1 JMP 99 Dauntless Onslaught
+1 JMP 125 Pacifism
+6 JMP 45 Plains
+1 JMP 4 Release the Dogs
+1 JMP 7 Supply Runners
+1 JMP 8 Trusty Retriever
+1 JMP 35 Thriving Heath
+1 JMP 45 Plains
+1 M21 29 Pack Leader
+1 M21 35 Secure the Scene
+1 M21 2 Alpine Watchdog
+1 M21 30 Rambunctious Mutt
+
+# Elves (1)
+1 JMP 385 Craterhoof Behemoth
+1 JMP 391 Elvish Archdruid
+1 JMP 389 Dwynen's Elite
+1 JMP 400 Ghirapur Guide
+1 JMP 430 Silhana Wayfinder
+1 JMP 444 Wildheart Invoker
+1 JMP 386 Crushing Canopy
+1 M21 210 Titanic Growth
+1 JMP 420 Presence of Gond
+6 JMP 74 Forest
+1 JMP 34 Thriving Grove
+1 JMP 77 Forest
+1 M21 206 Skyway Sniper
+1 M21 193 Llanowar Visionary
+1 M21 189 Hunter's Edge
+
+# Elves (2)
+1 JMP 389 Dwynen's Elite
+1 JMP 430 Silhana Wayfinder
+1 JMP 447 Wren's Run Vanquisher
+1 JMP 408 Leaf Gilder
+1 JMP 444 Wildheart Invoker
+1 JMP 386 Crushing Canopy
+1 M21 210 Titanic Growth
+1 JMP 420 Presence of Gond
+6 JMP 74 Forest
+1 JMP 28 Allosaurus Shepherd
+1 JMP 34 Thriving Grove
+1 JMP 77 Forest
+1 M21 206 Skyway Sniper
+1 M21 193 Llanowar Visionary
+1 M21 189 Hunter's Edge
+
+# Feathered Friends (1)
+1 JMP 107 Healer's Hawk
+1 M21 12 Concordia Pegasus
+1 JMP 80 Aerial Assault
+1 JMP 99 Dauntless Onslaught
+1 JMP 124 Moment of Heroism
+1 JMP 133 Sky Tether
+6 JMP 45 Plains
+1 JMP 5 Steel-Plume Marshal
+1 JMP 35 Thriving Heath
+1 JMP 44 Plains
+1 M21 5 Aven Gagglemaster
+1 M21 18 Falconer Adept
+1 M21 44 Warded Battlements
+1 M21 20 Gale Swooper
+1 M21 11 Celestial Enforcer
+
+# Feathered Friends (2)
+1 M21 12 Concordia Pegasus
+1 JMP 107 Healer's Hawk
+1 JMP 135 Tandem Tactics
+1 JMP 133 Sky Tether
+6 JMP 45 Plains
+1 JMP 5 Steel-Plume Marshal
+1 JMP 35 Thriving Heath
+1 JMP 44 Plains
+1 M21 3 Angelic Ascension
+1 M21 5 Aven Gagglemaster
+1 M21 18 Falconer Adept
+1 M21 44 Warded Battlements
+1 M21 40 Swift Response
+1 M21 20 Gale Swooper
+1 M21 11 Celestial Enforcer
+
+# Feathered Friends (3)
+1 JMP 85 Angel of the Dire Hour
+1 JMP 107 Healer's Hawk
+1 M21 12 Concordia Pegasus
+1 JMP 99 Dauntless Onslaught
+1 JMP 134 Take Heart
+1 JMP 133 Sky Tether
+6 JMP 45 Plains
+1 JMP 35 Thriving Heath
+1 JMP 44 Plains
+1 M21 5 Aven Gagglemaster
+1 M21 18 Falconer Adept
+1 M21 44 Warded Battlements
+1 M21 40 Swift Response
+1 M21 20 Gale Swooper
+1 M21 11 Celestial Enforcer
+
+# Feathered Friends (4)
+1 JMP 89 Archon of Justice
+1 JMP 90 Archon of Redemption
+1 JMP 107 Healer's Hawk
+1 M21 12 Concordia Pegasus
+1 JMP 80 Aerial Assault
+1 JMP 79 Aegis of the Heavens
+1 JMP 99 Dauntless Onslaught
+1 JMP 133 Sky Tether
+6 JMP 45 Plains
+1 JMP 35 Thriving Heath
+1 JMP 44 Plains
+1 M21 18 Falconer Adept
+1 M21 44 Warded Battlements
+1 M21 20 Gale Swooper
+1 M21 11 Celestial Enforcer
+
+# Garruk
+1 JMP 373 Affectionate Indrik
+1 JMP 381 Brushstrider
+1 JMP 426 Rumbling Baloth
+1 JMP 416 Orazca Frillback
+1 JMP 402 Hunter's Insight
+1 M21 199 Ranger's Guile
+7 JMP 74 Forest
+1 JMP 34 Thriving Grove
+1 M21 305 Garruk, Unleashed
+1 M21 185 Garruk's Harbinger
+1 M21 308 Garruk's Uprising
+1 M21 189 Hunter's Edge
+1 M21 306 Garruk's Gorehorn
+1 M21 178 Drowsing Tyrannodon
+
+# Heavily Armored (1)
+1 JMP 128 Patron of the Valiant
+1 JMP 93 Bulwark Giant
+1 JMP 118 Lightwalker
+1 M21 26 Makeshift Battalion
+1 M21 19 Feat of Resistance
+1 JMP 95 Cathars' Crusade
+6 JMP 45 Plains
+1 JMP 8 Trusty Retriever
+1 JMP 35 Thriving Heath
+1 JMP 43 Plains
+1 M21 41 Tempered Veteran
+1 M21 37 Siege Striker
+1 M21 10 Basri's Solidarity
+1 M21 35 Secure the Scene
+1 M21 287 Basri's Acolyte
+
+# Heavily Armored (2)
+1 JMP 128 Patron of the Valiant
+1 JMP 93 Bulwark Giant
+1 JMP 118 Lightwalker
+1 M21 26 Makeshift Battalion
+1 JMP 106 Gird for Battle
+1 JMP 91 Battlefield Promotion
+1 JMP 95 Cathars' Crusade
+6 JMP 45 Plains
+1 JMP 8 Trusty Retriever
+1 JMP 35 Thriving Heath
+1 JMP 43 Plains
+1 M21 41 Tempered Veteran
+1 M21 37 Siege Striker
+1 M21 35 Secure the Scene
+1 M21 287 Basri's Acolyte
+
+# Heavily Armored (3)
+1 JMP 123 Mikaeus, the Lunarch
+1 JMP 128 Patron of the Valiant
+1 JMP 93 Bulwark Giant
+1 JMP 118 Lightwalker
+1 M21 26 Makeshift Battalion
+1 JMP 91 Battlefield Promotion
+1 JMP 100 Divine Arrow
+6 JMP 45 Plains
+1 JMP 7 Supply Runners
+1 JMP 8 Trusty Retriever
+1 JMP 35 Thriving Heath
+1 JMP 43 Plains
+1 M21 41 Tempered Veteran
+1 M21 10 Basri's Solidarity
+1 M21 287 Basri's Acolyte
+
+# Heavily Armored (4)
+1 JMP 108 High Sentinels of Arashin
+1 JMP 93 Bulwark Giant
+1 JMP 118 Lightwalker
+1 M21 26 Makeshift Battalion
+1 JMP 120 Long Road Home
+1 JMP 101 Duelist's Heritage
+6 JMP 45 Plains
+1 JMP 7 Supply Runners
+1 JMP 8 Trusty Retriever
+1 JMP 35 Thriving Heath
+1 JMP 43 Plains
+1 M21 41 Tempered Veteran
+1 M21 37 Siege Striker
+1 M21 40 Swift Response
+1 M21 287 Basri's Acolyte
+
+# Lands (1)
+1 JMP 439 Ulvenwald Hydra
+1 JMP 379 Awakener Druid
+1 JMP 395 Feral Hydra
+1 JMP 394 Fa'adiyah Seer
+1 JMP 398 Fertilid
+1 JMP 435 Sylvan Ranger
+1 JMP 444 Wildheart Invoker
+1 M21 177 Cultivate
+1 JMP 386 Crushing Canopy
+1 JMP 390 Elemental Uprising
+1 JMP 448 Zendikar's Roil
+1 JMP 440 Vastwood Zendikon
+6 JMP 74 Forest
+1 JMP 34 Thriving Grove
+1 JMP 71 Forest
+
+# Lands (2)
+1 JMP 415 Oracle of Mul Daya
+1 JMP 379 Awakener Druid
+1 JMP 395 Feral Hydra
+1 JMP 446 Woodborn Behemoth
+1 JMP 394 Fa'adiyah Seer
+1 JMP 398 Fertilid
+1 JMP 433 Sporemound
+1 JMP 435 Sylvan Ranger
+1 JMP 444 Wildheart Invoker
+1 M21 177 Cultivate
+1 JMP 386 Crushing Canopy
+1 JMP 390 Elemental Uprising
+6 JMP 74 Forest
+1 JMP 34 Thriving Grove
+1 JMP 71 Forest
+
+# Lightning (1)
+1 JMP 371 Weaver of Lightning
+1 JMP 344 Lightning Elemental
+1 JMP 345 Lightning Shrieker
+1 JMP 335 Homing Lightning
+1 JMP 341 Lightning Axe
+1 JMP 342 Lightning Bolt
+1 JMP 343 Lightning Diadem
+6 JMP 64 Mountain
+1 JMP 21 Lightning Phoenix
+1 JMP 23 Living Lightning
+2 JMP 22 Lightning Visionary
+1 JMP 32 Lightning-Core Excavator
+1 JMP 33 Thriving Bluff
+1 JMP 68 Mountain
+
+# Lightning (2)
+1 JMP 291 Ball Lightning
+1 JMP 371 Weaver of Lightning
+1 JMP 344 Lightning Elemental
+1 JMP 302 Chain Lightning
+1 JMP 341 Lightning Axe
+1 JMP 359 Riddle of Lightning
+1 JMP 343 Lightning Diadem
+6 JMP 64 Mountain
+1 JMP 21 Lightning Phoenix
+1 JMP 23 Living Lightning
+2 JMP 22 Lightning Visionary
+1 JMP 32 Lightning-Core Excavator
+1 JMP 33 Thriving Bluff
+1 JMP 68 Mountain
+
+# Liliana
+1 JMP 250 Liliana's Elite
+1 JMP 205 Blighted Bat
+1 JMP 238 Ghoulraiser
+1 JMP 286 Wailing Ghoul
+1 JMP 276 Settle the Score
+1 JMP 217 Cemetery Recruitment
+7 JMP 57 Swamp
+1 JMP 37 Thriving Moor
+1 M21 297 Liliana, Waker of the Dead
+1 M21 110 Liliana's Standard Bearer
+1 M21 101 Goremand
+1 M21 109 Liliana's Devotee
+1 M21 119 Rise Again
+1 M21 300 Liliana's Steward
+
+# Minions (1)
+1 JMP 237 Ghoulcaller's Accomplice
+1 JMP 212 Bone Picker
+1 JMP 226 Dutiful Attendant
+1 JMP 277 Shambling Goblin
+1 JMP 244 Innocent Blood
+6 JMP 57 Swamp
+1 JMP 15 Kels, Fight Fixer
+1 JMP 16 Nocturnal Feeder
+1 JMP 37 Thriving Moor
+1 JMP 54 Swamp
+1 M21 129 Witch's Cauldron
+1 M21 97 Eliminate
+1 M21 101 Goremand
+1 M21 93 Crypt Lurker
+1 M21 126 Village Rites
+
+# Minions (2)
+1 JMP 228 Eternal Taskmaster
+1 JMP 224 Drainpipe Vermin
+1 JMP 226 Dutiful Attendant
+1 JMP 237 Ghoulcaller's Accomplice
+1 JMP 213 Bone Splinters
+6 JMP 57 Swamp
+1 JMP 15 Kels, Fight Fixer
+1 JMP 16 Nocturnal Feeder
+1 JMP 37 Thriving Moor
+1 JMP 54 Swamp
+1 M21 129 Witch's Cauldron
+1 M21 97 Eliminate
+1 M21 101 Goremand
+1 M21 93 Crypt Lurker
+1 M21 126 Village Rites
+
+# Minions (3)
+1 JMP 226 Dutiful Attendant
+1 JMP 224 Drainpipe Vermin
+1 JMP 237 Ghoulcaller's Accomplice
+1 M21 98 Fetid Imp
+1 JMP 248 Launch Party
+1 JMP 493 Phyrexian Tower
+5 JMP 57 Swamp
+1 JMP 16 Nocturnal Feeder
+1 JMP 37 Thriving Moor
+1 JMP 54 Swamp
+1 M21 129 Witch's Cauldron
+1 M21 97 Eliminate
+1 M21 101 Goremand
+1 M21 109 Liliana's Devotee
+1 M21 93 Crypt Lurker
+1 M21 126 Village Rites
+
+# Minions (4)
+1 JMP 236 Ghoulcaller Gisa
+1 JMP 226 Dutiful Attendant
+1 JMP 237 Ghoulcaller's Accomplice
+1 JMP 277 Shambling Goblin
+1 M21 98 Fetid Imp
+1 JMP 244 Innocent Blood
+6 JMP 57 Swamp
+1 JMP 16 Nocturnal Feeder
+1 JMP 37 Thriving Moor
+1 JMP 54 Swamp
+1 M21 129 Witch's Cauldron
+1 M21 97 Eliminate
+1 M21 101 Goremand
+1 M21 109 Liliana's Devotee
+1 M21 126 Village Rites
+
+# Phyrexian
+1 JMP 278 Sheoldred, Whispering One
+1 JMP 227 Entomber Exarch
+1 JMP 265 Phyrexian Gargantua
+1 JMP 475 Myr Sire
+1 JMP 476 Perilous Myr
+1 JMP 263 Phyrexian Broodlings
+1 JMP 264 Phyrexian Debaser
+1 JMP 266 Phyrexian Rager
+1 JMP 223 Douse in Gloom
+1 JMP 267 Phyrexian Reclamation
+1 JMP 262 Parasitic Implant
+6 JMP 57 Swamp
+1 JMP 37 Thriving Moor
+1 JMP 58 Swamp
+1 M21 112 Malefic Scythe
+
+# Pirates (1)
+1 JMP 170 Rishadan Airship
+1 JMP 149 Departed Deckhand
+1 JMP 178 Spectral Sailor
+1 JMP 155 Kitesail Corsair
+1 JMP 165 Prosperous Pirates
+1 JMP 172 Sailor of Means
+1 JMP 142 Chart a Course
+1 JMP 189 Voyage's End
+1 JMP 477 Pirate's Cutlass
+1 JMP 144 Coastal Piracy
+1 JMP 192 Waterknot
+6 JMP 50 Island
+1 JMP 11 Corsair Captain
+1 JMP 36 Thriving Isle
+1 JMP 52 Island
+
+# Pirates (2)
+1 JMP 149 Departed Deckhand
+1 JMP 178 Spectral Sailor
+1 JMP 155 Kitesail Corsair
+1 JMP 165 Prosperous Pirates
+1 JMP 170 Rishadan Airship
+1 JMP 172 Sailor of Means
+1 JMP 142 Chart a Course
+1 JMP 477 Pirate's Cutlass
+1 JMP 148 Curious Obsession
+1 M21 47 Capture Sphere
+6 JMP 50 Island
+1 JMP 11 Corsair Captain
+1 JMP 36 Thriving Isle
+1 JMP 52 Island
+1 M21 62 Read the Tides
+
+# Predatory (1)
+1 JMP 431 Somberwald Stag
+1 JMP 434 Sylvan Brushstrider
+1 JMP 380 Brindle Shoat
+1 JMP 387 Dawntreader Elk
+1 JMP 406 Irresistible Prey
+1 JMP 438 Time to Feed
+1 JMP 386 Crushing Canopy
+1 JMP 473 Marauder's Axe
+6 JMP 74 Forest
+1 JMP 30 Neyith of the Dire Hunt
+1 JMP 34 Thriving Grove
+1 JMP 76 Forest
+1 M21 182 Fungal Rebirth
+1 M21 212 Trufflesnout
+1 M21 202 Sabertooth Mauler
+
+# Predatory (2)
+1 JMP 466 Gingerbrute
+1 JMP 373 Affectionate Indrik
+1 JMP 380 Brindle Shoat
+1 JMP 387 Dawntreader Elk
+1 JMP 406 Irresistible Prey
+1 JMP 438 Time to Feed
+1 JMP 386 Crushing Canopy
+1 JMP 473 Marauder's Axe
+6 JMP 74 Forest
+1 JMP 30 Neyith of the Dire Hunt
+1 JMP 34 Thriving Grove
+1 JMP 76 Forest
+1 M21 182 Fungal Rebirth
+1 M21 212 Trufflesnout
+1 M21 202 Sabertooth Mauler
+
+# Predatory (3)
+1 JMP 436 Thragtusk
+1 JMP 431 Somberwald Stag
+1 JMP 380 Brindle Shoat
+1 JMP 387 Dawntreader Elk
+1 JMP 466 Gingerbrute
+1 JMP 406 Irresistible Prey
+1 JMP 438 Time to Feed
+1 JMP 386 Crushing Canopy
+1 JMP 473 Marauder's Axe
+6 JMP 74 Forest
+1 JMP 34 Thriving Grove
+1 JMP 76 Forest
+1 M21 182 Fungal Rebirth
+1 M21 212 Trufflesnout
+1 M21 202 Sabertooth Mauler
+
+# Predatory (4)
+1 JMP 424 Ravenous Baloth
+1 JMP 373 Affectionate Indrik
+1 JMP 380 Brindle Shoat
+1 JMP 387 Dawntreader Elk
+1 JMP 434 Sylvan Brushstrider
+1 JMP 406 Irresistible Prey
+1 JMP 438 Time to Feed
+1 JMP 411 Momentous Fall
+1 JMP 386 Crushing Canopy
+6 JMP 74 Forest
+1 JMP 34 Thriving Grove
+1 JMP 76 Forest
+1 M21 182 Fungal Rebirth
+1 M21 212 Trufflesnout
+1 M21 202 Sabertooth Mauler
+
+# Rainbow
+1 JMP 454 Maelstrom Archangel
+1 JMP 461 Chamber Sentry
+1 JMP 450 Dinrova Horror
+1 JMP 451 Fusion Elemental
+1 JMP 455 Raging Regisaur
+1 JMP 452 Ironroot Warlord
+1 JMP 457 Alloy Myr
+1 JMP 486 Skittering Surveyor
+1 M21 235 Prismite
+1 JMP 449 Auger Spree
+1 JMP 478 Prophetic Prism
+1 JMP 453 Lawmage's Binding
+1 JMP 492 Mirrodin's Core
+1 JMP 495 Rupture Spire
+1 JMP 74 Forest
+1 JMP 50 Island
+1 JMP 64 Mountain
+1 JMP 45 Plains
+1 JMP 57 Swamp
+1 JMP 78 Terramorphic Expanse
+
+# Reanimated (1)
+1 JMP 257 Mire Triton
+1 JMP 215 Cadaver Imp
+1 JMP 221 Crow of Dark Tidings
+1 JMP 256 Miasmic Mummy
+1 JMP 284 Tithebearer Giant
+1 JMP 270 Reanimate
+1 JMP 235 Funeral Rites
+6 JMP 57 Swamp
+1 JMP 37 Thriving Moor
+1 JMP 56 Swamp
+1 M21 88 Archfiend's Vessel
+1 M21 101 Goremand
+1 M21 97 Eliminate
+1 M21 93 Crypt Lurker
+1 M21 119 Rise Again
+
+# Reanimated (2)
+1 JMP 257 Mire Triton
+1 JMP 221 Crow of Dark Tidings
+1 JMP 256 Miasmic Mummy
+1 JMP 270 Reanimate
+1 JMP 235 Funeral Rites
+1 JMP 252 Macabre Waltz
+6 JMP 57 Swamp
+1 JMP 37 Thriving Moor
+1 JMP 56 Swamp
+1 M21 92 Carrion Grub
+1 M21 101 Goremand
+1 M21 97 Eliminate
+1 M21 93 Crypt Lurker
+1 M21 119 Rise Again
+1 M21 100 Gloom Sower
+
+# Reanimated (3)
+1 JMP 241 Gravewaker
+1 JMP 274 Scourge of Nel Toth
+1 JMP 257 Mire Triton
+1 JMP 221 Crow of Dark Tidings
+1 JMP 256 Miasmic Mummy
+1 JMP 230 Exhume
+1 JMP 235 Funeral Rites
+1 JMP 288 Zombie Infestation
+6 JMP 57 Swamp
+1 JMP 37 Thriving Moor
+1 JMP 56 Swamp
+1 M21 101 Goremand
+1 M21 97 Eliminate
+1 M21 93 Crypt Lurker
+1 M21 119 Rise Again
+
+# Reanimated (4)
+1 JMP 257 Mire Triton
+1 JMP 221 Crow of Dark Tidings
+1 JMP 256 Miasmic Mummy
+1 JMP 271 Rise of the Dark Realms
+1 JMP 235 Funeral Rites
+1 JMP 280 Soul Salvage
+6 JMP 57 Swamp
+1 JMP 37 Thriving Moor
+1 JMP 56 Swamp
+1 M21 92 Carrion Grub
+1 M21 101 Goremand
+1 M21 97 Eliminate
+1 M21 93 Crypt Lurker
+1 M21 119 Rise Again
+1 M21 100 Gloom Sower
+
+# Seismic
+1 JMP 331 Grim Lavamancer
+1 JMP 304 Cinder Elemental
+1 JMP 362 Seismic Elemental
+1 JMP 290 Ashmouth Hound
+1 JMP 297 Bloodrock Cyclops
+1 JMP 351 Molten Ravager
+1 JMP 364 Spitting Earth
+1 JMP 347 Magmaquake
+1 JMP 346 Magma Jet
+1 JMP 368 Volcanic Fallout
+1 M21 171 Volcanic Geyser
+1 JMP 472 Mana Geode
+6 JMP 64 Mountain
+1 JMP 33 Thriving Bluff
+1 JMP 64 Mountain
+
+# Teferi
+1 JMP 164 Prescient Chimera
+1 JMP 171 Sage's Row Savant
+1 M21 83 Vodalian Arcanist
+1 JMP 182 Talrand's Invocation
+1 JMP 152 Exclude
+1 JMP 156 Leave in the Dust
+1 M21 59 Opt
+7 JMP 50 Island
+1 JMP 36 Thriving Isle
+1 M21 290 Teferi, Master of Time
+1 M21 76 Teferi's Ageless Insight
+1 M21 80 Tolarian Kraken
+1 M21 78 Teferi's Tutelage
+1 M21 295 Teferi's Protege
+
+# Tree-Hugging (1)
+1 JMP 422 Primordial Sage
+1 JMP 442 Wall of Blossoms
+1 JMP 375 Ambassador Oak
+1 M21 207 Snarespinner
+1 JMP 412 Nature's Way
+1 JMP 393 Explore
+1 JMP 374 Aggressive Urge
+1 JMP 386 Crushing Canopy
+6 JMP 74 Forest
+1 JMP 34 Thriving Grove
+1 JMP 70 Forest
+1 M21 174 Burlfist Oak
+1 M21 213 Warden of the Woods
+1 M21 193 Llanowar Visionary
+1 M21 187 Gnarled Sage
+
+# Tree-Hugging (2)
+1 JMP 422 Primordial Sage
+1 JMP 442 Wall of Blossoms
+1 JMP 375 Ambassador Oak
+1 M21 207 Snarespinner
+1 JMP 412 Nature's Way
+1 JMP 393 Explore
+1 JMP 386 Crushing Canopy
+1 JMP 445 Wildsize
+6 JMP 74 Forest
+1 JMP 34 Thriving Grove
+1 JMP 70 Forest
+1 M21 174 Burlfist Oak
+1 M21 213 Warden of the Woods
+1 M21 193 Llanowar Visionary
+1 M21 187 Gnarled Sage
+
+# Tree-Hugging (3)
+1 JMP 442 Wall of Blossoms
+1 JMP 375 Ambassador Oak
+1 M21 207 Snarespinner
+1 JMP 412 Nature's Way
+1 JMP 393 Explore
+1 JMP 374 Aggressive Urge
+1 JMP 386 Crushing Canopy
+6 JMP 74 Forest
+1 JMP 34 Thriving Grove
+1 JMP 70 Forest
+1 M21 376 Jolrael, Mwonvuli Recluse
+1 M21 174 Burlfist Oak
+1 M21 213 Warden of the Woods
+1 M21 193 Llanowar Visionary
+1 M21 187 Gnarled Sage
+
+# Tree-Hugging (4)
+1 JMP 432 Soul of the Harvest
+1 JMP 442 Wall of Blossoms
+1 M21 207 Snarespinner
+1 JMP 412 Nature's Way
+1 JMP 393 Explore
+1 JMP 386 Crushing Canopy
+1 JMP 445 Wildsize
+1 JMP 441 Verdant Embrace
+6 JMP 74 Forest
+1 JMP 34 Thriving Grove
+1 JMP 70 Forest
+1 M21 174 Burlfist Oak
+1 M21 213 Warden of the Woods
+1 M21 193 Llanowar Visionary
+1 M21 187 Gnarled Sage
+
+# Under the Sea (1)
+1 JMP 177 Sigiled Starfish
+1 JMP 161 Octoprophet
+1 JMP 197 Wishful Merfolk
+1 JMP 138 Aegis Turtle
+1 JMP 189 Voyage's End
+1 JMP 489 Unstable Obelisk
+1 JMP 192 Waterknot
+7 JMP 50 Island
+1 JMP 36 Thriving Isle
+1 JMP 46 Island
+1 M21 60 Pursued Whale
+1 M21 80 Tolarian Kraken
+1 M21 84 Waker of Waves
+1 M21 67 Rousing Read
+
+# Under the Sea (2)
+1 JMP 146 Cryptic Serpent
+1 JMP 177 Sigiled Starfish
+1 JMP 161 Octoprophet
+1 JMP 197 Wishful Merfolk
+1 JMP 138 Aegis Turtle
+1 JMP 193 Whelming Wave
+1 JMP 180 Sweep Away
+1 JMP 489 Unstable Obelisk
+1 JMP 192 Waterknot
+7 JMP 50 Island
+1 JMP 36 Thriving Isle
+1 JMP 46 Island
+1 M21 60 Pursued Whale
+1 M21 84 Waker of Waves
+
+# Unicorns
+1 JMP 112 Inspiring Unicorn
+1 JMP 122 Mesa Unicorn
+1 JMP 131 Ronom Unicorn
+1 JMP 136 Valorous Stance
+1 JMP 97 Cloudshift
+1 JMP 110 Inspired Charge
+1 M21 17 Faith's Fetters
+6 JMP 45 Plains
+1 JMP 3 Emiel the Blessed
+1 JMP 1 Blessed Sanctuary
+1 JMP 2 Brightmare
+1 JMP 35 Thriving Heath
+1 JMP 41 Plains
+1 M21 42 Valorous Steed
+1 M21 14 Daybreak Charger
+
+# Vampires (1)
+1 JMP 206 Blood Artist
+1 JMP 209 Bloodbond Vampire
+1 JMP 239 Gifted Aetherborn
+1 JMP 245 Kalastria Nightwatch
+1 JMP 199 Agonizing Syphon
+1 JMP 247 Last Gasp
+1 JMP 229 Eternal Thirst
+6 JMP 57 Swamp
+1 JMP 16 Nocturnal Feeder
+1 JMP 37 Thriving Moor
+1 JMP 60 Swamp
+1 M21 362 Vito, Thorn of the Dusk Rose
+1 M21 122 Silversmote Ghoul
+1 M21 121 Sanguine Indulgence
+1 M21 100 Gloom Sower
+
+# Vampires (2)
+1 JMP 208 Blood Host
+1 JMP 209 Bloodbond Vampire
+1 JMP 239 Gifted Aetherborn
+1 JMP 218 Child of Night
+1 JMP 199 Agonizing Syphon
+1 JMP 247 Last Gasp
+1 JMP 229 Eternal Thirst
+6 JMP 57 Swamp
+1 JMP 16 Nocturnal Feeder
+1 JMP 37 Thriving Moor
+1 JMP 60 Swamp
+1 M21 362 Vito, Thorn of the Dusk Rose
+1 M21 122 Silversmote Ghoul
+1 M21 121 Sanguine Indulgence
+1 M21 100 Gloom Sower
+
+# Vampires (3)
+1 JMP 272 Sangromancer
+1 JMP 209 Bloodbond Vampire
+1 JMP 239 Gifted Aetherborn
+1 JMP 275 Sengir Vampire
+1 JMP 285 Vampire Neonate
+1 JMP 199 Agonizing Syphon
+1 JMP 247 Last Gasp
+1 JMP 231 Exquisite Blood
+6 JMP 57 Swamp
+1 JMP 16 Nocturnal Feeder
+1 JMP 37 Thriving Moor
+1 JMP 60 Swamp
+1 M21 122 Silversmote Ghoul
+1 M21 121 Sanguine Indulgence
+1 M21 100 Gloom Sower
+
+# Vampires (4)
+1 JMP 225 Drana, Liberator of Malakir
+1 JMP 209 Bloodbond Vampire
+1 JMP 232 Falkenrath Noble
+1 JMP 239 Gifted Aetherborn
+1 JMP 218 Child of Night
+1 JMP 199 Agonizing Syphon
+1 JMP 247 Last Gasp
+1 JMP 254 Mark of the Vampire
+6 JMP 57 Swamp
+1 JMP 16 Nocturnal Feeder
+1 JMP 37 Thriving Moor
+1 JMP 60 Swamp
+1 M21 122 Silversmote Ghoul
+1 M21 121 Sanguine Indulgence
+1 M21 100 Gloom Sower
+
+# Walls
+1 JMP 442 Wall of Blossoms
+1 JMP 382 Carven Caryatid
+1 JMP 465 Gargoyle Sentinel
+1 JMP 417 Overgrown Battlement
+1 JMP 401 Grave Bramble
+1 JMP 443 Wall of Vines
+1 JMP 480 Roving Keep
+1 M21 195 Portcullis Vine
+1 JMP 386 Crushing Canopy
+1 JMP 490 Warmonger's Chariot
+1 JMP 378 Assault Formation
+6 JMP 74 Forest
+1 JMP 31 Towering Titan
+1 JMP 34 Thriving Grove
+1 JMP 75 Forest
+
+# Well-Read (1)
+1 JMP 487 Suspicious Bookcase
+1 JMP 162 Oneirophage
+1 JMP 150 Erratic Visionary
+1 JMP 481 Runed Servitor
+1 M21 59 Opt
+1 JMP 147 Curiosity
+1 JMP 159 Narcolepsy
+6 JMP 50 Island
+1 JMP 13 Ormos, Archive Keeper
+1 JMP 36 Thriving Isle
+1 JMP 53 Island
+1 M21 80 Tolarian Kraken
+1 M21 81 Tome Anima
+1 M21 55 Library Larcenist
+1 M21 67 Rousing Read
+
+# Well-Read (2)
+1 JMP 162 Oneirophage
+1 JMP 150 Erratic Visionary
+1 JMP 481 Runed Servitor
+1 M21 59 Opt
+1 JMP 459 Arcane Encyclopedia
+1 JMP 147 Curiosity
+1 M21 47 Capture Sphere
+6 JMP 50 Island
+1 JMP 13 Ormos, Archive Keeper
+1 JMP 36 Thriving Isle
+1 JMP 53 Island
+1 M21 80 Tolarian Kraken
+1 M21 81 Tome Anima
+1 M21 55 Library Larcenist
+1 M21 67 Rousing Read
+
+# Well-Read (3)
+1 JMP 162 Oneirophage
+1 JMP 487 Suspicious Bookcase
+1 JMP 143 Cloudreader Sphinx
+1 JMP 150 Erratic Visionary
+1 JMP 481 Runed Servitor
+1 M21 59 Opt
+1 JMP 169 Rhystic Study
+1 JMP 147 Curiosity
+1 JMP 159 Narcolepsy
+6 JMP 50 Island
+1 JMP 36 Thriving Isle
+1 JMP 53 Island
+1 M21 80 Tolarian Kraken
+1 M21 81 Tome Anima
+1 M21 55 Library Larcenist
+
+# Well-Read (4)
+1 JMP 158 Mystic Archaeologist
+1 JMP 162 Oneirophage
+1 JMP 487 Suspicious Bookcase
+1 JMP 143 Cloudreader Sphinx
+1 JMP 481 Runed Servitor
+1 JMP 167 Read the Runes
+1 M21 59 Opt
+1 JMP 147 Curiosity
+1 M21 47 Capture Sphere
+6 JMP 50 Island
+1 JMP 36 Thriving Isle
+1 JMP 53 Island
+1 M21 80 Tolarian Kraken
+1 M21 81 Tome Anima
+1 M21 55 Library Larcenist
+
+# Witchcraft (1)
+1 JMP 211 Bogbrew Witch
+1 JMP 253 Malakir Familiar
+1 JMP 283 Tempting Witch
+1 JMP 210 Bloodhunter Bat
+1 JMP 216 Cauldron Familiar
+2 JMP 234 Festering Newt
+1 JMP 201 Bake into a Pie
+1 JMP 460 Bubbling Cauldron
+6 JMP 57 Swamp
+1 JMP 18 Witch of the Moors
+1 JMP 37 Thriving Moor
+1 JMP 59 Swamp
+1 M21 129 Witch's Cauldron
+1 M21 99 Finishing Blow
+
+# Witchcraft (2)
+1 JMP 283 Tempting Witch
+1 JMP 211 Bogbrew Witch
+1 JMP 282 Swarm of Bloodflies
+1 JMP 203 Black Cat
+1 JMP 210 Bloodhunter Bat
+2 JMP 234 Festering Newt
+1 JMP 207 Blood Divination
+1 JMP 201 Bake into a Pie
+1 JMP 247 Last Gasp
+1 JMP 460 Bubbling Cauldron
+6 JMP 57 Swamp
+1 JMP 18 Witch of the Moors
+1 JMP 37 Thriving Moor
+1 JMP 59 Swamp
+
+# Wizards (1)
+1 JMP 181 Talrand, Sky Summoner
+1 JMP 153 Exclusion Mage
+1 JMP 171 Sage's Row Savant
+1 M21 83 Vodalian Arcanist
+1 JMP 182 Talrand's Invocation
+1 JMP 196 Winged Words
+1 JMP 198 Wizard's Retort
+1 JMP 140 Befuddle
+1 M21 51 Frost Breath
+1 M21 59 Opt
+6 JMP 50 Island
+1 JMP 36 Thriving Isle
+1 JMP 47 Island
+1 M21 71 Shipwreck Dowser
+1 M21 62 Read the Tides
+
+# Wizards (2)
+1 JMP 153 Exclusion Mage
+1 JMP 145 Crookclaw Transmuter
+1 JMP 171 Sage's Row Savant
+1 JMP 173 Sea Gate Oracle
+1 M21 83 Vodalian Arcanist
+1 JMP 182 Talrand's Invocation
+1 JMP 196 Winged Words
+1 JMP 198 Wizard's Retort
+1 M21 59 Opt
+1 JMP 494 Riptide Laboratory
+5 JMP 50 Island
+1 JMP 36 Thriving Isle
+1 JMP 47 Island
+1 M21 71 Shipwreck Dowser
+1 M21 66 Rookie Mistake
+1 M21 62 Read the Tides
+
+# Wizards (3)
+1 JMP 181 Talrand, Sky Summoner
+1 JMP 171 Sage's Row Savant
+1 M21 83 Vodalian Arcanist
+1 JMP 182 Talrand's Invocation
+1 JMP 196 Winged Words
+1 JMP 198 Wizard's Retort
+1 M21 82 Unsubstantiate
+1 M21 59 Opt
+1 M21 47 Capture Sphere
+6 JMP 50 Island
+1 JMP 36 Thriving Isle
+1 JMP 47 Island
+1 M21 71 Shipwreck Dowser
+1 M21 62 Read the Tides
+1 M21 295 Teferi's Protege
+
+# Wizards (4)
+1 JMP 171 Sage's Row Savant
+1 JMP 179 Storm Sculptor
+1 M21 83 Vodalian Arcanist
+1 JMP 182 Talrand's Invocation
+1 JMP 198 Wizard's Retort
+1 JMP 163 Peel from Reality
+1 M21 61 Rain of Revelation
+1 M21 51 Frost Breath
+6 JMP 50 Island
+1 JMP 36 Thriving Isle
+1 JMP 47 Island
+1 M21 348 Barrin, Tolarian Archmage
+1 M21 71 Shipwreck Dowser
+1 M21 62 Read the Tides
+1 M21 295 Teferi's Protege

From 6e1da09023eea715e5572246bef0e6fec9d2dc76 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Tue, 23 Jun 2020 00:59:17 +0400
Subject: [PATCH 460/586] * Morph ability - fixed that card with morph ability
 marked as playable all the time (#6680);

---
 .../cards/abilities/keywords/MorphTest.java   | 72 ++++++++++++++++--
 .../main/java/mage/abilities/AbilityImpl.java | 42 +++++-----
 .../main/java/mage/players/PlayerImpl.java    | 76 +++++++++----------
 3 files changed, 119 insertions(+), 71 deletions(-)

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MorphTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MorphTest.java
index 253339652e..7e3333567a 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MorphTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MorphTest.java
@@ -636,15 +636,14 @@ public class MorphTest extends CardTestPlayerBase {
      * restriction."
      */
     @Test
-    public void testReflectorMageBouncesFaceupCreatureReplayAsMorph() {
-
+    public void test_ReflectorMageCantStopMorphToCast_TryNormalCast() {
         // {1}{W}{U} When Reflector Mage enters the battlefield, return target creature an opponent controls to its owner's hand.
         // That creature's owner can't cast spells with the same name as that creature until your next turn.
         addCard(Zone.HAND, playerA, "Reflector Mage"); // 2/3
         addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
         addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
 
-        //Tap: Add {G}, {U}, or {R}.
+        // Tap: Add {G}, {U}, or {R}.
         // Morph 2 (You may cast this card face down as a 2/2 creature for 3. Turn it face up any time for its morph cost.)
         // When Rattleclaw Mystic is turned face up, add {G}{U}{R}.
         addCard(Zone.BATTLEFIELD, playerB, "Rattleclaw Mystic"); // 2/1
@@ -652,20 +651,58 @@ public class MorphTest extends CardTestPlayerBase {
         addCard(Zone.BATTLEFIELD, playerB, "Island");
         addCard(Zone.BATTLEFIELD, playerB, "Mountain");
 
+        // return to hand
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Reflector Mage");
         addTarget(playerA, "Rattleclaw Mystic");
 
+        // try cast as normal -- must not work
         castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Rattleclaw Mystic");
-        setChoice(playerB, "Yes"); // cast it face down as 2/2 creature
+        setChoice(playerB, "No"); // try cast as normal
 
+        //setStrictChooseMode(true); // no strict mode - cause can't cast as normal
         setStopAt(2, PhaseStep.BEGIN_COMBAT);
-
         execute();
+        //assertAllCommandsUsed();
 
         assertPermanentCount(playerA, "Reflector Mage", 1);
         assertPermanentCount(playerB, "Rattleclaw Mystic", 0);
-        assertHandCount(playerB, "Rattleclaw Mystic", 0); // should have been replayed
-        assertPermanentCount(playerB, EmptyNames.FACE_DOWN_CREATURE.toString(), 1); // Rattleclaw played as a morph
+        assertHandCount(playerB, "Rattleclaw Mystic", 1); // can't play
+        assertPermanentCount(playerB, EmptyNames.FACE_DOWN_CREATURE.toString(), 0); // don't try as morph
+    }
+
+    @Test
+    public void test_ReflectorMageCantStopMorphToCast_TryMorph() {
+        // {1}{W}{U} When Reflector Mage enters the battlefield, return target creature an opponent controls to its owner's hand.
+        // That creature's owner can't cast spells with the same name as that creature until your next turn.
+        addCard(Zone.HAND, playerA, "Reflector Mage"); // 2/3
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
+
+        // Tap: Add {G}, {U}, or {R}.
+        // Morph 2 (You may cast this card face down as a 2/2 creature for 3. Turn it face up any time for its morph cost.)
+        // When Rattleclaw Mystic is turned face up, add {G}{U}{R}.
+        addCard(Zone.BATTLEFIELD, playerB, "Rattleclaw Mystic"); // 2/1
+        addCard(Zone.BATTLEFIELD, playerB, "Forest");
+        addCard(Zone.BATTLEFIELD, playerB, "Island");
+        addCard(Zone.BATTLEFIELD, playerB, "Mountain");
+
+        // return to hand
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Reflector Mage");
+        addTarget(playerA, "Rattleclaw Mystic");
+
+        // try cast as morph - must work
+        castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Rattleclaw Mystic");
+        setChoice(playerB, "Yes"); // try cast as morph
+
+        setStrictChooseMode(true);
+        setStopAt(2, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerA, "Reflector Mage", 1);
+        assertPermanentCount(playerB, "Rattleclaw Mystic", 0);
+        assertHandCount(playerB, "Rattleclaw Mystic", 0); // able cast as morph
+        assertPermanentCount(playerB, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
     }
 
     /**
@@ -1007,4 +1044,25 @@ public class MorphTest extends CardTestPlayerBase {
         assertPermanentCount(playerA, "Zoetic Cavern", 0);
         assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
     }
+
+    @Test
+    public void test_CantActivateOnOpponentTurn() {
+        // https://github.com/magefree/mage/issues/6698
+
+        // Morph {1}{U} (You may cast this card face down as a 2/2 creature for {3}. Turn it face up any time for its morph cost.)
+        // When Willbender is turned face up, change the target of target spell or ability with a single target.
+        addCard(Zone.HAND, playerA, "Willbender");
+        addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
+
+        // can play on own turn
+        checkPlayableAbility("can", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Willbender", true);
+
+        // can't play on opponent turn
+        checkPlayableAbility("can't", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Willbender", false);
+
+        setStrictChooseMode(true);
+        setStopAt(2, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+    }
 }
diff --git a/Mage/src/main/java/mage/abilities/AbilityImpl.java b/Mage/src/main/java/mage/abilities/AbilityImpl.java
index 62a2d3fea1..4a053e6a86 100644
--- a/Mage/src/main/java/mage/abilities/AbilityImpl.java
+++ b/Mage/src/main/java/mage/abilities/AbilityImpl.java
@@ -425,36 +425,28 @@ public abstract class AbilityImpl implements Ability {
             }
         }
 
-        boolean alternativeCostisUsed = false;
+        boolean alternativeCostUsed = false;
         if (sourceObject != null && !(sourceObject instanceof Permanent)) {
-            Abilities<Ability> abilities = null;
-            if (sourceObject instanceof Card) {
-                abilities = ((Card) sourceObject).getAbilities(game);
-            } else {
-                sourceObject.getAbilities();
-            }
-
-            if (abilities != null) {
-                for (Ability ability : abilities) {
-                    // if cast for noMana no Alternative costs are allowed
-                    if (canUseAlternativeCost && !noMana && ability instanceof AlternativeSourceCosts) {
-                        AlternativeSourceCosts alternativeSpellCosts = (AlternativeSourceCosts) ability;
-                        if (alternativeSpellCosts.isAvailable(this, game)) {
-                            if (alternativeSpellCosts.askToActivateAlternativeCosts(this, game)) {
-                                // only one alternative costs may be activated
-                                alternativeCostisUsed = true;
-                                break;
-                            }
+            Abilities<Ability> abilities = CardUtil.getAbilities(sourceObject, game);
+            for (Ability ability : abilities) {
+                // if cast for noMana no Alternative costs are allowed
+                if (canUseAlternativeCost && !noMana && ability instanceof AlternativeSourceCosts) {
+                    AlternativeSourceCosts alternativeSpellCosts = (AlternativeSourceCosts) ability;
+                    if (alternativeSpellCosts.isAvailable(this, game)) {
+                        if (alternativeSpellCosts.askToActivateAlternativeCosts(this, game)) {
+                            // only one alternative costs may be activated
+                            alternativeCostUsed = true;
+                            break;
                         }
                     }
-                    if (canUseAdditionalCost && ability instanceof OptionalAdditionalSourceCosts) {
-                        ((OptionalAdditionalSourceCosts) ability).addOptionalAdditionalCosts(this, game);
-                    }
+                }
+                if (canUseAdditionalCost && ability instanceof OptionalAdditionalSourceCosts) {
+                    ((OptionalAdditionalSourceCosts) ability).addOptionalAdditionalCosts(this, game);
                 }
             }
 
             // controller specific alternate spell costs
-            if (canUseAlternativeCost && !noMana && !alternativeCostisUsed) {
+            if (canUseAlternativeCost && !noMana && !alternativeCostUsed) {
                 if (this.getAbilityType() == AbilityType.SPELL
                         // 117.9a Only one alternative cost can be applied to any one spell as it's being cast.
                         // So an alternate spell ability can't be paid with Omniscience
@@ -463,7 +455,7 @@ public abstract class AbilityImpl implements Ability {
                         if (alternativeSourceCosts.isAvailable(this, game)) {
                             if (alternativeSourceCosts.askToActivateAlternativeCosts(this, game)) {
                                 // only one alternative costs may be activated
-                                alternativeCostisUsed = true;
+                                alternativeCostUsed = true;
                                 break;
                             }
                         }
@@ -472,7 +464,7 @@ public abstract class AbilityImpl implements Ability {
             }
         }
 
-        return alternativeCostisUsed;
+        return alternativeCostUsed;
     }
 
     /**
diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java
index 5ac203e08e..d2066015ee 100644
--- a/Mage/src/main/java/mage/players/PlayerImpl.java
+++ b/Mage/src/main/java/mage/players/PlayerImpl.java
@@ -3140,54 +3140,52 @@ public abstract class PlayerImpl implements Player, Serializable {
     }
 
     protected ActivatedAbility findActivatedAbilityFromAlternativeSourceCost(MageObject object, ManaOptions manaAvailable, Ability ability, Game game) {
-        // alternative cost must be replaced by real play ability
-        if (ability instanceof AlternativeSourceCosts) {
-            AlternativeSourceCosts altAbility = (AlternativeSourceCosts) ability;
+        // return play ability that can activate AlternativeSourceCosts
+        if (ability instanceof AlternativeSourceCosts && !(object instanceof Permanent)) {
+            ActivatedAbility playAbility = null;
             if (object.isLand()) {
-                // land
-                // morph ability is static, so it must be replaced with play land ability (playLand search and try to use face down first)
-                if (canLandPlayAlternateSourceCostsAbility(object, manaAvailable, ability, game)) { // e.g. Land with Morph
-                    Ability landAbility = CardUtil.getAbilities(object, game).stream().filter(a -> a instanceof PlayLandAbility).findFirst().orElse(null);
-                    if (landAbility != null) {
-                        return (PlayLandAbility) landAbility;
-                    }
-                }
+                playAbility = (PlayLandAbility) CardUtil.getAbilities(object, game).stream().filter(a -> a instanceof PlayLandAbility).findFirst().orElse(null);
+            } else if (object instanceof Card) {
+                playAbility = ((Card) object).getSpellAbility();
+            }
+            if (playAbility == null) {
+                return null;
+            }
+
+            // 707.4.Objects that are cast face down are turned face down before they are put onto the stack
+            // (e.g. no lands per turn limit, no cast restrictions, another cost, etc)
+            // so morph must checks only mana payment here
+            boolean canUse;
+            if (ability instanceof MorphAbility) {
+                canUse = game.canPlaySorcery(playerId) && canPayAlternateSourceCostsAbility(object, playAbility, manaAvailable, ability, game);
             } else {
-                // creature and other
-                if (object instanceof Card) {
-                    SpellAbility spellAbility = ((Card) object).getSpellAbility();
-                    if (altAbility.isAvailable(spellAbility, game)) {
-                        return spellAbility;
-                    }
-                }
+                canUse = canPlay(playAbility, manaAvailable, object, game); // canPlay already checks alternative source costs and all conditions
+            }
+
+            if (canUse) {
+                return playAbility;
             }
         }
         return null;
     }
 
-    protected boolean canLandPlayAlternateSourceCostsAbility(MageObject sourceObject, ManaOptions available, Ability ability, Game game) {
-        if (sourceObject != null && !(sourceObject instanceof Permanent)) {
-            Ability sourceAbility = sourceObject.getAbilities().stream()
-                    .filter(landAbility -> landAbility.getAbilityType() == AbilityType.PLAY_LAND)
-                    .findFirst().orElse(null);
-
-            if (sourceAbility != null && ((AlternativeSourceCosts) ability).isAvailable(sourceAbility, game)) {
-                if (ability.getCosts().canPay(ability, sourceObject.getId(), this.getId(), game)) {
-                    ManaCostsImpl manaCosts = new ManaCostsImpl();
-                    for (Cost cost : ability.getCosts()) {
-                        if (cost instanceof ManaCost) {
-                            manaCosts.add((ManaCost) cost);
-                        }
+    protected boolean canPayAlternateSourceCostsAbility(MageObject sourceObject, Ability sourceAbility, ManaOptions available, Ability alternativeAbility, Game game) {
+        if (sourceAbility != null && ((AlternativeSourceCosts) alternativeAbility).isAvailable(sourceAbility, game)) {
+            if (alternativeAbility.getCosts().canPay(alternativeAbility, sourceObject.getId(), this.getId(), game)) {
+                ManaCostsImpl manaCosts = new ManaCostsImpl();
+                for (Cost cost : alternativeAbility.getCosts()) {
+                    if (cost instanceof ManaCost) {
+                        manaCosts.add((ManaCost) cost);
                     }
+                }
 
-                    if (manaCosts.isEmpty()) {
-                        return true;
-                    } else {
-                        for (Mana mana : manaCosts.getOptions()) {
-                            for (Mana avail : available) {
-                                if (mana.enough(avail)) {
-                                    return true;
-                                }
+                if (manaCosts.isEmpty()) {
+                    return true;
+                } else {
+                    for (Mana mana : manaCosts.getOptions()) {
+                        for (Mana avail : available) {
+                            if (mana.enough(avail)) {
+                                return true;
                             }
                         }
                     }

From df69b8d50a103820a797754ba30f87aa1877fcd6 Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Mon, 22 Jun 2020 16:48:45 -0500
Subject: [PATCH 461/586] - Fixed #6672

---
 Mage.Sets/src/mage/cards/c/ChandrasPhoenix.java | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/c/ChandrasPhoenix.java b/Mage.Sets/src/mage/cards/c/ChandrasPhoenix.java
index af3ba402a7..5efc304390 100644
--- a/Mage.Sets/src/mage/cards/c/ChandrasPhoenix.java
+++ b/Mage.Sets/src/mage/cards/c/ChandrasPhoenix.java
@@ -1,4 +1,3 @@
-
 package mage.cards.c;
 
 import java.util.UUID;
@@ -8,6 +7,7 @@ import mage.abilities.TriggeredAbilityImpl;
 import mage.abilities.effects.common.ReturnToHandSourceEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.abilities.keyword.HasteAbility;
+import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
@@ -26,7 +26,7 @@ import mage.game.stack.StackObject;
 public final class ChandrasPhoenix extends CardImpl {
 
     public ChandrasPhoenix(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{R}{R}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}{R}");
         this.subtype.add(SubType.PHOENIX);
 
         this.power = new MageInt(2);
@@ -34,9 +34,13 @@ public final class ChandrasPhoenix extends CardImpl {
 
         // Flying
         this.addAbility(FlyingAbility.getInstance());
+        
         // Haste (This creature can attack and as soon as it comes under your control.)
         this.addAbility(HasteAbility.getInstance());
-        // Whenever an opponent is dealt damage by a red instant or sorcery spell you control or by a red planeswalker you control, return Chandra's Phoenix from your graveyard to your hand.
+        
+        // Whenever an opponent is dealt damage by a red instant or sorcery spell 
+        // you control or by a red planeswalker you control, return Chandra's 
+        // Phoenix from your graveyard to your hand.
         this.addAbility(new ChandrasPhoenixTriggeredAbility());
     }
 
@@ -72,7 +76,9 @@ class ChandrasPhoenixTriggeredAbility extends TriggeredAbilityImpl {
 
     @Override
     public boolean checkTrigger(GameEvent event, Game game) {
-        if (game.getOpponents(this.controllerId).contains(event.getPlayerId())) {
+        Card chandrasPhoenix = game.getCard(this.getSourceId());
+        if (chandrasPhoenix != null
+                && game.getOpponents(chandrasPhoenix.getOwnerId()).contains(event.getPlayerId())) {
             StackObject stackObject = game.getStack().getStackObject(event.getSourceId());
             if (stackObject != null) {
                 MageObject sourceObjectDamage;
@@ -96,6 +102,7 @@ class ChandrasPhoenixTriggeredAbility extends TriggeredAbilityImpl {
 
     @Override
     public String getRule() {
-        return "Whenever an opponent is dealt damage by a red instant or sorcery spell you control or by a red planeswalker you control, return {this} from your graveyard to your hand.";
+        return "Whenever an opponent is dealt damage by a red instant or sorcery spell "
+                + "you control or by a red planeswalker you control, return {this} from your graveyard to your hand.";
     }
 }

From cdedd9c2c8d1fed377fbfd69281a5f28e968b1df Mon Sep 17 00:00:00 2001
From: arcox <10953229+arcox@users.noreply.github.com>
Date: Mon, 22 Jun 2020 23:39:44 +0000
Subject: [PATCH 462/586] Implement Radha, Heart of Keld from Core 2021 (#6690)

* Implement Radha, Heart of Keld from Core 2021

* Fix oracle text on Radha
---
 .../src/mage/cards/r/RadhaHeartOfKeld.java    | 75 +++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |  1 +
 2 files changed, 76 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/r/RadhaHeartOfKeld.java

diff --git a/Mage.Sets/src/mage/cards/r/RadhaHeartOfKeld.java b/Mage.Sets/src/mage/cards/r/RadhaHeartOfKeld.java
new file mode 100644
index 0000000000..64237b8e0e
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/r/RadhaHeartOfKeld.java
@@ -0,0 +1,75 @@
+package mage.cards.r;
+
+import mage.MageInt;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.condition.common.MyTurnCondition;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.decorator.ConditionalContinuousEffect;
+import mage.abilities.dynamicvalue.DynamicValue;
+import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
+import mage.abilities.effects.common.continuous.BoostSourceEffect;
+import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
+import mage.abilities.effects.common.continuous.LookAtTopCardOfLibraryAnyTimeEffect;
+import mage.abilities.effects.common.continuous.PlayTheTopCardEffect;
+import mage.abilities.hint.common.MyTurnHint;
+import mage.abilities.keyword.FirstStrikeAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+import mage.constants.SuperType;
+import mage.filter.FilterCard;
+import mage.filter.StaticFilters;
+import mage.filter.common.FilterLandCard;
+
+import java.util.UUID;
+
+/**
+ * @author arcox
+ */
+public final class RadhaHeartOfKeld extends CardImpl {
+    private static final FilterCard filter = new FilterLandCard("play land cards");
+
+    public RadhaHeartOfKeld(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}{G}");
+
+        this.addSuperType(SuperType.LEGENDARY);
+        this.subtype.add(SubType.ELF);
+        this.subtype.add(SubType.WARRIOR);
+        this.power = new MageInt(3);
+        this.toughness = new MageInt(3);
+
+        // As long as it's your turn, Radha, Heart of Keld has first strike.
+        this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect(
+                new GainAbilitySourceEffect(FirstStrikeAbility.getInstance(), Duration.WhileOnBattlefield),
+                MyTurnCondition.instance, "As long as it's your turn, {this} has first strike."
+        )).addHint(MyTurnHint.instance));
+
+        // You may look at the top card of your library any time, and you may play lands from the top of your library.
+        LookAtTopCardOfLibraryAnyTimeEffect lookEffect = new LookAtTopCardOfLibraryAnyTimeEffect();
+        lookEffect.overrideRuleText("You may look at the top card of your library any time, ");
+        PlayTheTopCardEffect playEffect = new PlayTheTopCardEffect(filter);
+        playEffect.overrideRuleText("and you may play lands from the top of your library");
+
+        SimpleStaticAbility lookAndPlayAbility = new SimpleStaticAbility(lookEffect);
+        lookAndPlayAbility.addEffect(playEffect);
+        this.addAbility(lookAndPlayAbility);
+
+        // 4RG: Radha gets +X/+X until end of turn, where X is the number of lands you control.
+        DynamicValue controlledLands = new PermanentsOnBattlefieldCount(StaticFilters.FILTER_CONTROLLED_PERMANENT_LANDS);
+        BoostSourceEffect bse = new BoostSourceEffect(controlledLands, controlledLands, Duration.EndOfTurn, true);
+        bse.overrideRuleText("Radha gets +X/+X until end of turn, where X is the number of lands you control");
+        this.addAbility(new SimpleActivatedAbility(bse, new ManaCostsImpl("{4}{R}{G}")));
+    }
+
+    private RadhaHeartOfKeld(final RadhaHeartOfKeld card) {
+        super(card);
+    }
+
+    @Override
+    public RadhaHeartOfKeld copy() {
+        return new RadhaHeartOfKeld(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index ce4281b2b5..22ed04e9c0 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -197,6 +197,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Prismite", 235, Rarity.COMMON, mage.cards.p.Prismite.class));
         cards.add(new SetCardInfo("Pursued Whale", 60, Rarity.RARE, mage.cards.p.PursuedWhale.class));
         cards.add(new SetCardInfo("Quirion Dryad", 198, Rarity.UNCOMMON, mage.cards.q.QuirionDryad.class));
+        cards.add(new SetCardInfo("Radha, Heart of Keld", 224, Rarity.RARE, mage.cards.r.RadhaHeartOfKeld.class));
         cards.add(new SetCardInfo("Radiant Fountain", 248, Rarity.COMMON, mage.cards.r.RadiantFountain.class));
         cards.add(new SetCardInfo("Rain of Revelation", 61, Rarity.UNCOMMON, mage.cards.r.RainOfRevelation.class));
         cards.add(new SetCardInfo("Rambunctious Mutt", 30, Rarity.COMMON, mage.cards.r.RambunctiousMutt.class));

From b6352953a8188ce55774b0ed8c3d0f6edab15cc2 Mon Sep 17 00:00:00 2001
From: arcox <10953229+arcox@users.noreply.github.com>
Date: Mon, 22 Jun 2020 23:42:00 +0000
Subject: [PATCH 463/586] Implement Nine Lives from M21 (#6706)

---
 Mage.Sets/src/mage/cards/n/NineLives.java     | 127 ++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |   1 +
 .../main/java/mage/counters/CounterType.java  |   1 +
 3 files changed, 129 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/n/NineLives.java

diff --git a/Mage.Sets/src/mage/cards/n/NineLives.java b/Mage.Sets/src/mage/cards/n/NineLives.java
new file mode 100644
index 0000000000..aa28b27648
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/n/NineLives.java
@@ -0,0 +1,127 @@
+package mage.cards.n;
+
+import mage.abilities.Ability;
+import mage.abilities.StateTriggeredAbility;
+import mage.abilities.common.LeavesBattlefieldTriggeredAbility;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.effects.PreventionEffectImpl;
+import mage.abilities.effects.common.ExileSourceEffect;
+import mage.abilities.effects.common.LoseGameSourceControllerEffect;
+import mage.abilities.keyword.HexproofAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.Zone;
+import mage.counters.CounterType;
+import mage.game.Game;
+import mage.game.events.DamageEvent;
+import mage.game.events.GameEvent;
+import mage.game.events.PreventDamageEvent;
+import mage.game.permanent.Permanent;
+import mage.players.Player;
+
+import java.util.UUID;
+
+/**
+ * @author arcox
+ */
+public final class NineLives extends CardImpl {
+
+    public NineLives(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}{W}");
+
+        // Hexproof
+        this.addAbility(HexproofAbility.getInstance());
+
+        // If a source would deal damage to you, prevent that damage and put an incarnation counter on Nine Lives.
+        this.addAbility(new SimpleStaticAbility(new NineLivesPreventionEffect()));
+
+        // When there are nine or more incarnation counters on Nine Lives, exile it.
+        this.addAbility(new NineLivesStateTriggeredAbility());
+
+        // When Nine Lives leaves the battlefield, you lose the game.
+        this.addAbility(new LeavesBattlefieldTriggeredAbility(new LoseGameSourceControllerEffect(), false));
+    }
+
+    public NineLives(final NineLives card) {
+        super(card);
+    }
+
+    @Override
+    public NineLives copy() {
+        return new NineLives(this);
+    }
+}
+
+
+class NineLivesPreventionEffect extends PreventionEffectImpl {
+
+    public NineLivesPreventionEffect() {
+        super(Duration.WhileOnBattlefield);
+        staticText = "If a source would deal damage to you, prevent that damage and put an incarnation counter on {this}";
+    }
+
+    public NineLivesPreventionEffect(final NineLivesPreventionEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public NineLivesPreventionEffect copy() {
+        return new NineLivesPreventionEffect(this);
+    }
+
+    @Override
+    public boolean replaceEvent(GameEvent event, Ability source, Game game) {
+        GameEvent preventEvent = new PreventDamageEvent(source.getFirstTarget(), source.getSourceId(), source.getControllerId(), event.getAmount(), ((DamageEvent) event).isCombatDamage());
+        if (!game.replaceEvent(preventEvent)) {
+            int damage = event.getAmount();
+            Player player = game.getPlayer(source.getControllerId());
+            if (player != null) {
+                Permanent nineLives = source.getSourcePermanentIfItStillExists(game);
+                if (nineLives != null) {
+                    nineLives.addCounters(CounterType.INCARNATION.createInstance(1), source, game);
+                }
+            }
+            event.setAmount(0);
+            game.fireEvent(GameEvent.getEvent(GameEvent.EventType.PREVENTED_DAMAGE, source.getFirstTarget(), source.getSourceId(), source.getControllerId(), damage));
+        }
+        return false;
+    }
+
+    @Override
+    public boolean applies(GameEvent event, Ability source, Game game) {
+        if (super.applies(event, source, game)) {
+            return event.getTargetId().equals(source.getControllerId());
+        }
+        return false;
+    }
+}
+
+
+class NineLivesStateTriggeredAbility extends StateTriggeredAbility {
+
+    public NineLivesStateTriggeredAbility() {
+        super(Zone.BATTLEFIELD, new ExileSourceEffect());
+    }
+
+    public NineLivesStateTriggeredAbility(final NineLivesStateTriggeredAbility ability) {
+        super(ability);
+    }
+
+    @Override
+    public NineLivesStateTriggeredAbility copy() {
+        return new NineLivesStateTriggeredAbility(this);
+    }
+
+    @Override
+    public boolean checkTrigger(GameEvent event, Game game) {
+        Permanent permanent = game.getPermanent(getSourceId());
+        return permanent != null && permanent.getCounters(game).getCount(CounterType.INCARNATION) >= 9;
+    }
+
+    @Override
+    public String getRule() {
+        return "When there are nine or more incarnation counters on {this}, exile it.";
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 22ed04e9c0..ed64828b6d 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -180,6 +180,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Mystic Skyfish", 326, Rarity.COMMON, mage.cards.m.MysticSkyfish.class));
         cards.add(new SetCardInfo("Necromentia", 116, Rarity.RARE, mage.cards.n.Necromentia.class));
         cards.add(new SetCardInfo("Niambi, Esteemed Speaker", 222, Rarity.RARE, mage.cards.n.NiambiEsteemedSpeaker.class));
+        cards.add(new SetCardInfo("Nine Lives", 28, Rarity.RARE, mage.cards.n.NineLives.class));
         cards.add(new SetCardInfo("Obsessive Stitcher", 223, Rarity.UNCOMMON, mage.cards.o.ObsessiveStitcher.class));
         cards.add(new SetCardInfo("Onakke Ogre", 155, Rarity.COMMON, mage.cards.o.OnakkeOgre.class));
         cards.add(new SetCardInfo("Opt", 59, Rarity.COMMON, mage.cards.o.Opt.class));
diff --git a/Mage/src/main/java/mage/counters/CounterType.java b/Mage/src/main/java/mage/counters/CounterType.java
index f5a30d359f..a0fa7e1513 100644
--- a/Mage/src/main/java/mage/counters/CounterType.java
+++ b/Mage/src/main/java/mage/counters/CounterType.java
@@ -73,6 +73,7 @@ public enum CounterType {
     HOURGLASS("hourglass"),
     HUNGER("hunger"),
     ICE("ice"),
+    INCARNATION("incarnation"),
     INDESTRUCTIBLE("indestructible"),
     INFECTION("infection"),
     INTERVENTION("intervention"),

From 5b30ec480441c3acdf47671f0956d8d37e7cde75 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 22 Jun 2020 21:01:53 -0400
Subject: [PATCH 464/586] fixed some cost reduction effects not working

---
 Mage.Sets/src/mage/cards/d/DaybreakChimera.java     | 2 +-
 Mage.Sets/src/mage/cards/d/DragToTheUnderworld.java | 2 +-
 Mage.Sets/src/mage/cards/h/HourOfRevelation.java    | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/d/DaybreakChimera.java b/Mage.Sets/src/mage/cards/d/DaybreakChimera.java
index 32eeb17e45..7b6d0c1bb0 100644
--- a/Mage.Sets/src/mage/cards/d/DaybreakChimera.java
+++ b/Mage.Sets/src/mage/cards/d/DaybreakChimera.java
@@ -28,7 +28,7 @@ public final class DaybreakChimera extends CardImpl {
         this.toughness = new MageInt(3);
 
         // This spell costs {X} less to cast, where X is your devotion to white.
-        this.addAbility(new SimpleStaticAbility(Zone.STACK, new DaybreakChimeraEffect()).addHint(DevotionCount.W.getHint()));
+        this.addAbility(new SimpleStaticAbility(Zone.ALL, new DaybreakChimeraEffect()).addHint(DevotionCount.W.getHint()));
 
         // Flying
         this.addAbility(FlyingAbility.getInstance());
diff --git a/Mage.Sets/src/mage/cards/d/DragToTheUnderworld.java b/Mage.Sets/src/mage/cards/d/DragToTheUnderworld.java
index e8c6687c80..a569e15e87 100644
--- a/Mage.Sets/src/mage/cards/d/DragToTheUnderworld.java
+++ b/Mage.Sets/src/mage/cards/d/DragToTheUnderworld.java
@@ -24,7 +24,7 @@ public final class DragToTheUnderworld extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{B}{B}");
 
         // This spell costs {X} less to cast, where X is your devotion to black.
-        this.addAbility(new SimpleStaticAbility(Zone.STACK, new DragToTheUnderworldEffect())
+        this.addAbility(new SimpleStaticAbility(Zone.ALL, new DragToTheUnderworldEffect())
                 .addHint(DevotionCount.B.getHint())
                 .setRuleAtTheTop(true));
 
diff --git a/Mage.Sets/src/mage/cards/h/HourOfRevelation.java b/Mage.Sets/src/mage/cards/h/HourOfRevelation.java
index 15eb45648a..2c359686be 100644
--- a/Mage.Sets/src/mage/cards/h/HourOfRevelation.java
+++ b/Mage.Sets/src/mage/cards/h/HourOfRevelation.java
@@ -23,7 +23,7 @@ public final class HourOfRevelation extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{W}{W}{W}");
 
         // Hour of Revelation costs {3} less to cast if there are ten or more nonland permanents on the battlefield.
-        SimpleStaticAbility ability = new SimpleStaticAbility(Zone.STACK,
+        SimpleStaticAbility ability = new SimpleStaticAbility(Zone.ALL,
                 new SpellCostReductionSourceEffect(3, new PermanentsOnTheBattlefieldCondition(
                         new FilterNonlandPermanent("there are ten or more nonland permanents on the battlefield"), ComparisonType.MORE_THAN, 9, false)));
         ability.setRuleAtTheTop(true);

From b2783c8993f87c59acdd817cf3919f26ca820475 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 22 Jun 2020 21:36:01 -0400
Subject: [PATCH 465/586] fixed Tentative Connection cost reduction

---
 Mage.Sets/src/mage/cards/t/TentativeConnection.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Mage.Sets/src/mage/cards/t/TentativeConnection.java b/Mage.Sets/src/mage/cards/t/TentativeConnection.java
index 48a3673c90..6dba4d8648 100644
--- a/Mage.Sets/src/mage/cards/t/TentativeConnection.java
+++ b/Mage.Sets/src/mage/cards/t/TentativeConnection.java
@@ -38,7 +38,7 @@ public final class TentativeConnection extends CardImpl {
 
         // This spell costs {3} less to cast if you control a creature with menace.
         Ability ability = new SimpleStaticAbility(
-                Zone.STACK,
+                Zone.ALL,
                 new SpellCostReductionSourceEffect(
                         3, new PermanentsOnTheBattlefieldCondition(filter)
                 )

From cac8c5966cef71d2c9d77b676627e8453dd5f760 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 22 Jun 2020 22:30:55 -0400
Subject: [PATCH 466/586] fixed Chandra's Incinerator not getting the correct
 cost reduction

---
 Mage.Sets/src/mage/cards/c/ChandrasIncinerator.java | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/Mage.Sets/src/mage/cards/c/ChandrasIncinerator.java b/Mage.Sets/src/mage/cards/c/ChandrasIncinerator.java
index 8081f8614b..b100360c62 100644
--- a/Mage.Sets/src/mage/cards/c/ChandrasIncinerator.java
+++ b/Mage.Sets/src/mage/cards/c/ChandrasIncinerator.java
@@ -109,13 +109,14 @@ class ChandrasIncineratorWatcher extends Watcher {
             return;
         }
         for (UUID playerId : game.getOpponents(event.getPlayerId())) {
-            damageMap.compute(playerId, ((u, i) -> i == null ? 0 : Integer.sum(i, event.getAmount())));
+            damageMap.compute(playerId, ((u, i) -> i == null ? event.getAmount() : Integer.sum(i, event.getAmount())));
         }
     }
 
     @Override
     public void reset() {
         damageMap.clear();
+        super.reset();
     }
 
     int getDamage(UUID playerId) {

From fa368f7eba443bf588e54c841ac0c856b3a6214f Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 22 Jun 2020 22:35:14 -0400
Subject: [PATCH 467/586] fixed Archaeomender targeting enchantment cards
 (#6677)

---
 Mage.Sets/src/mage/cards/a/Archaeomender.java | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/a/Archaeomender.java b/Mage.Sets/src/mage/cards/a/Archaeomender.java
index a96b186963..7d796a4504 100644
--- a/Mage.Sets/src/mage/cards/a/Archaeomender.java
+++ b/Mage.Sets/src/mage/cards/a/Archaeomender.java
@@ -9,7 +9,7 @@ import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.SubType;
 import mage.filter.FilterCard;
-import mage.filter.common.FilterArtifactOrEnchantmentCard;
+import mage.filter.common.FilterArtifactCard;
 import mage.target.common.TargetCardInYourGraveyard;
 
 import java.util.UUID;
@@ -20,7 +20,7 @@ import java.util.UUID;
 public final class Archaeomender extends CardImpl {
 
     private static final FilterCard filter
-            = new FilterArtifactOrEnchantmentCard("artifact card from your graveyard");
+            = new FilterArtifactCard("artifact card from your graveyard");
 
     public Archaeomender(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}");

From 23fea9426357b516adc7adf89cfdedca09cfc91d Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 22 Jun 2020 22:36:11 -0400
Subject: [PATCH 468/586] fixed Aven Gagglemaster counting opposing creatures
 (#6677)

---
 Mage.Sets/src/mage/cards/a/AvenGagglemaster.java | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/a/AvenGagglemaster.java b/Mage.Sets/src/mage/cards/a/AvenGagglemaster.java
index 46f82ea96f..0ce91fdf8e 100644
--- a/Mage.Sets/src/mage/cards/a/AvenGagglemaster.java
+++ b/Mage.Sets/src/mage/cards/a/AvenGagglemaster.java
@@ -11,7 +11,7 @@ import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.SubType;
 import mage.filter.FilterPermanent;
-import mage.filter.common.FilterCreaturePermanent;
+import mage.filter.common.FilterControlledCreaturePermanent;
 import mage.filter.predicate.mageobject.AbilityPredicate;
 
 import java.util.UUID;
@@ -21,7 +21,7 @@ import java.util.UUID;
  */
 public final class AvenGagglemaster extends CardImpl {
 
-    private static final FilterPermanent filter = new FilterCreaturePermanent();
+    private static final FilterPermanent filter = new FilterControlledCreaturePermanent();
 
     static {
         filter.add(new AbilityPredicate(FlyingAbility.class));

From 1224f0621f20b3d0dd4bd428ee692adf88261965 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 22 Jun 2020 22:36:37 -0400
Subject: [PATCH 469/586] fixed Ormos, Archive Keeper not adding counters
 (#6677)

---
 Mage.Sets/src/mage/cards/o/OrmosArchiveKeeper.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Mage.Sets/src/mage/cards/o/OrmosArchiveKeeper.java b/Mage.Sets/src/mage/cards/o/OrmosArchiveKeeper.java
index f8bb8d77de..b57dbfc34d 100644
--- a/Mage.Sets/src/mage/cards/o/OrmosArchiveKeeper.java
+++ b/Mage.Sets/src/mage/cards/o/OrmosArchiveKeeper.java
@@ -86,7 +86,7 @@ class OrmosArchiveKeeperEffect extends ReplacementEffectImpl {
     @Override
     public boolean replaceEvent(GameEvent event, Ability source, Game game) {
         Permanent permanent = game.getPermanent(source.getSourceId());
-        if (permanent == null) {
+        if (permanent != null) {
             permanent.addCounters(CounterType.P1P1.createInstance(5), source, game);
         }
         return true;

From b9e93a5da2580e97681403f4909d3d2b69870215 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 22 Jun 2020 22:37:11 -0400
Subject: [PATCH 470/586] fixed Tinybones, Trinket Thief not causing opponents
 to lose life (#6677)

---
 Mage.Sets/src/mage/cards/t/TinybonesTrinketThief.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Mage.Sets/src/mage/cards/t/TinybonesTrinketThief.java b/Mage.Sets/src/mage/cards/t/TinybonesTrinketThief.java
index 8c5cfdfd5b..dd67bdf538 100644
--- a/Mage.Sets/src/mage/cards/t/TinybonesTrinketThief.java
+++ b/Mage.Sets/src/mage/cards/t/TinybonesTrinketThief.java
@@ -116,7 +116,7 @@ class TinybonesTrinketThiefEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         for (UUID playerId : game.getOpponents(source.getControllerId())) {
-            Player player = game.getPlayer(source.getControllerId());
+            Player player = game.getPlayer(playerId);
             if (player != null && player.getHand().isEmpty()) {
                 player.loseLife(10, game, false);
             }

From 28e142a19642918dce893779b553e59fa5e7d228 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 22 Jun 2020 22:43:00 -0400
Subject: [PATCH 471/586] fixed Alchemist's Gift not pumping or granting an
 ability (#6646)

---
 Mage.Sets/src/mage/cards/a/AlchemistsGift.java | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/a/AlchemistsGift.java b/Mage.Sets/src/mage/cards/a/AlchemistsGift.java
index 52653791ad..f589e7f96b 100644
--- a/Mage.Sets/src/mage/cards/a/AlchemistsGift.java
+++ b/Mage.Sets/src/mage/cards/a/AlchemistsGift.java
@@ -66,8 +66,8 @@ class AlchemistsGiftEffect extends OneShotEffect {
                 outcome, "Deathtouch or lifelink?", null,
                 "Deathtouch", "Lifelink", source, game
         ) ? DeathtouchAbility.getInstance() : LifelinkAbility.getInstance();
-        game.addEffect(new BoostSourceEffect(1, 1, Duration.EndOfTurn), ability);
-        game.addEffect(new GainAbilitySourceEffect(ability, Duration.EndOfTurn), ability);
+        game.addEffect(new BoostSourceEffect(1, 1, Duration.EndOfTurn), source);
+        game.addEffect(new GainAbilitySourceEffect(ability, Duration.EndOfTurn), source);
         return true;
     }
 }

From 20b724bc67cf8eeae500ec1305eaa80e5d6dbb81 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 22 Jun 2020 22:45:49 -0400
Subject: [PATCH 472/586] fixed Chandra's Incinerator not triggering on damage
 (#6646)

---
 Mage.Sets/src/mage/cards/c/ChandrasIncinerator.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Mage.Sets/src/mage/cards/c/ChandrasIncinerator.java b/Mage.Sets/src/mage/cards/c/ChandrasIncinerator.java
index b100360c62..2f17b549af 100644
--- a/Mage.Sets/src/mage/cards/c/ChandrasIncinerator.java
+++ b/Mage.Sets/src/mage/cards/c/ChandrasIncinerator.java
@@ -144,7 +144,7 @@ class ChandrasIncineratorTriggeredAbility extends TriggeredAbilityImpl {
         DamagedPlayerEvent dEvent = (DamagedPlayerEvent) event;
         if (dEvent.isCombatDamage()
                 || !game.getOpponents(event.getTargetId()).contains(getControllerId())
-                || !event.getPlayerId().equals(getControllerId())) {
+                || !game.getControllerId(event.getSourceId()).equals(getControllerId())) {
             return false;
         }
         this.getEffects().clear();

From 1c41c48d16c28db63514b5e6a5cb60d82c7e6814 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 22 Jun 2020 22:46:58 -0400
Subject: [PATCH 473/586] fixed Dire Fleet Warmonger granting trample
 indefinitely (#6646)

---
 Mage.Sets/src/mage/cards/d/DireFleetWarmonger.java | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/Mage.Sets/src/mage/cards/d/DireFleetWarmonger.java b/Mage.Sets/src/mage/cards/d/DireFleetWarmonger.java
index d146792b7a..b43fafe14e 100644
--- a/Mage.Sets/src/mage/cards/d/DireFleetWarmonger.java
+++ b/Mage.Sets/src/mage/cards/d/DireFleetWarmonger.java
@@ -37,7 +37,9 @@ public final class DireFleetWarmonger extends CardImpl {
                 new SacrificeTargetCost(new TargetControlledPermanent(
                         StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE
                 ))
-        ).addEffect(new GainAbilitySourceEffect(TrampleAbility.getInstance())), TargetController.YOU, false));
+        ).addEffect(new GainAbilitySourceEffect(
+                TrampleAbility.getInstance(), Duration.EndOfTurn
+        )), TargetController.YOU, false));
     }
 
     private DireFleetWarmonger(final DireFleetWarmonger card) {

From 4aec3e15c10ec36fa4111c06c8330f79b8feb9ef Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 22 Jun 2020 22:50:53 -0400
Subject: [PATCH 474/586] fixed Tolarian Kraken confirmation text (#6646)

---
 Mage.Sets/src/mage/cards/t/TolarianKraken.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Mage.Sets/src/mage/cards/t/TolarianKraken.java b/Mage.Sets/src/mage/cards/t/TolarianKraken.java
index 8fc46dbd91..c4f5d19c42 100644
--- a/Mage.Sets/src/mage/cards/t/TolarianKraken.java
+++ b/Mage.Sets/src/mage/cards/t/TolarianKraken.java
@@ -34,7 +34,7 @@ public final class TolarianKraken extends CardImpl {
         ability.addTarget(new TargetCreaturePermanent());
         this.addAbility(new DrawCardControllerTriggeredAbility(new DoWhenCostPaid(
                 ability, new GenericManaCost(1),
-                "Pay {1} to tap or untap a permanent?"
+                "Pay {1} to tap or untap a creature?"
         ), false));
     }
 

From 77d5b37ca67c6e44af4a551e62f36ee17609e289 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 22 Jun 2020 22:51:46 -0400
Subject: [PATCH 475/586] fixed Warded Battlements giving +1/+1 instead of
 +1/+0 (#6646)

---
 Mage.Sets/src/mage/cards/w/WardedBattlements.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Mage.Sets/src/mage/cards/w/WardedBattlements.java b/Mage.Sets/src/mage/cards/w/WardedBattlements.java
index f803f264b6..db605fb69f 100644
--- a/Mage.Sets/src/mage/cards/w/WardedBattlements.java
+++ b/Mage.Sets/src/mage/cards/w/WardedBattlements.java
@@ -30,7 +30,7 @@ public final class WardedBattlements extends CardImpl {
 
         // Attacking creatures you control get +1/+0.
         this.addAbility(new SimpleStaticAbility(new BoostControlledEffect(
-                1, 1, Duration.WhileOnBattlefield,
+                1, 0, Duration.WhileOnBattlefield,
                 StaticFilters.FILTER_ATTACKING_CREATURES
         )));
     }

From dd22905e9d5ad6106fa5fb37bccee03afb2bcc8e Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 22 Jun 2020 22:56:54 -0400
Subject: [PATCH 476/586] added another fix for Chandra's Incinerator (#6646)

---
 Mage.Sets/src/mage/cards/c/ChandrasIncinerator.java | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/c/ChandrasIncinerator.java b/Mage.Sets/src/mage/cards/c/ChandrasIncinerator.java
index 2f17b549af..9730e9b7ed 100644
--- a/Mage.Sets/src/mage/cards/c/ChandrasIncinerator.java
+++ b/Mage.Sets/src/mage/cards/c/ChandrasIncinerator.java
@@ -119,8 +119,13 @@ class ChandrasIncineratorWatcher extends Watcher {
         super.reset();
     }
 
-    int getDamage(UUID playerId) {
-        return damageMap.getOrDefault(playerId, 0);
+    int getDamage(UUID playerId, Game game) {
+        return game
+                .getOpponents(playerId)
+                .stream()
+                .filter(damageMap::containsKey)
+                .mapToInt(damageMap::get)
+                .sum();
     }
 }
 

From 75220caf0fb9423dff2bc9213acbf8985aeb66ac Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 22 Jun 2020 22:57:47 -0400
Subject: [PATCH 477/586] small additional fix

---
 Mage.Sets/src/mage/cards/c/ChandrasIncinerator.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Mage.Sets/src/mage/cards/c/ChandrasIncinerator.java b/Mage.Sets/src/mage/cards/c/ChandrasIncinerator.java
index 9730e9b7ed..7375c39911 100644
--- a/Mage.Sets/src/mage/cards/c/ChandrasIncinerator.java
+++ b/Mage.Sets/src/mage/cards/c/ChandrasIncinerator.java
@@ -76,7 +76,7 @@ class ChandrasIncineratorCostReductionEffect extends CostModificationEffectImpl
         if (watcher == null) {
             return true;
         }
-        int reductionAmount = watcher.getDamage(source.getControllerId());
+        int reductionAmount = watcher.getDamage(source.getControllerId(), game);
         CardUtil.reduceCost(abilityToModify, Math.max(0, reductionAmount));
         return true;
     }

From 5ae041f39a80edd76550ba57924a8ef210e7ec10 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Tue, 23 Jun 2020 09:29:38 +0400
Subject: [PATCH 478/586] Additional tests for morph and #6680

---
 Mage.Sets/src/mage/cards/a/Abolish.java       |   5 +-
 .../cards/abilities/keywords/MorphTest.java   |  46 +++++++
 .../UseAlternateSourceCostsTest.java          |  84 +++++++++++-
 .../CostReduceWithConditionTest.java          |  53 ++++++++
 .../main/java/mage/players/PlayerImpl.java    | 124 ++++++++----------
 5 files changed, 235 insertions(+), 77 deletions(-)
 create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostReduceWithConditionTest.java

diff --git a/Mage.Sets/src/mage/cards/a/Abolish.java b/Mage.Sets/src/mage/cards/a/Abolish.java
index 47d2170ba0..e619b5c4ae 100644
--- a/Mage.Sets/src/mage/cards/a/Abolish.java
+++ b/Mage.Sets/src/mage/cards/a/Abolish.java
@@ -1,4 +1,3 @@
-
 package mage.cards.a;
 
 import mage.abilities.costs.AlternativeCostSourceAbility;
@@ -16,7 +15,6 @@ import mage.target.common.TargetCardInHand;
 import java.util.UUID;
 
 /**
- *
  * @author Backfir3
  */
 public final class Abolish extends CardImpl {
@@ -28,8 +26,7 @@ public final class Abolish extends CardImpl {
     }
 
     public Abolish(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{W}{W}");
-
+        super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}{W}");
 
         // You may discard a Plains card rather than pay Abolish's mana cost.
         this.addAbility(new AlternativeCostSourceAbility(new DiscardTargetCost(new TargetCardInHand(filterCost))));
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MorphTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MorphTest.java
index 7e3333567a..7f2802ac69 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MorphTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MorphTest.java
@@ -1065,4 +1065,50 @@ public class MorphTest extends CardTestPlayerBase {
         execute();
         assertAllCommandsUsed();
     }
+
+    @Test
+    public void test_MorphWithCostReductionMustBePlayable_NormalCondition() {
+        // {1}{U} creature
+        // Morph {1}{U} (You may cast this card face down as a 2/2 creature for {3}. Turn it face up any time for its morph cost.)
+        // When Willbender is turned face up, change the target of target spell or ability with a single target.
+        addCard(Zone.HAND, playerA, "Willbender");
+        addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
+        //
+        // Creature spells you cast cost {1} less to cast.
+        addCard(Zone.BATTLEFIELD, playerA, "Nylea, Keen-Eyed");
+
+        checkPlayableAbility("can", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Willbender", true);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Willbender");
+        setChoice(playerA, "Yes"); // morph
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
+    }
+
+    @Test
+    public void test_MorphWithCostReductionMustBePlayable_MorphCondition() {
+        // {1}{U} creature
+        // Morph {1}{U} (You may cast this card face down as a 2/2 creature for {3}. Turn it face up any time for its morph cost.)
+        // When Willbender is turned face up, change the target of target spell or ability with a single target.
+        addCard(Zone.HAND, playerA, "Willbender");
+        addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
+        //
+        // Face-down creature spells you cast cost {1} less to cast.
+        addCard(Zone.BATTLEFIELD, playerA, "Dream Chisel");
+
+        checkPlayableAbility("can", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Willbender", true);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Willbender");
+        setChoice(playerA, "Yes"); // morph
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
+    }
 }
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/alternate/UseAlternateSourceCostsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/alternate/UseAlternateSourceCostsTest.java
index ae8ea580ce..1ecc0ebc70 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/cost/alternate/UseAlternateSourceCostsTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/alternate/UseAlternateSourceCostsTest.java
@@ -1,13 +1,12 @@
-
 package org.mage.test.cards.cost.alternate;
 
 import mage.constants.PhaseStep;
 import mage.constants.Zone;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.mage.test.serverside.base.CardTestPlayerBase;
 
 /**
- *
  * @author LevelX2
  */
 public class UseAlternateSourceCostsTest extends CardTestPlayerBase {
@@ -75,4 +74,85 @@ public class UseAlternateSourceCostsTest extends CardTestPlayerBase {
         assertPermanentCount(playerA, "Gray Ogre", 1);
         assertGraveyardCount(playerA, "Lightning Bolt", 1);
     }
+
+
+    @Test
+    public void test_Playable_WithMana() {
+        // {1}{W}{W} instant
+        // You may discard a Plains card rather than pay Abolish's mana cost.
+        // Destroy target artifact or enchantment.
+        addCard(Zone.HAND, playerA, "Abolish");
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 3);
+        addCard(Zone.HAND, playerA, "Plains", 1); // discard cost
+        //
+        addCard(Zone.BATTLEFIELD, playerB, "Alpha Myr");
+
+        checkPlayableAbility("can", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Abolish", true);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Abolish", "Alpha Myr");
+        setChoice(playerA, "Yes"); // use alternative cost
+        setChoice(playerA, "Plains");
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertGraveyardCount(playerB, "Alpha Myr", 1);
+        assertTappedCount("Plains", false, 3); // must discard 1 instead tap
+    }
+
+    @Test
+    public void test_Playable_WithoutMana() {
+        // {1}{W}{W} instant
+        // You may discard a Plains card rather than pay Abolish's mana cost.
+        // Destroy target artifact or enchantment.
+        addCard(Zone.HAND, playerA, "Abolish");
+        //addCard(Zone.BATTLEFIELD, playerA, "Plains", 3);
+        addCard(Zone.HAND, playerA, "Plains", 1); // discard cost
+        //
+        addCard(Zone.BATTLEFIELD, playerB, "Alpha Myr");
+
+        checkPlayableAbility("can", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Abolish", true);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Abolish", "Alpha Myr");
+        setChoice(playerA, "Yes"); // use alternative cost
+        setChoice(playerA, "Plains");
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertGraveyardCount(playerB, "Alpha Myr", 1);
+    }
+
+    @Test
+    public void test_Playable_WithoutManaAndCost() {
+        // {1}{W}{W} instant
+        // You may discard a Plains card rather than pay Abolish's mana cost.
+        // Destroy target artifact or enchantment.
+        addCard(Zone.HAND, playerA, "Abolish");
+        //addCard(Zone.BATTLEFIELD, playerA, "Plains", 3);
+        //addCard(Zone.HAND, playerA, "Plains", 1); // discard cost
+        //
+        addCard(Zone.BATTLEFIELD, playerB, "Alpha Myr");
+
+        // can't see as playable (no mana for normal, no discard for alternative)
+        checkPlayableAbility("can't", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Abolish", false);
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+    }
+
+    @Test
+    @Ignore // TODO: make test to check combo of alternative cost and cost reduction effects
+    public void test_Playable_WithCostReduction() {
+        addCard(Zone.HAND, playerA, "xxx");
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+    }
 }
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostReduceWithConditionTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostReduceWithConditionTest.java
new file mode 100644
index 0000000000..cd733ed0f8
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostReduceWithConditionTest.java
@@ -0,0 +1,53 @@
+package org.mage.test.cards.cost.modification;
+
+import mage.constants.PhaseStep;
+import mage.constants.Zone;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestPlayerBase;
+
+/**
+ * @author JayDi85
+ */
+public class CostReduceWithConditionTest extends CardTestPlayerBase {
+
+    @Test
+    public void test_PriceOfFame_Normal() {
+        // {3}{B}
+        // This spell costs {2} less to cast if it targets a legendary creature.
+        // Destroy target creature.
+        addCard(Zone.HAND, playerA, "Price of Fame", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4);
+        addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears", 1);
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Price of Fame", "Balduvian Bears");
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        assertGraveyardCount(playerB, "Balduvian Bears", 1);
+    }
+
+    @Test
+    @Ignore
+    // TODO: implement workaround like putToStackAsNonPlayable for abilities, see https://github.com/magefree/mage/issues/6685
+    public void test_PriceOfFame_Reduce() {
+        // {3}{B}
+        // This spell costs {2} less to cast if it targets a legendary creature.
+        // Destroy target creature.
+        addCard(Zone.HAND, playerA, "Price of Fame", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4 - 2);
+        addCard(Zone.BATTLEFIELD, playerB, "Anje Falkenrath", 1);
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Price of Fame", "Anje Falkenrath");
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        assertGraveyardCount(playerB, "Anje Falkenrath", 1);
+    }
+}
diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java
index d2066015ee..55125a4602 100644
--- a/Mage/src/main/java/mage/players/PlayerImpl.java
+++ b/Mage/src/main/java/mage/players/PlayerImpl.java
@@ -2852,7 +2852,7 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public ManaOptions getManaAvailable(Game game) {
-        ManaOptions available = new ManaOptions();
+        ManaOptions availableMana = new ManaOptions();
 
         List<Abilities<ActivatedManaAbilityImpl>> sourceWithoutManaCosts = new ArrayList<>();
         List<Abilities<ActivatedManaAbilityImpl>> sourceWithCosts = new ArrayList<>();
@@ -2884,17 +2884,17 @@ public abstract class PlayerImpl implements Player, Serializable {
         }
 
         for (Abilities<ActivatedManaAbilityImpl> manaAbilities : sourceWithoutManaCosts) {
-            available.addMana(manaAbilities, game);
+            availableMana.addMana(manaAbilities, game);
         }
         for (Abilities<ActivatedManaAbilityImpl> manaAbilities : sourceWithCosts) {
-            available.removeDuplicated();
-            available.addManaWithCost(manaAbilities, game);
+            availableMana.removeDuplicated();
+            availableMana.addManaWithCost(manaAbilities, game);
         }
 
         // remove duplicated variants (see ManaOptionsTest for info - when that rises)
-        available.removeDuplicated();
+        availableMana.removeDuplicated();
 
-        return available;
+        return availableMana;
     }
 
     // returns only mana producers that don't require mana payment
@@ -2958,18 +2958,18 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     /**
      * @param ability
-     * @param available    if null, it won't be checked if enough mana is available
+     * @param availableMana if null, it won't be checked if enough mana is available
      * @param sourceObject
      * @param game
      * @return
      */
-    protected boolean canPlay(ActivatedAbility ability, ManaOptions available, MageObject sourceObject, Game game) {
+    protected boolean canPlay(ActivatedAbility ability, ManaOptions availableMana, MageObject sourceObject, Game game) {
         if (!(ability instanceof ActivatedManaAbilityImpl)) {
             ActivatedAbility copy = ability.copy(); // Copy is needed because cost reduction effects modify e.g. the mana to activate/cast the ability
             if (!copy.canActivate(playerId, game).canActivate()) {
                 return false;
             }
-            if (available != null) {
+            if (availableMana != null) {
                 game.getContinuousEffects().costModification(copy, game);
             }
             boolean canBeCastRegularly = true;
@@ -2980,31 +2980,8 @@ public abstract class PlayerImpl implements Player, Serializable {
                 canBeCastRegularly = false;
             }
             if (canBeCastRegularly) {
-                ManaOptions abilityOptions = copy.getMinimumCostToActivate(playerId, game);
-                if (abilityOptions.isEmpty()) {
+                if (canPayMinimumManaCost(copy, availableMana, game)) {
                     return true;
-                } else {
-                    if (available == null) {
-                        return true;
-                    }
-                    MageObjectReference permittingObject = game.getContinuousEffects().asThough(copy.getSourceId(),
-                            AsThoughEffectType.SPEND_OTHER_MANA, copy, copy.getControllerId(), game);
-                    for (Mana mana : abilityOptions) {
-                        for (Mana avail : available) {
-                            // TODO: SPEND_OTHER_MANA effects with getAsThoughManaType can change mana type to pay,
-                            //  but that code processing it as any color, need to test and fix another use cases
-                            //  (example: Sunglasses of Urza - may spend white mana as though it were red mana)
-
-                            //
-                            //  add tests for non any color like Sunglasses of Urza
-                            if (permittingObject != null && mana.count() <= avail.count()) {
-                                return true;
-                            }
-                            if (mana.enough(avail)) { // here we need to check if spend mana as though allow to pay the mana cost
-                                return true;
-                            }
-                        }
-                    }
                 }
             }
 
@@ -3036,12 +3013,42 @@ public abstract class PlayerImpl implements Player, Serializable {
             }
 
             // ALTERNATIVE COST from source card (any AlternativeSourceCosts)
-            return canPlayCardByAlternateCost(game.getCard(ability.getSourceId()), available, copy, game);
+            return canPlayCardByAlternateCost(game.getCard(ability.getSourceId()), availableMana, copy, game);
         }
         return false;
     }
 
-    protected boolean canPlayCardByAlternateCost(Card sourceObject, ManaOptions available, Ability ability, Game game) {
+    protected boolean canPayMinimumManaCost(ActivatedAbility ability, ManaOptions availableMana, Game game) {
+        ManaOptions abilityOptions = ability.getMinimumCostToActivate(playerId, game);
+        if (abilityOptions.isEmpty()) {
+            return true;
+        } else {
+            if (availableMana == null) {
+                return true;
+            }
+            MageObjectReference permittingObject = game.getContinuousEffects().asThough(ability.getSourceId(),
+                    AsThoughEffectType.SPEND_OTHER_MANA, ability, ability.getControllerId(), game);
+            for (Mana mana : abilityOptions) {
+                for (Mana avail : availableMana) {
+                    // TODO: SPEND_OTHER_MANA effects with getAsThoughManaType can change mana type to pay,
+                    //  but that code processing it as any color, need to test and fix another use cases
+                    //  (example: Sunglasses of Urza - may spend white mana as though it were red mana)
+
+                    //
+                    //  add tests for non any color like Sunglasses of Urza
+                    if (permittingObject != null && mana.count() <= avail.count()) {
+                        return true;
+                    }
+                    if (mana.enough(avail)) { // here we need to check if spend mana as though allow to pay the mana cost
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    protected boolean canPlayCardByAlternateCost(Card sourceObject, ManaOptions availableMana, Ability ability, Game game) {
         if (sourceObject != null && !(sourceObject instanceof Permanent)) {
             for (Ability alternateSourceCostsAbility : sourceObject.getAbilities()) {
                 // if cast for noMana no Alternative costs are allowed
@@ -3058,11 +3065,11 @@ public abstract class PlayerImpl implements Player, Serializable {
                             if (manaCosts.isEmpty()) {
                                 return true;
                             } else {
-                                if (available == null) {
+                                if (availableMana == null) {
                                     return true;
                                 }
                                 for (Mana mana : manaCosts.getOptions()) {
-                                    for (Mana avail : available) {
+                                    for (Mana avail : availableMana) {
                                         if (mana.enough(avail)) {
                                             return true;
                                         }
@@ -3090,7 +3097,7 @@ public abstract class PlayerImpl implements Player, Serializable {
                                 return true;
                             } else {
                                 for (Mana mana : manaCosts.getOptions()) {
-                                    for (Mana avail : available) {
+                                    for (Mana avail : availableMana) {
                                         if (mana.enough(avail)) {
                                             return true;
                                         }
@@ -3105,10 +3112,10 @@ public abstract class PlayerImpl implements Player, Serializable {
         return false;
     }
 
-    protected ActivatedAbility findActivatedAbilityFromPlayable(MageObject object, ManaOptions manaAvailable, Ability ability, Game game) {
+    protected ActivatedAbility findActivatedAbilityFromPlayable(MageObject object, ManaOptions availableMana, Ability ability, Game game) {
 
         // special mana to pay spell cost
-        ManaOptions manaFull = manaAvailable.copy();
+        ManaOptions manaFull = availableMana.copy();
         if (ability instanceof SpellAbility) {
             for (AlternateManaPaymentAbility altAbility : CardUtil.getAbilities(object, game).stream()
                     .filter(a -> a instanceof AlternateManaPaymentAbility)
@@ -3139,7 +3146,7 @@ public abstract class PlayerImpl implements Player, Serializable {
         return null;
     }
 
-    protected ActivatedAbility findActivatedAbilityFromAlternativeSourceCost(MageObject object, ManaOptions manaAvailable, Ability ability, Game game) {
+    protected ActivatedAbility findActivatedAbilityFromAlternativeSourceCost(MageObject object, ManaOptions availableMana, Ability ability, Game game) {
         // return play ability that can activate AlternativeSourceCosts
         if (ability instanceof AlternativeSourceCosts && !(object instanceof Permanent)) {
             ActivatedAbility playAbility = null;
@@ -3153,13 +3160,14 @@ public abstract class PlayerImpl implements Player, Serializable {
             }
 
             // 707.4.Objects that are cast face down are turned face down before they are put onto the stack
-            // (e.g. no lands per turn limit, no cast restrictions, another cost, etc)
-            // so morph must checks only mana payment here
+            // E.g. no lands per turn limit, no cast restrictions, cost reduce, etc
+            // Even mana cost can't be checked here without lookahead
+            // So make it available all the time
             boolean canUse;
             if (ability instanceof MorphAbility) {
-                canUse = game.canPlaySorcery(playerId) && canPayAlternateSourceCostsAbility(object, playAbility, manaAvailable, ability, game);
+                canUse = game.canPlaySorcery(playerId) && ((MorphAbility) ability).isAvailable(playAbility, game);
             } else {
-                canUse = canPlay(playAbility, manaAvailable, object, game); // canPlay already checks alternative source costs and all conditions
+                canUse = canPlay(playAbility, availableMana, object, game); // canPlay already checks alternative source costs and all conditions
             }
 
             if (canUse) {
@@ -3169,32 +3177,6 @@ public abstract class PlayerImpl implements Player, Serializable {
         return null;
     }
 
-    protected boolean canPayAlternateSourceCostsAbility(MageObject sourceObject, Ability sourceAbility, ManaOptions available, Ability alternativeAbility, Game game) {
-        if (sourceAbility != null && ((AlternativeSourceCosts) alternativeAbility).isAvailable(sourceAbility, game)) {
-            if (alternativeAbility.getCosts().canPay(alternativeAbility, sourceObject.getId(), this.getId(), game)) {
-                ManaCostsImpl manaCosts = new ManaCostsImpl();
-                for (Cost cost : alternativeAbility.getCosts()) {
-                    if (cost instanceof ManaCost) {
-                        manaCosts.add((ManaCost) cost);
-                    }
-                }
-
-                if (manaCosts.isEmpty()) {
-                    return true;
-                } else {
-                    for (Mana mana : manaCosts.getOptions()) {
-                        for (Mana avail : available) {
-                            if (mana.enough(avail)) {
-                                return true;
-                            }
-                        }
-                    }
-                }
-            }
-        }
-        return false;
-    }
-
     private void getPlayableFromObjectAll(Game game, Zone fromZone, MageObject object, ManaOptions availableMana, List<ActivatedAbility> output) {
         if (fromZone == null || object == null) {
             return;

From 7065b7f7e7612af811dd4e62408e96e22ba517e7 Mon Sep 17 00:00:00 2001
From: John Hitchings <hitch17@gmail.com>
Date: Mon, 22 Jun 2020 23:09:55 -0700
Subject: [PATCH 479/586] use graveyard instead of library as
 ThievesGuildEnforcer condition.

---
 Mage.Sets/src/mage/cards/t/ThievesGuildEnforcer.java | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/t/ThievesGuildEnforcer.java b/Mage.Sets/src/mage/cards/t/ThievesGuildEnforcer.java
index 489eac53a5..c62d5c63f3 100644
--- a/Mage.Sets/src/mage/cards/t/ThievesGuildEnforcer.java
+++ b/Mage.Sets/src/mage/cards/t/ThievesGuildEnforcer.java
@@ -1,5 +1,8 @@
 package mage.cards.t;
 
+import java.util.Objects;
+import java.util.UUID;
+
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility;
@@ -19,12 +22,9 @@ import mage.constants.SubType;
 import mage.constants.TargetController;
 import mage.filter.FilterPermanent;
 import mage.game.Game;
-import mage.players.Library;
+import mage.game.Graveyard;
 import mage.players.Player;
 
-import java.util.Objects;
-import java.util.UUID;
-
 /**
  * @author TheElk801
  */
@@ -82,8 +82,8 @@ enum ThievesGuildEnforcerCondition implements Condition {
                 .stream()
                 .map(game::getPlayer)
                 .filter(Objects::nonNull)
-                .map(Player::getLibrary)
-                .mapToInt(Library::size)
+                .map(Player::getGraveyard)
+                .mapToInt(Graveyard::size)
                 .anyMatch(i -> i >= 8);
     }
 }
\ No newline at end of file

From 29e5230469c2dc0ac8c740ae2920c138560b528d Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Tue, 23 Jun 2020 09:18:40 +0200
Subject: [PATCH 480/586] * Oathbreaker format - Fixed that signate spell
 didn't return to command zone. Added unit test for oathbreaker format (fixes
 #6695).

---
 Mage.Tests/Oathbreaker_UR.dck                 | 48 +++++++++++++
 .../cards/abilities/keywords/BolsterTest.java | 37 ++++++++++
 .../BasicSaheekiSublimeArtificerTest.java     | 69 +++++++++++++++++++
 .../base/CardTestOathbreaker3PlayersFFA.java  | 38 ++++++++++
 .../OathbreakerOnBattlefieldCondition.java    | 12 ++--
 .../CommanderReplacementEffect.java           | 35 ++++++----
 6 files changed, 220 insertions(+), 19 deletions(-)
 create mode 100644 Mage.Tests/Oathbreaker_UR.dck
 create mode 100644 Mage.Tests/src/test/java/org/mage/test/oathbreaker/FFA3/BasicSaheekiSublimeArtificerTest.java
 create mode 100644 Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestOathbreaker3PlayersFFA.java

diff --git a/Mage.Tests/Oathbreaker_UR.dck b/Mage.Tests/Oathbreaker_UR.dck
new file mode 100644
index 0000000000..9574902d44
--- /dev/null
+++ b/Mage.Tests/Oathbreaker_UR.dck
@@ -0,0 +1,48 @@
+NAME:Oathbreaker_UR
+1 [M15:44] Aetherspouts
+1 [ISD:130] Blasphemous Act
+1 [M21:46] Cancel
+1 [C20:146] Chaos Warp
+1 [GRN:233] Chromatic Lantern
+1 [7ED:67] Counterspell
+1 [M15:242] Darksteel Citadel
+1 [EMN:55] Displace
+1 [RAV:46] Drift of Phantasms
+1 [JMP:313] Dualcaster Mage
+1 [NPH:35] Gitaxian Probe
+1 [MRD:282] Great Furnace
+1 [M19:145] Guttersnipe
+1 [LRW:175] Heat Shimmer
+1 [DTK:140] Impact Tremors
+1 [VIS:34] Impulse
+9 [M21:263] Island
+1 [GRN:251] Izzet Guildgate
+1 [GRN:238] Izzet Locket
+1 [M12:63] Mana Leak
+1 [SOI:73] Manic Scribe
+7 [M21:269] Mountain
+1 [M20:69] Negate
+1 [M21:59] Opt
+1 [AER:167] Ornithopter
+1 [ELD:60] Overwhelmed Apprentice
+1 [M11:70] Preordain
+1 [M19:68] Psychic Corrosion
+1 [THS:135] Purphoros, God of the Forge
+1 [KLD:126] Reckless Fireweaver
+1 [M19:254] Reliquary Tower
+1 [MRD:283] Seat of the Synod
+1 [CHK:268] Sensei's Divining Top
+1 [M15:161] Shrapnel Blast
+1 [5DN:150] Silent Arbiter
+1 [XLN:81] Spell Pierce
+1 [M10:220] Spellbook
+1 [SOM:46] Stoic Rebuttal
+1 [MH1:231] Talisman of Creativity
+1 [M11:229] Terramorphic Expanse
+1 [KTK:59] Treasure Cruise
+1 [JOU:115] Twinflame
+1 [DOM:72] Unwind
+1 [M14:163] Young Pyromancer
+SB: 1 [WAR:234] Saheeli, Sublime Artificer
+SB: 1 [MRD:54] Thoughtcast
+
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BolsterTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BolsterTest.java
index c9eb93a740..c83bc8fa8d 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BolsterTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BolsterTest.java
@@ -91,4 +91,41 @@ public class BolsterTest extends CardTestPlayerBase {
 
         assertLife(playerB, 16);
     }
+    
+
+    @Test
+    public void EliteScaleguardTriggerTwiceTest() {
+        // When Elite Scaleguard enters the battlefield, bolster 2.
+        // Whenever a creature you control with a +1/+1 counter on it attacks, tap target creature defending player controls.
+        addCard(Zone.HAND, playerA, "Elite Scaleguard"); // Creature 2/3 {4}{W}
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
+        addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1);
+
+        addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox", 1);
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Elite Scaleguard");
+
+        attack(3, playerA, "Silvercoat Lion");
+        addTarget(playerA, "Pillarfield Ox"); // Tap from triggered ability of Elite Scaleguard
+
+        attack(5, playerA, "Silvercoat Lion");
+        addTarget(playerA, "Pillarfield Ox"); // Tap from triggered ability of Elite Scaleguard
+
+        setStopAt(5, PhaseStep.POSTCOMBAT_MAIN);
+
+        setStrictChooseMode(true);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPowerToughness(playerA, "Elite Scaleguard", 2, 3);
+        assertCounterCount("Silvercoat Lion", CounterType.P1P1, 2);
+        assertPowerToughness(playerA, "Silvercoat Lion", 4, 4);
+        assertTapped("Silvercoat Lion", true);
+
+        assertPermanentCount(playerB, "Pillarfield Ox", 1);
+        assertTapped("Pillarfield Ox", true);
+
+        assertLife(playerB, 12);
+    }
+    
 }
diff --git a/Mage.Tests/src/test/java/org/mage/test/oathbreaker/FFA3/BasicSaheekiSublimeArtificerTest.java b/Mage.Tests/src/test/java/org/mage/test/oathbreaker/FFA3/BasicSaheekiSublimeArtificerTest.java
new file mode 100644
index 0000000000..118983824d
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/oathbreaker/FFA3/BasicSaheekiSublimeArtificerTest.java
@@ -0,0 +1,69 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.mage.test.oathbreaker.FFA3;
+
+import mage.constants.PhaseStep;
+import mage.constants.Zone;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestOathbreaker3PlayersFFA;
+
+/**
+ *
+ * @author LevelX2
+ */
+
+public class BasicSaheekiSublimeArtificerTest extends CardTestOathbreaker3PlayersFFA {
+
+    /**
+     * Check that if a player left the game, it's commander is also removed
+     */
+    @Test
+    public void commanderRemovedTest() {
+
+        concede(1, PhaseStep.PRECOMBAT_MAIN, playerA);
+        setStopAt(1, PhaseStep.DECLARE_ATTACKERS);
+        execute();
+
+        assertCommandZoneCount(playerB, "Saheeli, Sublime Artificer", 1);
+        assertCommandZoneCount(playerB, "Thoughtcast", 1);
+        assertCommandZoneCount(playerC, "Saheeli, Sublime Artificer", 1);
+        assertCommandZoneCount(playerC, "Thoughtcast", 1);
+        assertCommandZoneCount(playerA, "Saheeli, Sublime Artificer", 0);
+        assertCommandZoneCount(playerA, "Thoughtcast", 0);
+
+    }
+    
+    @Test
+    public void castCommanderTest() {
+
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 4);
+        addCard(Zone.BATTLEFIELD, playerA, "Ornithopter", 4);
+
+        // Planeswalker 5 Loyality Counter
+        // Whenever you cast a noncreature spell, create a 1/1 colorless Servo artifact creature token.
+        // -2: Target artifact you control becomes a copy of another target artifact or creature you control until end of turn, 
+        // except it's an artifact in addition to its other types.        
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Saheeli, Sublime Artificer"); // Planeswalker 5  {1}{U/R}{U/R}
+
+        // Affinity for artifacts
+        // Draw two cards.        
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Thoughtcast"); // Sorcery {4}{U}
+        
+        setStopAt(1, PhaseStep.DECLARE_ATTACKERS);
+        execute();
+
+        
+        assertCommandZoneCount(playerB, "Saheeli, Sublime Artificer", 1);
+        assertCommandZoneCount(playerB, "Thoughtcast", 1);
+        assertCommandZoneCount(playerC, "Saheeli, Sublime Artificer", 1);
+        assertCommandZoneCount(playerC, "Thoughtcast", 1);
+
+        assertHandCount(playerA, 3);
+        assertPermanentCount(playerA, "Saheeli, Sublime Artificer", 1);
+        assertCommandZoneCount(playerA, "Thoughtcast", 1);
+
+    }
+}
\ No newline at end of file
diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestOathbreaker3PlayersFFA.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestOathbreaker3PlayersFFA.java
new file mode 100644
index 0000000000..003b9bcb08
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestOathbreaker3PlayersFFA.java
@@ -0,0 +1,38 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.mage.test.serverside.base;
+
+import java.io.FileNotFoundException;
+import mage.constants.MultiplayerAttackOption;
+import mage.constants.RangeOfInfluence;
+import mage.game.Game;
+import mage.game.GameException;
+import mage.game.OathbreakerFreeForAll;
+import mage.game.mulligan.MulliganType;
+import org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl;
+
+/**
+ * @author LevelX2
+ */
+public abstract class CardTestOathbreaker3PlayersFFA extends CardTestPlayerAPIImpl {
+
+    public CardTestOathbreaker3PlayersFFA() {
+        super();
+        this.deckNameA = "Oathbreaker_UR.dck"; // PW: Saheeli, Sublime Artificer  SS: Thoughtcast
+        this.deckNameB = "Oathbreaker_UR.dck";
+        this.deckNameC = "Oathbreaker_UR.dck";
+    }
+
+    @Override
+    protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
+        Game game = new OathbreakerFreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ONE, MulliganType.GAME_DEFAULT.getMulligan(0), 20);
+        playerA = createPlayer(game, playerA, "PlayerA", deckNameA);
+        playerB = createPlayer(game, playerB, "PlayerB", deckNameB);
+        playerC = createPlayer(game, playerC, "PlayerC", deckNameC);
+        return game;
+    }
+
+}
\ No newline at end of file
diff --git a/Mage/src/main/java/mage/abilities/condition/common/OathbreakerOnBattlefieldCondition.java b/Mage/src/main/java/mage/abilities/condition/common/OathbreakerOnBattlefieldCondition.java
index ad26f8a79d..ba27bdfbbc 100644
--- a/Mage/src/main/java/mage/abilities/condition/common/OathbreakerOnBattlefieldCondition.java
+++ b/Mage/src/main/java/mage/abilities/condition/common/OathbreakerOnBattlefieldCondition.java
@@ -22,9 +22,9 @@ import java.util.UUID;
  */
 public class OathbreakerOnBattlefieldCondition implements Condition {
 
-    private UUID playerId;
-    private FilterControlledPermanent filter;
-    private String compatibleNames;
+    private final UUID playerId;
+    private final FilterControlledPermanent filter;
+    private final String compatibleNames;
 
     public OathbreakerOnBattlefieldCondition(Game game, UUID playerId, UUID signatureSpellId, Set<UUID> oathbreakersToSearch) {
         this.playerId = playerId;
@@ -35,17 +35,17 @@ public class OathbreakerOnBattlefieldCondition implements Condition {
 
         // spell can be casted by any compatible oathbreakers
         List<PermanentIdPredicate> compatibleList = new ArrayList<>();
-        List<String> compatibleNames = new ArrayList<>();
+        List<String> compatibleNamesList = new ArrayList<>();
         if (oathbreakersToSearch != null && !oathbreakersToSearch.isEmpty()) {
             for (UUID id : oathbreakersToSearch) {
                 Card commander = game.getCard(id);
                 if (commander != null && ManaUtil.isColorIdentityCompatible(commander.getColorIdentity(), spellColors)) {
                     compatibleList.add(new PermanentIdPredicate(id));
-                    compatibleNames.add(commander.getName());
+                    compatibleNamesList.add(commander.getName());
                 }
             }
         }
-        this.compatibleNames = String.join("; ", compatibleNames);
+        this.compatibleNames = String.join("; ", compatibleNamesList);
 
         if (compatibleList.isEmpty()) {
             // random id to disable condition
diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/CommanderReplacementEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/CommanderReplacementEffect.java
index 5ccacd8711..94aa319796 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/continuous/CommanderReplacementEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/CommanderReplacementEffect.java
@@ -28,9 +28,7 @@ import java.util.UUID;
     that permanent and the card representing it that isn’t a commander are put into the appropriate zone, and the card
     that represents it and is a commander is put into the command zone.
  */
-
 // Oathbreaker mode: If your Oathbreaker changes zones, you may return it to the Command Zone. The Signature Spell must return to the Command Zone.
-
 public class CommanderReplacementEffect extends ReplacementEffectImpl {
 
     private final UUID commanderId;
@@ -39,6 +37,17 @@ public class CommanderReplacementEffect extends ReplacementEffectImpl {
     private final boolean forceToMove;
     private final String commanderTypeName;
 
+    /**
+     *
+     * @param commanderId
+     * @param alsoHand is the replacement effect also applied if commander
+     * object goes to hand zone
+     * @param alsoLibrary is the replacement effect also applied if commander
+     * object goes to library zone
+     * @param forceToMove used for signature spell of Oathbreaker format (spell
+     * is mandatory moved to command zone instead)
+     * @param commanderTypeName type of commander object to set the correct text
+     */
     public CommanderReplacementEffect(UUID commanderId, boolean alsoHand, boolean alsoLibrary, boolean forceToMove, String commanderTypeName) {
         super(Duration.WhileOnBattlefield, Outcome.Benefit);
         String mayStr = forceToMove ? " " : " may ";
@@ -89,11 +98,11 @@ public class CommanderReplacementEffect extends ReplacementEffectImpl {
 
     @Override
     public boolean applies(GameEvent event, Ability source, Game game) {
-        ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
+        if (!commanderId.equals(event.getTargetId())) {
+            return false;
+        }
 
-//        if (!game.isSimulation() && commanderId.equals(zEvent.getTargetId())) {
-//            System.out.println("applies " + game.getTurnNum() + ": " + game.getObject(event.getTargetId()).getName() + ": " + zEvent.getFromZone() + " -> " + zEvent.getToZone() + "; " + game.getObject(zEvent.getSourceId()));
-//        }
+        ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
 
         if (zEvent.getToZone().equals(Zone.HAND) && !alsoHand) {
             return false;
@@ -106,10 +115,14 @@ public class CommanderReplacementEffect extends ReplacementEffectImpl {
         switch (zEvent.getToZone()) {
             case LIBRARY:
             case HAND:
-                if (commanderId.equals(zEvent.getTargetId())) {
+                return true;
+        }
+        if (forceToMove) {
+            switch (zEvent.getToZone()) {
+                case BATTLEFIELD:
+                case GRAVEYARD:
                     return true;
-                }
-                break;
+            }
         }
         return false;
     }
@@ -119,10 +132,6 @@ public class CommanderReplacementEffect extends ReplacementEffectImpl {
         ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
         String originToZone = zEvent.getToZone().toString().toLowerCase(Locale.ENGLISH);
 
-        if (!game.isSimulation()) {
-            //System.out.println("replace " + game.getTurnNum() + ": " + game.getObject(event.getTargetId()).getName() + ": " + zEvent.getFromZone() + " -> " + zEvent.getToZone() + "; " + game.getObject(zEvent.getSourceId()));
-        }
-
         if (zEvent.getFromZone() == Zone.BATTLEFIELD) {
             Permanent permanent = zEvent.getTarget();
             if (permanent != null) {

From 4b14eb3724b9ed15bf57f3fb60fd65e79f8b5dbc Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Tue, 23 Jun 2020 10:09:37 +0200
Subject: [PATCH 481/586] * Delirum - Fixed wrong card type count. Added unit
 test for delirum (fixes #6704).

---
 .../abilities/keywords/DeliriumTest.java      | 167 ++++++++++++++++++
 .../common/CardTypesInGraveyardCount.java     |  11 +-
 2 files changed, 175 insertions(+), 3 deletions(-)
 create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DeliriumTest.java

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DeliriumTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DeliriumTest.java
new file mode 100644
index 0000000000..7f539189d1
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DeliriumTest.java
@@ -0,0 +1,167 @@
+package org.mage.test.cards.abilities.keywords;
+
+import mage.constants.PhaseStep;
+import mage.constants.Zone;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps;
+
+/**
+ *
+ * @author LevelX2
+ */
+public class DeliriumTest extends CardTestPlayerBaseWithAIHelps {
+
+    // Delirium - if there are four or more card types among cards in your graveyard
+    @Test
+    public void noDeliriumTest() {
+        /**
+         * 4/8/2016 If you have three non-sorcery card types among cards in your
+         * graveyard at the time Descend upon the Sinful resolves, you won’t get
+         * an Angel token. Descend upon the Sinful isn’t put into your graveyard
+         * until after it’s finished resolving.
+         */
+
+        // Exile all creatures
+        // Delirium - Create a 4/4 white Angel creature token with flying if there are four or more card types among cards in your graveyard.
+        addCard(Zone.HAND, playerA, "Descend upon the Sinful", 1); // Sorcery {4}{W}{W}
+
+        addCard(Zone.GRAVEYARD, playerA, "Thought Vessel", 2); // Artifact
+        addCard(Zone.GRAVEYARD, playerA, "Tempered Steel", 2); // Enchantment
+        addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion", 2); // Creature
+
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 6);
+        addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 4);
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Descend upon the Sinful");
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        assertExileCount(playerA, "Balduvian Bears", 4);
+        assertGraveyardCount(playerA, "Descend upon the Sinful", 1);
+
+        assertPermanentCount(playerA, "Angel", 0);
+
+    }
+
+    @Test
+    public void ItsDeliriumTest() {
+        /**
+         * 4/8/2016 The number of card types matters, not the number of cards.
+         * For example, Wicker Witch (an artifact creature) along with Catalog
+         * (an instant) and Chaplain’s Blessing (a sorcery) will enable
+         * delirium.
+         */
+        // Exile all creatures
+        // Delirium - Create a 4/4 white Angel creature token with flying if there are four or more card types among cards in your graveyard.
+        addCard(Zone.HAND, playerA, "Descend upon the Sinful", 1); // Sorcery {4}{W}{W}
+
+        addCard(Zone.GRAVEYARD, playerA, "Wicker Witch", 1); // Artifact Creature
+        addCard(Zone.GRAVEYARD, playerA, "Catalog", 1); // Instant
+        addCard(Zone.GRAVEYARD, playerA, "Chaplain's Blessing", 1); // Sorcery
+
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 6);
+        addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 4);
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Descend upon the Sinful");
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        assertExileCount(playerA, "Balduvian Bears", 4);
+        assertGraveyardCount(playerA, "Descend upon the Sinful", 1);
+
+        assertPermanentCount(playerA, "Angel", 1);
+
+    }
+
+    @Test
+    public void noDeliriumWithCreatureEnchantmentTest() {
+
+        /**
+         * 4/8/2016 If one of those creatures was enchanted, its Aura won’t be
+         * put into a player’s graveyard until after Descend upon the Sinful has
+         * finished resolving. If the controller of Descend upon the Sinful
+         * owned the Aura, it won’t be in the graveyard in time to be counted
+         * for the delirium ability.
+         */
+        // Exile all creatures
+        // Delirium - Create a 4/4 white Angel creature token with flying if there are four or more card types among cards in your graveyard.
+        addCard(Zone.HAND, playerA, "Descend upon the Sinful", 1); // Sorcery {4}{W}{W}
+
+        // Enchant creature
+        // Enchanted creature gets +1/+1 and has protection from creatures.
+        addCard(Zone.HAND, playerA, "Spirit Mantle", 1); // Enchantment Aura {1}{W}
+
+        addCard(Zone.GRAVEYARD, playerA, "Thought Vessel", 2); // Artifact
+        addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion", 2); // Creature
+        addCard(Zone.GRAVEYARD, playerA, "Plains", 2); // Land
+
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 8);
+        addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 4);
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Spirit Mantle", "Balduvian Bears");
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Descend upon the Sinful");
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        assertHandCount(playerA, 0);
+        assertExileCount(playerA, "Balduvian Bears", 4);
+        assertGraveyardCount(playerA, "Spirit Mantle", 1);
+        assertGraveyardCount(playerA, "Descend upon the Sinful", 1);
+
+        assertPermanentCount(playerA, "Angel", 0);
+
+    }
+
+    @Test
+    public void noDeliriumWithDoubleFacedCardsTest() {
+
+        /**
+         * 4/8/2016 Because you consider only the characteristics of a
+         * double-faced card’s front face while it’s not on the battlefield, the
+         * types of its back face won’t be counted for delirium.
+         */
+        // Exile all creatures
+        // Delirium - Create a 4/4 white Angel creature token with flying if there are four or more card types among cards in your graveyard.
+        addCard(Zone.HAND, playerA, "Descend upon the Sinful", 1); // Sorcery {4}{W}{W}
+
+        addCard(Zone.GRAVEYARD, playerA, "Elbrus, the Binding Blade", 1); // Artifact (not counted as Creature from Backside)
+        addCard(Zone.GRAVEYARD, playerA, "Catalog", 1); // Instant
+        addCard(Zone.GRAVEYARD, playerA, "Plains", 1); // Land
+
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 6);
+        addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 4);
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Descend upon the Sinful");
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        assertHandCount(playerA, 0);
+        assertExileCount(playerA, "Balduvian Bears", 4);
+        assertGraveyardCount(playerA, "Descend upon the Sinful", 1);
+
+        assertPermanentCount(playerA, "Angel", 0);
+
+    }
+
+    /**
+     * 4/8/2016 In some rare cases, you can have a token or a copy of a spell in
+     * your graveyard at the moment that an object’s delirium ability counts the
+     * card types among cards in your graveyard, before that token or copy
+     * ceases to exist. Because tokens and copies of spells are not cards, even
+     * if they are copies of cards, their types will never be counted.
+     */
+    
+    // No test added yet
+}
diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/CardTypesInGraveyardCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/CardTypesInGraveyardCount.java
index 4bdc931e36..aba073e3f7 100644
--- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/CardTypesInGraveyardCount.java
+++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/CardTypesInGraveyardCount.java
@@ -1,12 +1,14 @@
 package mage.abilities.dynamicvalue.common;
 
-import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
 import mage.abilities.Ability;
 import mage.abilities.dynamicvalue.DynamicValue;
 import mage.abilities.effects.Effect;
 import mage.cards.Card;
 import mage.constants.CardType;
 import mage.game.Game;
+import mage.game.permanent.PermanentToken;
 import mage.players.Player;
 
 /**
@@ -20,9 +22,12 @@ public enum CardTypesInGraveyardCount implements DynamicValue {
     public int calculate(Game game, Ability sourceAbility, Effect effect) {
         Player controller = game.getPlayer(sourceAbility.getControllerId());
         if (controller != null) {
-            ArrayList<CardType> foundCardTypes = new ArrayList<>();
+            Set<CardType> foundCardTypes = new HashSet<>();
             for (Card card : controller.getGraveyard().getCards(game)) {
-                foundCardTypes.addAll(card.getCardType());
+                // 4/8/2016 In some rare cases, you can have a token or a copy of a spell in your graveyard at the moment that an object’s delirium ability counts the card types among cards in your graveyard, before that token or copy ceases to exist. Because tokens and copies of spells are not cards, even if they are copies of cards, their types will never be counted.
+                if (!card.isCopy() && !(card instanceof PermanentToken)) {
+                    foundCardTypes.addAll(card.getCardType());
+                }
             }
             return foundCardTypes.size();
         }

From de9ecd58bd4e7c76ff6cb9b37dfa90c101e92b1b Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Tue, 23 Jun 2020 17:12:22 +0200
Subject: [PATCH 482/586] * Added test for Trostani Descordant gain control
 triggerd ability.

---
 .../GainControlOfOwnedCreaturesTest.java      | 115 ++++++++++++++++++
 1 file changed, 115 insertions(+)
 create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/control/GainControlOfOwnedCreaturesTest.java

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/control/GainControlOfOwnedCreaturesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/control/GainControlOfOwnedCreaturesTest.java
new file mode 100644
index 0000000000..83d0e02d3f
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/control/GainControlOfOwnedCreaturesTest.java
@@ -0,0 +1,115 @@
+package org.mage.test.cards.control;
+
+import mage.constants.PhaseStep;
+import mage.constants.Zone;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestPlayerBase;
+
+/**
+ *
+ * @author LevelX2
+ */
+public class GainControlOfOwnedCreaturesTest extends CardTestPlayerBase {
+
+    /**
+     *
+     *
+     */
+    @Test
+    public void TrostaniDiscordantTest() {
+        // Other creatures you control get +1/+1.
+        // When Trostani Discordant enters the battlefield, create two 1/1 white Soldier creature tokens with lifelink.
+        // At the beginning of your end step, each player gains control of all creatures they own.
+        addCard(Zone.LIBRARY, playerA, "Trostani Discordant", 1); // Creature {3}{G}{W}  1/4
+
+        // Your opponent can't cast spells with even converted mana costs. (Zero is even.)
+        // Your opponents can't block with creatures with even converted mana costs.        
+        addCard(Zone.LIBRARY, playerA, "Void Winnower", 1); // Creature 11/9      
+        skipInitShuffling();
+
+        // Look at the top ten cards of your library, exile up to two creature cards from among them, then shuffle your library. 
+        // Target opponent may choose one of the exiled cards and put it onto the battlefield under their control. Put the rest onto the battlefield under your control.
+        addCard(Zone.HAND, playerA, "Dubious Challenge", 1); // Sorcery {3}{G}
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 4);
+
+        addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1);
+
+        addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion");
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dubious Challenge");
+        setChoice(playerA, "Trostani Discordant^Void Winnower");
+
+        addTarget(playerA, playerB);
+
+        setChoice(playerB, "Trostani Discordant");
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+
+        assertAllCommandsUsed();
+
+        assertGraveyardCount(playerA, "Dubious Challenge", 1);
+        assertPermanentCount(playerA, "Void Winnower", 1);
+        assertPowerToughness(playerA, "Void Winnower", 11, 9);
+        assertPermanentCount(playerB, "Trostani Discordant", 1);
+        assertPowerToughness(playerB, "Trostani Discordant", 1, 4);
+
+        assertPowerToughness(playerA, "Silvercoat Lion", 2, 2);
+        assertPowerToughness(playerB, "Silvercoat Lion", 3, 3);
+    }
+
+    /**
+     * https://www.slightlymagic.net/forum/viewtopic.php?f=70&t=29805&p=243997#p243989
+     *
+     * I was playing a Dubious Challenge Pionner deck. In my game, Trostani
+     * Discordant was put on the battlefield after Ive launched a Dubious
+     * Challenge sorcery. My opponent took the Trostani Discordant and I took a
+     * Void Winnower . Unfortnately, at the end step, the Trostani Discordant
+     * ability that makes all players gain control of the creatures they OWN
+     * doesn't happened.
+     */
+    @Test
+    public void TrostaniDiscordantTriggerTest() {
+        // Other creatures you control get +1/+1.
+        // When Trostani Discordant enters the battlefield, create two 1/1 white Soldier creature tokens with lifelink.
+        // At the beginning of your end step, each player gains control of all creatures they own.
+        addCard(Zone.LIBRARY, playerA, "Trostani Discordant", 1); // Creature {3}{G}{W}  1/4
+
+        // Your opponent can't cast spells with even converted mana costs. (Zero is even.)
+        // Your opponents can't block with creatures with even converted mana costs.        
+        addCard(Zone.LIBRARY, playerA, "Void Winnower", 1); // Creature 11/9      
+        skipInitShuffling();
+
+        // Look at the top ten cards of your library, exile up to two creature cards from among them, then shuffle your library. 
+        // Target opponent may choose one of the exiled cards and put it onto the battlefield under their control. Put the rest onto the battlefield under your control.
+        addCard(Zone.HAND, playerA, "Dubious Challenge", 1); // Sorcery {3}{G}
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 4);
+
+        addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1);
+
+        addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion");
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dubious Challenge");
+        setChoice(playerA, "Trostani Discordant^Void Winnower");
+
+        addTarget(playerA, playerB);
+
+        setChoice(playerB, "Trostani Discordant");
+
+        setStrictChooseMode(true);
+        setStopAt(8, PhaseStep.PRECOMBAT_MAIN);
+        execute();
+
+        assertAllCommandsUsed();
+
+        assertGraveyardCount(playerA, "Dubious Challenge", 1);
+        assertPermanentCount(playerA, "Void Winnower", 1);
+        assertPowerToughness(playerA, "Void Winnower", 12, 10);
+        assertPermanentCount(playerA, "Trostani Discordant", 1);
+        assertPowerToughness(playerA, "Trostani Discordant", 1, 4);
+
+        assertPowerToughness(playerA, "Silvercoat Lion", 3, 3);
+        assertPowerToughness(playerB, "Silvercoat Lion", 2, 2);
+    }
+}

From 5b9135ef71aff20c440776a414167df41a29720a Mon Sep 17 00:00:00 2001
From: 18ths <13023067+18ths@users.noreply.github.com>
Date: Tue, 23 Jun 2020 17:26:05 +0200
Subject: [PATCH 483/586] fixed clearing temporary effects while rollbacks

---
 .../main/java/mage/abilities/effects/ContinuousEffectsList.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousEffectsList.java b/Mage/src/main/java/mage/abilities/effects/ContinuousEffectsList.java
index 803cdc5bc1..6dd37673e0 100644
--- a/Mage/src/main/java/mage/abilities/effects/ContinuousEffectsList.java
+++ b/Mage/src/main/java/mage/abilities/effects/ContinuousEffectsList.java
@@ -210,7 +210,7 @@ public class ContinuousEffectsList<T extends ContinuousEffect> extends ArrayList
     public void removeEffects(UUID effectIdToRemove, Set<Ability> abilitiesToRemove) {
         Set<Ability> abilities = effectAbilityMap.get(effectIdToRemove);
         if (abilitiesToRemove != null && abilities != null) {
-            abilities.removeAll(abilitiesToRemove);
+            abilities.removeIf(ability -> abilitiesToRemove.stream().anyMatch(a -> a.isSameInstance(ability)));
         }
         if (abilities == null || abilities.isEmpty()) {
             for (Iterator<T> iterator = this.iterator(); iterator.hasNext();) {

From 324c2bdcf3a4294e12b02f8a2f19c4bc7f2cbd0a Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Tue, 23 Jun 2020 19:47:42 +0200
Subject: [PATCH 484/586] * Arcane Artisan - Fixed that the tokens were not
 exiled but destroyes (fixes #6710).

---
 Mage.Sets/src/mage/cards/a/ArcaneArtisan.java | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/a/ArcaneArtisan.java b/Mage.Sets/src/mage/cards/a/ArcaneArtisan.java
index e4028535a3..d46ca7710f 100644
--- a/Mage.Sets/src/mage/cards/a/ArcaneArtisan.java
+++ b/Mage.Sets/src/mage/cards/a/ArcaneArtisan.java
@@ -30,6 +30,7 @@ import mage.util.CardUtil;
 import java.util.HashSet;
 import java.util.Set;
 import java.util.UUID;
+import mage.cards.CardsImpl;
 
 /**
  * @author TheElk801
@@ -167,12 +168,10 @@ class ArcaneArtisanExileEffect extends OneShotEffect {
     public boolean apply(Game game, Ability source) {
         Object object = game.getState().getValue(CardUtil.getCardZoneString("_tokensCreated", source.getSourceId(), game, true));
         if (object != null) {
-            Set<UUID> tokensCreated = (Set<UUID>) object;
-            for (UUID tokenId : tokensCreated) {
-                Permanent token = game.getPermanent(tokenId);
-                if (token != null) {
-                    token.destroy(source.getSourceId(), game, true);
-                }
+            Player controller = game.getPlayer(source.getControllerId());
+            if (controller != null) {
+                controller.moveCards(new CardsImpl((Set<UUID>) object), Zone.EXILED, source, game);
+                return true;
             }
         }
         return true;

From 93b4fe5c95d05633abea0538c41ff4d52c9e5974 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 23 Jun 2020 17:24:04 -0400
Subject: [PATCH 485/586] Implemented Subira, Tulzidi Caravanner

---
 .../mage/cards/s/SubiraTulzidiCaravanner.java | 114 ++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |   1 +
 2 files changed, 115 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/s/SubiraTulzidiCaravanner.java

diff --git a/Mage.Sets/src/mage/cards/s/SubiraTulzidiCaravanner.java b/Mage.Sets/src/mage/cards/s/SubiraTulzidiCaravanner.java
new file mode 100644
index 0000000000..4f5d497f83
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/SubiraTulzidiCaravanner.java
@@ -0,0 +1,114 @@
+package mage.cards.s;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.DelayedTriggeredAbility;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.common.DiscardHandCost;
+import mage.abilities.costs.common.TapSourceCost;
+import mage.abilities.costs.mana.GenericManaCost;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
+import mage.abilities.effects.common.DrawCardSourceControllerEffect;
+import mage.abilities.effects.common.combat.CantBeBlockedTargetEffect;
+import mage.abilities.keyword.HasteAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.*;
+import mage.filter.FilterPermanent;
+import mage.filter.common.FilterCreaturePermanent;
+import mage.filter.predicate.mageobject.PowerPredicate;
+import mage.filter.predicate.permanent.AnotherPredicate;
+import mage.game.Game;
+import mage.game.events.DamagedPlayerEvent;
+import mage.game.events.GameEvent;
+import mage.game.permanent.Permanent;
+import mage.target.TargetPermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class SubiraTulzidiCaravanner extends CardImpl {
+
+    private static final FilterPermanent filter = new FilterCreaturePermanent("another creature with power 2 or less");
+
+    static {
+        filter.add(new PowerPredicate(ComparisonType.FEWER_THAN, 3));
+        filter.add(AnotherPredicate.instance);
+    }
+
+    public SubiraTulzidiCaravanner(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}");
+
+        this.addSuperType(SuperType.LEGENDARY);
+        this.subtype.add(SubType.HUMAN);
+        this.subtype.add(SubType.SHAMAN);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(3);
+
+        // Haste
+        this.addAbility(HasteAbility.getInstance());
+
+        // {1}: Another target creature with power 2 or less can't be blocked this turn.
+        Ability ability = new SimpleActivatedAbility(
+                new CantBeBlockedTargetEffect(Duration.EndOfTurn)
+                        .setText("another target creature with power 2 or less can't be blocked this turn"),
+                new GenericManaCost(1)
+        );
+        ability.addTarget(new TargetPermanent(filter));
+        this.addAbility(ability);
+
+        // {1}{R}, {T}, Discard your hand: Until end of turn, whenever a creature you control with power 2 or less deals combat damage to a player, draw a card.
+        ability = new SimpleActivatedAbility(new CreateDelayedTriggeredAbilityEffect(
+                new SubiraTulzidiCaravannerAbility()
+        ), new ManaCostsImpl("{1}{R}"));
+        ability.addCost(new TapSourceCost());
+        ability.addCost(new DiscardHandCost());
+        this.addAbility(ability);
+    }
+
+    private SubiraTulzidiCaravanner(final SubiraTulzidiCaravanner card) {
+        super(card);
+    }
+
+    @Override
+    public SubiraTulzidiCaravanner copy() {
+        return new SubiraTulzidiCaravanner(this);
+    }
+}
+
+class SubiraTulzidiCaravannerAbility extends DelayedTriggeredAbility {
+
+    SubiraTulzidiCaravannerAbility() {
+        super(new DrawCardSourceControllerEffect(1), Duration.EndOfTurn, false, false);
+    }
+
+    private SubiraTulzidiCaravannerAbility(final SubiraTulzidiCaravannerAbility ability) {
+        super(ability);
+    }
+
+    @Override
+    public boolean checkEventType(GameEvent event, Game game) {
+        return event.getType() == GameEvent.EventType.DAMAGED_PLAYER;
+    }
+
+    @Override
+    public boolean checkTrigger(GameEvent event, Game game) {
+        DamagedPlayerEvent dEvent = (DamagedPlayerEvent) event;
+        if (!dEvent.isCombatDamage()) {
+            return false;
+        }
+        Permanent permanent = game.getPermanent(event.getSourceId());
+        return permanent != null
+                && permanent.isControlledBy(getControllerId())
+                && permanent.isCreature()
+                && permanent.getPower().getValue() <= 2;
+    }
+
+    @Override
+    public SubiraTulzidiCaravannerAbility copy() {
+        return new SubiraTulzidiCaravannerAbility(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index ed64828b6d..0ba15b56a2 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -251,6 +251,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Staunch Shieldmate", 39, Rarity.COMMON, mage.cards.s.StaunchShieldmate.class));
         cards.add(new SetCardInfo("Storm Caller", 335, Rarity.COMMON, mage.cards.s.StormCaller.class));
         cards.add(new SetCardInfo("Stormwing Entity", 73, Rarity.RARE, mage.cards.s.StormwingEntity.class));
+        cards.add(new SetCardInfo("Subira, Tulzidi Caravanner", 162, Rarity.RARE, mage.cards.s.SubiraTulzidiCaravanner.class));
         cards.add(new SetCardInfo("Sublime Epiphany", 74, Rarity.RARE, mage.cards.s.SublimeEpiphany.class));
         cards.add(new SetCardInfo("Sure Strike", 163, Rarity.COMMON, mage.cards.s.SureStrike.class));
         cards.add(new SetCardInfo("Swamp", 266, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS));

From b28a9c2ba27d04643176243523cfae01ccef24bd Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 23 Jun 2020 17:25:47 -0400
Subject: [PATCH 486/586] Implemented Terror of the Peaks

---
 .../src/mage/cards/t/TerrorOfThePeaks.java    | 128 ++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |   1 +
 2 files changed, 129 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/t/TerrorOfThePeaks.java

diff --git a/Mage.Sets/src/mage/cards/t/TerrorOfThePeaks.java b/Mage.Sets/src/mage/cards/t/TerrorOfThePeaks.java
new file mode 100644
index 0000000000..705677402b
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/t/TerrorOfThePeaks.java
@@ -0,0 +1,128 @@
+package mage.cards.t;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.Mode;
+import mage.abilities.SpellAbility;
+import mage.abilities.common.EntersBattlefieldAllTriggeredAbility;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.costs.common.PayLifeCost;
+import mage.abilities.effects.common.DamageTargetEffect;
+import mage.abilities.effects.common.cost.CostModificationEffectImpl;
+import mage.abilities.keyword.FlyingAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.*;
+import mage.filter.StaticFilters;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+import mage.game.permanent.Permanent;
+import mage.target.Target;
+
+import java.util.Collection;
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class TerrorOfThePeaks extends CardImpl {
+
+    public TerrorOfThePeaks(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{R}");
+
+        this.subtype.add(SubType.DRAGON);
+        this.power = new MageInt(5);
+        this.toughness = new MageInt(4);
+
+        // Flying
+        this.addAbility(FlyingAbility.getInstance());
+
+        // Spells your opponents cast that target Terror of the Peaks cost an additional 3 life to cast.
+        this.addAbility(new SimpleStaticAbility(new TerrorOfThePeaksCostIncreaseEffect()));
+
+        // Whenever another creature enters the battlefield under your control, Terror of the Peaks deals damage equal to that creature's power to any target.
+        this.addAbility(new TerrorOfThePeaksTriggeredAbility());
+    }
+
+    private TerrorOfThePeaks(final TerrorOfThePeaks card) {
+        super(card);
+    }
+
+    @Override
+    public TerrorOfThePeaks copy() {
+        return new TerrorOfThePeaks(this);
+    }
+}
+
+class TerrorOfThePeaksCostIncreaseEffect extends CostModificationEffectImpl {
+
+    TerrorOfThePeaksCostIncreaseEffect() {
+        super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST);
+        staticText = "Spells your opponents cast that target {this} cost an additional 3 life to cast";
+    }
+
+    private TerrorOfThePeaksCostIncreaseEffect(TerrorOfThePeaksCostIncreaseEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source, Ability abilityToModify) {
+        SpellAbility spellAbility = (SpellAbility) abilityToModify;
+        spellAbility.addCost(new PayLifeCost(3));
+        return true;
+    }
+
+    @Override
+    public boolean applies(Ability abilityToModify, Ability source, Game game) {
+        if (!(abilityToModify instanceof SpellAbility)
+                || !game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) {
+            return false;
+        }
+        return abilityToModify
+                .getModes()
+                .getSelectedModes()
+                .stream()
+                .map(uuid -> abilityToModify.getModes().get(uuid))
+                .map(Mode::getTargets)
+                .flatMap(Collection::stream)
+                .map(Target::getTargets)
+                .flatMap(Collection::stream)
+                .anyMatch(uuid -> uuid.equals(source.getSourceId()));
+    }
+
+    @Override
+    public TerrorOfThePeaksCostIncreaseEffect copy() {
+        return new TerrorOfThePeaksCostIncreaseEffect(this);
+    }
+}
+
+class TerrorOfThePeaksTriggeredAbility extends EntersBattlefieldAllTriggeredAbility {
+
+    TerrorOfThePeaksTriggeredAbility() {
+        super(null, StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE);
+    }
+
+    private TerrorOfThePeaksTriggeredAbility(final TerrorOfThePeaksTriggeredAbility ability) {
+        super(ability);
+    }
+
+    @Override
+    public boolean checkTrigger(GameEvent event, Game game) {
+        if (!super.checkTrigger(event, game)) {
+            return false;
+        }
+        Permanent permanent = game.getPermanent(event.getTargetId());
+        if (permanent == null) {
+            return false;
+        }
+        this.getEffects().clear();
+        this.addEffect(new DamageTargetEffect(permanent.getPower().getValue()));
+        return true;
+    }
+
+    @Override
+    public String getRule() {
+        return "Whenever another creature enters the battlefield under your control, " +
+                "{this} deals damage equal to that creature's power to any target.";
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 0ba15b56a2..6cff003ba8 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -269,6 +269,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Temple of Mystery", 254, Rarity.RARE, mage.cards.t.TempleOfMystery.class));
         cards.add(new SetCardInfo("Temple of Silence", 255, Rarity.RARE, mage.cards.t.TempleOfSilence.class));
         cards.add(new SetCardInfo("Temple of Triumph", 256, Rarity.RARE, mage.cards.t.TempleOfTriumph.class));
+        cards.add(new SetCardInfo("Terror of the Peaks", 164, Rarity.MYTHIC, mage.cards.t.TerrorOfThePeaks.class));
         cards.add(new SetCardInfo("Thieves' Guild Enforcer", 125, Rarity.RARE, mage.cards.t.ThievesGuildEnforcer.class));
         cards.add(new SetCardInfo("Thornwood Falls", 257, Rarity.COMMON, mage.cards.t.ThornwoodFalls.class));
         cards.add(new SetCardInfo("Thrashing Brontodon", 209, Rarity.UNCOMMON, mage.cards.t.ThrashingBrontodon.class));

From 4a6a0e4d42c149f05e6da1b02a731d94eb58a418 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 23 Jun 2020 17:26:42 -0400
Subject: [PATCH 487/586] Implemented Branching Evolution

---
 .../src/mage/cards/b/BranchingEvolution.java  | 86 +++++++++++++++++++
 Mage.Sets/src/mage/sets/Jumpstart.java        |  1 +
 2 files changed, 87 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/b/BranchingEvolution.java

diff --git a/Mage.Sets/src/mage/cards/b/BranchingEvolution.java b/Mage.Sets/src/mage/cards/b/BranchingEvolution.java
new file mode 100644
index 0000000000..0e67487032
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/b/BranchingEvolution.java
@@ -0,0 +1,86 @@
+package mage.cards.b;
+
+import mage.abilities.Ability;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.effects.ReplacementEffectImpl;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.Outcome;
+import mage.counters.CounterType;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+import mage.game.permanent.Permanent;
+import mage.util.CardUtil;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class BranchingEvolution extends CardImpl {
+
+    public BranchingEvolution(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}");
+
+        // If one or more +1/+1 counters would be put a on a creature you control, twice that many +1/+1 counters are put on that creature instead.
+        this.addAbility(new SimpleStaticAbility(new BranchingEvolutionEffect()));
+    }
+
+    private BranchingEvolution(final BranchingEvolution card) {
+        super(card);
+    }
+
+    @Override
+    public BranchingEvolution copy() {
+        return new BranchingEvolution(this);
+    }
+}
+
+class BranchingEvolutionEffect extends ReplacementEffectImpl {
+
+    BranchingEvolutionEffect() {
+        super(Duration.WhileOnBattlefield, Outcome.BoostCreature, false);
+        staticText = "If one or more +1/+1 counters would be put on a creature you control, " +
+                "twice that many +1/+1 counters are put on it instead";
+    }
+
+    private BranchingEvolutionEffect(final BranchingEvolutionEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public boolean replaceEvent(GameEvent event, Ability source, Game game) {
+        event.setAmountForCounters(CardUtil.addWithOverflowCheck(event.getAmount(), event.getAmount()), true);
+        return false;
+    }
+
+    @Override
+    public boolean checksEventType(GameEvent event, Game game) {
+        return event.getType() == GameEvent.EventType.ADD_COUNTERS;
+    }
+
+    @Override
+    public boolean applies(GameEvent event, Ability source, Game game) {
+        if (event.getData().equals(CounterType.P1P1.getName()) && event.getAmount() > 0) {
+            Permanent permanent = game.getPermanent(event.getTargetId());
+            if (permanent == null) {
+                permanent = game.getPermanentEntering(event.getTargetId());
+            }
+            return permanent != null && permanent.isControlledBy(source.getControllerId())
+                    && permanent.isCreature();
+        }
+        return false;
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        return true;
+    }
+
+    @Override
+    public BranchingEvolutionEffect copy() {
+        return new BranchingEvolutionEffect(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index 344f54847e..bdfab43016 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -79,6 +79,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Bone Splinters", 213, Rarity.COMMON, mage.cards.b.BoneSplinters.class));
         cards.add(new SetCardInfo("Borderland Marauder", 300, Rarity.COMMON, mage.cards.b.BorderlandMarauder.class));
         cards.add(new SetCardInfo("Borderland Minotaur", 301, Rarity.COMMON, mage.cards.b.BorderlandMinotaur.class));
+        cards.add(new SetCardInfo("Branching Evolution", 29, Rarity.RARE, mage.cards.b.BranchingEvolution.class));
         cards.add(new SetCardInfo("Brightmare", 2, Rarity.UNCOMMON, mage.cards.b.Brightmare.class));
         cards.add(new SetCardInfo("Brindle Shoat", 380, Rarity.UNCOMMON, mage.cards.b.BrindleShoat.class));
         cards.add(new SetCardInfo("Brushstrider", 381, Rarity.UNCOMMON, mage.cards.b.Brushstrider.class));

From ba0e9af5fd9c7c104f125851f41add1c6a663e93 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 23 Jun 2020 17:29:14 -0400
Subject: [PATCH 488/586] Implemented Bruvac the Grandiloquent (still needs
 refactoring

---
 .../mage/cards/b/BruvacTheGrandiloquent.java  | 80 +++++++++++++++++++
 Mage.Sets/src/mage/sets/Jumpstart.java        |  1 +
 .../main/java/mage/game/events/GameEvent.java |  1 +
 3 files changed, 82 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/b/BruvacTheGrandiloquent.java

diff --git a/Mage.Sets/src/mage/cards/b/BruvacTheGrandiloquent.java b/Mage.Sets/src/mage/cards/b/BruvacTheGrandiloquent.java
new file mode 100644
index 0000000000..be1371b865
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/b/BruvacTheGrandiloquent.java
@@ -0,0 +1,80 @@
+package mage.cards.b;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.effects.ReplacementEffectImpl;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.*;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+import mage.util.CardUtil;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class BruvacTheGrandiloquent extends CardImpl {
+
+    public BruvacTheGrandiloquent(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}");
+
+        this.addSuperType(SuperType.LEGENDARY);
+        this.subtype.add(SubType.HUMAN);
+        this.subtype.add(SubType.ADVISOR);
+        this.power = new MageInt(1);
+        this.toughness = new MageInt(4);
+
+        // If an opponent would mill one or more cards, they mill twice that many cards instead.
+        this.addAbility(new SimpleStaticAbility(new BruvacTheGrandiloquentReplacementEffect()));
+    }
+
+    private BruvacTheGrandiloquent(final BruvacTheGrandiloquent card) {
+        super(card);
+    }
+
+    @Override
+    public BruvacTheGrandiloquent copy() {
+        return new BruvacTheGrandiloquent(this);
+    }
+}
+
+class BruvacTheGrandiloquentReplacementEffect extends ReplacementEffectImpl {
+
+    BruvacTheGrandiloquentReplacementEffect() {
+        super(Duration.WhileOnBattlefield, Outcome.Neutral);
+        staticText = "If an opponent would mill one or more cards, they mill twice that many cards instead";
+    }
+
+    private BruvacTheGrandiloquentReplacementEffect(final BruvacTheGrandiloquentReplacementEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public BruvacTheGrandiloquentReplacementEffect copy() {
+        return new BruvacTheGrandiloquentReplacementEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        return true;
+    }
+
+    @Override
+    public boolean checksEventType(GameEvent event, Game game) {
+        return event.getType() == GameEvent.EventType.MILL_CARDS;
+    }
+
+    @Override
+    public boolean applies(GameEvent event, Ability source, Game game) {
+        return game.getOpponents(source.getControllerId()).contains(event.getPlayerId());
+    }
+
+    @Override
+    public boolean replaceEvent(GameEvent event, Ability source, Game game) {
+        event.setAmount(CardUtil.addWithOverflowCheck(event.getAmount(), event.getAmount()));
+        return false;
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index bdfab43016..6fc7e0aa64 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -83,6 +83,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Brightmare", 2, Rarity.UNCOMMON, mage.cards.b.Brightmare.class));
         cards.add(new SetCardInfo("Brindle Shoat", 380, Rarity.UNCOMMON, mage.cards.b.BrindleShoat.class));
         cards.add(new SetCardInfo("Brushstrider", 381, Rarity.UNCOMMON, mage.cards.b.Brushstrider.class));
+        cards.add(new SetCardInfo("Bruvac the Grandiloquent", 10, Rarity.MYTHIC, mage.cards.b.BruvacTheGrandiloquent.class));
         cards.add(new SetCardInfo("Bubbling Cauldron", 460, Rarity.UNCOMMON, mage.cards.b.BubblingCauldron.class));
         cards.add(new SetCardInfo("Bulwark Giant", 93, Rarity.COMMON, mage.cards.b.BulwarkGiant.class));
         cards.add(new SetCardInfo("Burglar Rat", 214, Rarity.COMMON, mage.cards.b.BurglarRat.class));
diff --git a/Mage/src/main/java/mage/game/events/GameEvent.java b/Mage/src/main/java/mage/game/events/GameEvent.java
index d95756bc0d..9af64c0c37 100644
--- a/Mage/src/main/java/mage/game/events/GameEvent.java
+++ b/Mage/src/main/java/mage/game/events/GameEvent.java
@@ -88,6 +88,7 @@ public class GameEvent implements Serializable {
         CYCLE_CARD, CYCLED_CARD, CYCLE_DRAW,
         CLASH, CLASHED,
         DAMAGE_PLAYER,
+        MILL_CARDS,
         /* DAMAGED_PLAYER
          targetId    the id of the damaged player
          sourceId    sourceId of the ability which caused the damage

From 44394cf016bdbc32139f1cac7bc1924a18e2965c Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 23 Jun 2020 21:28:43 -0400
Subject: [PATCH 489/586] added another fix for Chandra's Incinerator

---
 Mage.Sets/src/mage/cards/c/ChandrasIncinerator.java | 13 ++++---------
 1 file changed, 4 insertions(+), 9 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/c/ChandrasIncinerator.java b/Mage.Sets/src/mage/cards/c/ChandrasIncinerator.java
index 7375c39911..5fd71a51ef 100644
--- a/Mage.Sets/src/mage/cards/c/ChandrasIncinerator.java
+++ b/Mage.Sets/src/mage/cards/c/ChandrasIncinerator.java
@@ -76,7 +76,7 @@ class ChandrasIncineratorCostReductionEffect extends CostModificationEffectImpl
         if (watcher == null) {
             return true;
         }
-        int reductionAmount = watcher.getDamage(source.getControllerId(), game);
+        int reductionAmount = watcher.getDamage(source.getControllerId());
         CardUtil.reduceCost(abilityToModify, Math.max(0, reductionAmount));
         return true;
     }
@@ -108,7 +108,7 @@ class ChandrasIncineratorWatcher extends Watcher {
                 || ((DamagedPlayerEvent) event).isCombatDamage()) {
             return;
         }
-        for (UUID playerId : game.getOpponents(event.getPlayerId())) {
+        for (UUID playerId : game.getOpponents(event.getTargetId())) {
             damageMap.compute(playerId, ((u, i) -> i == null ? event.getAmount() : Integer.sum(i, event.getAmount())));
         }
     }
@@ -119,13 +119,8 @@ class ChandrasIncineratorWatcher extends Watcher {
         super.reset();
     }
 
-    int getDamage(UUID playerId, Game game) {
-        return game
-                .getOpponents(playerId)
-                .stream()
-                .filter(damageMap::containsKey)
-                .mapToInt(damageMap::get)
-                .sum();
+    int getDamage(UUID playerId) {
+        return damageMap.getOrDefault(playerId, 0);
     }
 }
 

From 9c73f294840218dd3eb68cfa0106656f878bfe80 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 23 Jun 2020 21:29:34 -0400
Subject: [PATCH 490/586] fixed Alchemist's Gift not doing anything

---
 Mage.Sets/src/mage/cards/a/AlchemistsGift.java | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/a/AlchemistsGift.java b/Mage.Sets/src/mage/cards/a/AlchemistsGift.java
index f589e7f96b..0c3ee91bcf 100644
--- a/Mage.Sets/src/mage/cards/a/AlchemistsGift.java
+++ b/Mage.Sets/src/mage/cards/a/AlchemistsGift.java
@@ -2,8 +2,8 @@ package mage.cards.a;
 
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
-import mage.abilities.effects.common.continuous.BoostSourceEffect;
-import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
+import mage.abilities.effects.common.continuous.BoostTargetEffect;
+import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
 import mage.abilities.keyword.DeathtouchAbility;
 import mage.abilities.keyword.LifelinkAbility;
 import mage.cards.CardImpl;
@@ -66,8 +66,8 @@ class AlchemistsGiftEffect extends OneShotEffect {
                 outcome, "Deathtouch or lifelink?", null,
                 "Deathtouch", "Lifelink", source, game
         ) ? DeathtouchAbility.getInstance() : LifelinkAbility.getInstance();
-        game.addEffect(new BoostSourceEffect(1, 1, Duration.EndOfTurn), source);
-        game.addEffect(new GainAbilitySourceEffect(ability, Duration.EndOfTurn), source);
+        game.addEffect(new BoostTargetEffect(1, 1, Duration.EndOfTurn), source);
+        game.addEffect(new GainAbilityTargetEffect(ability, Duration.EndOfTurn), source);
         return true;
     }
 }

From 785be83484ad992be0555048ed0fb55e5a7a2510 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Wed, 24 Jun 2020 07:50:00 -0400
Subject: [PATCH 491/586] Refactoring cards that mill (WIP, do not merge)
 (#6713)

* added mill method

* updated mill effects to use new method

* refactored individual cards

* small updated to Grindstone and Sphinx's Tutelage

* another updated to Grindstone

* fixed a test

* fixed Countermand null check

* more refactoring

* updated dredge ability to use mill
---
 .../src/mage/cards/a/AberrantResearcher.java  | 25 ++++---
 .../src/mage/cards/a/AcolyteOfAffliction.java |  3 +-
 .../src/mage/cards/a/AltarOfDementia.java     |  4 +-
 .../src/mage/cards/b/BelltowerSphinx.java     |  2 +-
 .../src/mage/cards/b/BrokenAmbitions.java     |  5 +-
 Mage.Sets/src/mage/cards/c/CauldronsGift.java |  5 +-
 .../src/mage/cards/c/CephalidVandal.java      |  4 +-
 .../mage/cards/c/ChainsOfMephistopheles.java  |  2 +-
 .../mage/cards/c/ChancellorOfTheSpires.java   | 56 +++-----------
 .../src/mage/cards/c/ChillOfForeboding.java   | 49 ++-----------
 .../src/mage/cards/c/ChronicFlooding.java     |  2 +-
 .../src/mage/cards/c/CloudhoofKirin.java      | 24 +++---
 .../src/mage/cards/c/CoercedConfession.java   | 57 ++++++++-------
 .../src/mage/cards/c/CombustibleGearhulk.java | 19 ++---
 Mage.Sets/src/mage/cards/c/Countermand.java   | 10 +--
 .../src/mage/cards/c/CrosstownCourier.java    |  2 +-
 .../mage/cards/c/CurseOfTheBloodyTome.java    |  2 +-
 Mage.Sets/src/mage/cards/d/DeepSpawn.java     |  2 +-
 .../src/mage/cards/d/DidntSayPlease.java      |  6 +-
 Mage.Sets/src/mage/cards/d/DreadSummons.java  | 62 ++++++++--------
 Mage.Sets/src/mage/cards/d/DreambornMuse.java | 14 ++--
 Mage.Sets/src/mage/cards/d/DrownInFilth.java  |  2 +-
 .../src/mage/cards/e/EnterTheGodEternals.java |  2 +-
 .../src/mage/cards/e/ExtractFromDarkness.java | 51 ++++---------
 Mage.Sets/src/mage/cards/e/EyeCollector.java  | 46 +-----------
 Mage.Sets/src/mage/cards/f/FlintGolem.java    |  2 +-
 .../src/mage/cards/f/FolioOfFancies.java      | 30 ++++----
 Mage.Sets/src/mage/cards/f/Foreshadow.java    | 26 +++----
 Mage.Sets/src/mage/cards/f/FrayingSanity.java |  6 +-
 .../src/mage/cards/g/GethLordOfTheVault.java  | 33 ++++-----
 .../src/mage/cards/g/GhoulcallersBell.java    | 49 ++-----------
 .../src/mage/cards/g/GorgingVulture.java      | 12 +--
 Mage.Sets/src/mage/cards/g/GraveStrength.java |  2 +-
 Mage.Sets/src/mage/cards/g/Grindclock.java    |  6 +-
 Mage.Sets/src/mage/cards/g/Grindstone.java    | 55 +++++++++-----
 .../src/mage/cards/g/GrislySpectacle.java     |  2 +-
 .../src/mage/cards/g/GrizzledAngler.java      | 12 +--
 .../src/mage/cards/g/GyrudaDoomOfDepths.java  | 22 +++---
 Mage.Sets/src/mage/cards/h/HeedTheMists.java  | 26 +++----
 .../src/mage/cards/h/HelmOfObedience.java     | 73 ++++++++++---------
 .../src/mage/cards/h/HereticsPunishment.java  | 49 ++++++-------
 .../src/mage/cards/i/IcebergCancrix.java      |  2 +-
 .../src/mage/cards/i/IncreasingConfusion.java |  4 +-
 .../src/mage/cards/i/InduceParanoia.java      |  4 +-
 .../src/mage/cards/i/InfernalGenesis.java     | 39 +++++-----
 .../src/mage/cards/j/JacesMindseeker.java     | 23 ++----
 Mage.Sets/src/mage/cards/k/KeeningStone.java  | 13 ++--
 .../src/mage/cards/l/LammastideWeave.java     | 30 ++++----
 .../mage/cards/l/LilianaUntouchedByDeath.java | 30 +++-----
 .../src/mage/cards/l/LilianasIndignation.java | 54 +++++++-------
 Mage.Sets/src/mage/cards/l/LoafingGiant.java  | 34 +++++----
 Mage.Sets/src/mage/cards/m/ManicScribe.java   | 21 +++---
 Mage.Sets/src/mage/cards/m/MemoryErosion.java |  2 +-
 Mage.Sets/src/mage/cards/m/MesmericOrb.java   | 27 +++----
 Mage.Sets/src/mage/cards/m/Mindcrank.java     |  2 +-
 Mage.Sets/src/mage/cards/m/Mindshrieker.java  | 44 ++++++-----
 .../src/mage/cards/n/NavigatorsRuin.java      | 15 ++--
 .../src/mage/cards/n/NemesisOfReason.java     | 28 ++++---
 .../src/mage/cards/p/PatientRebuilding.java   | 11 ++-
 .../mage/cards/p/PhenaxGodOfDeception.java    |  6 +-
 Mage.Sets/src/mage/cards/p/Predict.java       | 35 ++++-----
 Mage.Sets/src/mage/cards/p/PsychicSpiral.java |  2 +-
 Mage.Sets/src/mage/cards/p/PsychicStrike.java |  2 +-
 .../src/mage/cards/r/RakshasasSecret.java     | 15 ++--
 Mage.Sets/src/mage/cards/r/ReefPirates.java   |  2 +-
 Mage.Sets/src/mage/cards/r/Riddlekeeper.java  |  2 +-
 .../src/mage/cards/s/SanityGrinding.java      | 22 ++----
 .../src/mage/cards/s/SaprazzanBreaker.java    | 37 ++++------
 .../src/mage/cards/s/ScreamingShield.java     |  2 +-
 .../src/mage/cards/s/ScreechingSilcaw.java    | 15 ++--
 .../src/mage/cards/s/ScreechingSliver.java    |  2 +-
 Mage.Sets/src/mage/cards/s/SewerNemesis.java  |  2 +-
 Mage.Sets/src/mage/cards/s/SharedTrauma.java  |  2 +-
 Mage.Sets/src/mage/cards/s/Shriekgeist.java   |  2 +-
 .../mage/cards/s/SirenOfTheSilentSong.java    |  2 +-
 Mage.Sets/src/mage/cards/s/SongOfBlood.java   | 59 +++++++--------
 .../src/mage/cards/s/SphinxsTutelage.java     | 49 +++++++++----
 Mage.Sets/src/mage/cards/s/SternMentor.java   |  3 +-
 .../src/mage/cards/s/StitcherGeralf.java      |  6 +-
 .../src/mage/cards/s/SwordOfBodyAndMind.java  |  2 +-
 .../src/mage/cards/s/SzadekLordOfSecrets.java |  4 +-
 .../mage/cards/t/TheMendingOfDominaria.java   |  2 +-
 .../src/mage/cards/t/ThoughtCollapse.java     |  8 +-
 .../src/mage/cards/t/ToweringWaveMystic.java  |  3 +-
 .../src/mage/cards/t/TymaretCallsTheDead.java |  3 +-
 .../src/mage/cards/u/UndeadAlchemist.java     | 10 +--
 .../mage/cards/u/UrzaAcademyHeadmaster.java   |  2 +-
 Mage.Sets/src/mage/cards/v/ViciousRumors.java |  3 +-
 Mage.Sets/src/mage/cards/w/Whetstone.java     | 52 +++----------
 Mage.Sets/src/mage/cards/w/WorryBeads.java    | 14 ++--
 .../cards/replacement/GrindstoneTest.java     |  7 +-
 .../replacement/LeylineOfTheVoidTest.java     |  2 +-
 .../java/org/mage/test/player/TestPlayer.java |  5 ++
 .../java/org/mage/test/stub/PlayerStub.java   |  9 ++-
 ...utTopCardOfYourLibraryToGraveyardCost.java |  4 +-
 .../PutLibraryIntoGraveTargetEffect.java      |  3 +-
 ...ardOfLibraryIntoGraveControllerEffect.java |  3 +-
 ...ardOfLibraryIntoGraveEachPlayerEffect.java |  3 +-
 ...TopCardOfLibraryIntoGraveTargetEffect.java |  3 +-
 .../mage/abilities/keyword/DredgeAbility.java | 15 ++--
 Mage/src/main/java/mage/players/Player.java   | 16 ++--
 .../main/java/mage/players/PlayerImpl.java    | 11 +++
 102 files changed, 738 insertions(+), 957 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/a/AberrantResearcher.java b/Mage.Sets/src/mage/cards/a/AberrantResearcher.java
index 54c1bd42d4..c83c858867 100644
--- a/Mage.Sets/src/mage/cards/a/AberrantResearcher.java
+++ b/Mage.Sets/src/mage/cards/a/AberrantResearcher.java
@@ -1,22 +1,22 @@
 
 package mage.cards.a;
 
-import java.util.UUID;
-
 import mage.MageInt;
+import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.TransformSourceEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.abilities.keyword.TransformAbility;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.*;
 import mage.game.Game;
 import mage.players.Player;
 
+import java.util.UUID;
+
 /**
  * @author fireshoes
  */
@@ -54,7 +54,7 @@ class AberrantResearcherEffect extends OneShotEffect {
 
     public AberrantResearcherEffect() {
         super(Outcome.Benefit);
-        staticText = "put the top card of your library into your graveyard. If it's an instant or sorcery card, transform {this}";
+        staticText = "mill a card. If an instant or sorcery card was milled this way, transform {this}";
     }
 
     public AberrantResearcherEffect(final AberrantResearcherEffect effect) {
@@ -64,15 +64,16 @@ class AberrantResearcherEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player controller = game.getPlayer(source.getControllerId());
-        if (controller != null && controller.getLibrary().hasCards()) {
-            Card card = controller.getLibrary().getFromTop(game);
-            controller.moveCards(card, Zone.GRAVEYARD, source, game);
-            if (card.isInstant() || card.isSorcery()) {
-                new TransformSourceEffect(true).apply(game, source);
-            }
-            return true;
+        if (controller == null
+                || controller
+                .millCards(1, source, game)
+                .getCards(game)
+                .stream()
+                .noneMatch(MageObject::isInstantOrSorcery)) {
+            return false;
         }
-        return false;
+        new TransformSourceEffect(true).apply(game, source);
+        return true;
     }
 
     @Override
diff --git a/Mage.Sets/src/mage/cards/a/AcolyteOfAffliction.java b/Mage.Sets/src/mage/cards/a/AcolyteOfAffliction.java
index 3a158f7778..58791c6deb 100644
--- a/Mage.Sets/src/mage/cards/a/AcolyteOfAffliction.java
+++ b/Mage.Sets/src/mage/cards/a/AcolyteOfAffliction.java
@@ -53,8 +53,7 @@ class AcolyteOfAfflictionEffect extends OneShotEffect {
 
     AcolyteOfAfflictionEffect() {
         super(Outcome.Benefit);
-        staticText = "put the top two cards of your library into your graveyard, " +
-                "then you may return a permanent card from your graveyard to your hand.";
+        staticText = "mill two cards, then you may return a permanent card from your graveyard to your hand.";
     }
 
     private AcolyteOfAfflictionEffect(final AcolyteOfAfflictionEffect effect) {
diff --git a/Mage.Sets/src/mage/cards/a/AltarOfDementia.java b/Mage.Sets/src/mage/cards/a/AltarOfDementia.java
index 0413e9b960..339134c9b4 100644
--- a/Mage.Sets/src/mage/cards/a/AltarOfDementia.java
+++ b/Mage.Sets/src/mage/cards/a/AltarOfDementia.java
@@ -48,7 +48,7 @@ class AltarOfDementiaEffect extends OneShotEffect {
 
     public AltarOfDementiaEffect() {
         super(Outcome.Damage);
-        staticText = "Target player puts a number of cards equal to the sacrificed creature's power from the top of their library into their graveyard";
+        staticText = "Target player mills cards equal to the sacrificed creature's power";
     }
 
     public AltarOfDementiaEffect(final AltarOfDementiaEffect effect) {
@@ -67,7 +67,7 @@ class AltarOfDementiaEffect extends OneShotEffect {
                 }
             }
             if (amount > 0) {
-                player.moveCards(player.getLibrary().getTopCards(game, amount), Zone.GRAVEYARD, source, game);
+                player.millCards(amount, source, game);
             }
             return true;
         }
diff --git a/Mage.Sets/src/mage/cards/b/BelltowerSphinx.java b/Mage.Sets/src/mage/cards/b/BelltowerSphinx.java
index 4ef63a4281..ed65fe16d8 100644
--- a/Mage.Sets/src/mage/cards/b/BelltowerSphinx.java
+++ b/Mage.Sets/src/mage/cards/b/BelltowerSphinx.java
@@ -85,6 +85,6 @@ class BelltowerSphinxEffect extends TriggeredAbilityImpl {
 
     @Override
     public String getRule() {
-        return "Whenever a source deals damage to {this}, that source's controller puts that many cards from the top of their library into their graveyard.";
+        return "Whenever a source deals damage to {this}, that source's controller mills that many cards.";
     }
 }
diff --git a/Mage.Sets/src/mage/cards/b/BrokenAmbitions.java b/Mage.Sets/src/mage/cards/b/BrokenAmbitions.java
index b22f48055d..297b9d1c58 100644
--- a/Mage.Sets/src/mage/cards/b/BrokenAmbitions.java
+++ b/Mage.Sets/src/mage/cards/b/BrokenAmbitions.java
@@ -11,7 +11,6 @@ import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.Outcome;
-import mage.constants.Zone;
 import mage.game.Game;
 import mage.game.stack.Spell;
 import mage.players.Player;
@@ -45,7 +44,7 @@ public final class BrokenAmbitions extends CardImpl {
 
 class BrokenAmbitionsEffect extends OneShotEffect {
 
-    private static final String effectText = "Counter target spell unless its controller pays {X}. Clash with an opponent. If you win, that spell's controller puts the top four cards of their library into their graveyard";
+    private static final String effectText = "Counter target spell unless its controller pays {X}. Clash with an opponent. If you win, that spell's controller mills four cards";
 
     protected Cost cost;
     protected DynamicValue genericMana;
@@ -107,7 +106,7 @@ class BrokenAmbitionsEffect extends OneShotEffect {
             game.informPlayers(player.getLogName() + " chooses to pay " + costValueMessage + " to prevent the counter effect");
 
             if (ClashEffect.getInstance().apply(game, source)) {
-                player.moveCards(player.getLibrary().getTopCards(game, 4), Zone.GRAVEYARD, source, game);
+                player.millCards(4, source, game);
             }
             return true;
         }
diff --git a/Mage.Sets/src/mage/cards/c/CauldronsGift.java b/Mage.Sets/src/mage/cards/c/CauldronsGift.java
index 923e9568a8..6d936425ff 100644
--- a/Mage.Sets/src/mage/cards/c/CauldronsGift.java
+++ b/Mage.Sets/src/mage/cards/c/CauldronsGift.java
@@ -47,8 +47,7 @@ class CauldronsGiftEffect extends OneShotEffect {
     CauldronsGiftEffect() {
         super(Outcome.Benefit);
         staticText = "<i>Adamant</i> &mdash; If at least three black mana was spent to cast this spell, " +
-                "put the top four cards of your library into your graveyard." +
-                "<br>You may choose a creature card in your graveyard. " +
+                "mill four cards. <br>You may choose a creature card in your graveyard. " +
                 "If you do, return it to the battlefield with an additional +1/+1 counter on it.";
     }
 
@@ -68,7 +67,7 @@ class CauldronsGiftEffect extends OneShotEffect {
             return false;
         }
         if (AdamantCondition.BLACK.apply(game, source)) {
-            player.moveCards(player.getLibrary().getTopCards(game, 4), Zone.GRAVEYARD, source, game);
+            player.millCards(4, source, game);
         }
         if (player.getGraveyard().count(StaticFilters.FILTER_CARD_CREATURE, game) == 0
                 || !player.chooseUse(outcome, "Choose a creature card in your graveyard to return to the battlefield?", source, game)) {
diff --git a/Mage.Sets/src/mage/cards/c/CephalidVandal.java b/Mage.Sets/src/mage/cards/c/CephalidVandal.java
index 780863a404..627b3c11a9 100644
--- a/Mage.Sets/src/mage/cards/c/CephalidVandal.java
+++ b/Mage.Sets/src/mage/cards/c/CephalidVandal.java
@@ -54,7 +54,7 @@ class CephalidVandalEffect extends OneShotEffect {
 
     public CephalidVandalEffect() {
         super(Outcome.Neutral);
-        staticText = "Then put the top card of your library into your graveyard for each shred counter on {this}";
+        staticText = "Then mill a card for each shred counter on {this}";
     }
 
     public CephalidVandalEffect(final CephalidVandalEffect effect) {
@@ -72,7 +72,7 @@ class CephalidVandalEffect extends OneShotEffect {
         Permanent permanent = game.getPermanent(source.getSourceId());
         if (permanent != null && controller != null) {
             int amount = permanent.getCounters(game).getCount(CounterType.SHRED);
-            controller.moveCards(controller.getLibrary().getTopCards(game, amount), Zone.GRAVEYARD, source, game);
+            controller.millCards(amount, source, game);
         }
         return true;
     }
diff --git a/Mage.Sets/src/mage/cards/c/ChainsOfMephistopheles.java b/Mage.Sets/src/mage/cards/c/ChainsOfMephistopheles.java
index 97210ba99b..d1e26d7750 100644
--- a/Mage.Sets/src/mage/cards/c/ChainsOfMephistopheles.java
+++ b/Mage.Sets/src/mage/cards/c/ChainsOfMephistopheles.java
@@ -43,7 +43,7 @@ class ChainsOfMephistophelesReplacementEffect extends ReplacementEffectImpl {
 
     public ChainsOfMephistophelesReplacementEffect() {
         super(Duration.WhileOnBattlefield, Outcome.Benefit);
-        staticText = "If a player would draw a card except the first one they draw in their draw step each turn, that player discards a card instead. If the player discards a card this way, they draw a card. If the player doesn't discard a card this way, they put the top card of their library into their graveyard";
+        staticText = "If a player would draw a card except the first one they draw in their draw step each turn, that player discards a card instead. If the player discards a card this way, they draw a card. If the player doesn't discard a card this way, they mill a card";
     }
 
     public ChainsOfMephistophelesReplacementEffect(final ChainsOfMephistophelesReplacementEffect effect) {
diff --git a/Mage.Sets/src/mage/cards/c/ChancellorOfTheSpires.java b/Mage.Sets/src/mage/cards/c/ChancellorOfTheSpires.java
index 689fd0bf19..d575eb23c7 100644
--- a/Mage.Sets/src/mage/cards/c/ChancellorOfTheSpires.java
+++ b/Mage.Sets/src/mage/cards/c/ChancellorOfTheSpires.java
@@ -1,36 +1,33 @@
-
 package mage.cards.c;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.DelayedTriggeredAbility;
 import mage.abilities.common.ChancellorAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
-import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.PlayTargetWithoutPayingManaEffect;
+import mage.abilities.effects.common.PutTopCardOfLibraryIntoGraveEachPlayerEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.SubType;
-import mage.constants.Outcome;
-import mage.constants.Zone;
+import mage.constants.TargetController;
 import mage.filter.FilterCard;
 import mage.filter.predicate.Predicates;
 import mage.game.Game;
 import mage.game.events.GameEvent;
 import mage.game.events.GameEvent.EventType;
-import mage.players.Player;
 import mage.target.common.TargetCardInOpponentsGraveyard;
 
+import java.util.UUID;
+
 /**
- *
  * @author BetaSteward
  */
 public final class ChancellorOfTheSpires extends CardImpl {
 
-    private static final String abilityText = "at the beginning of the first upkeep, each opponent puts the top seven cards of their library into their graveyard";
+    private static final String abilityText = "at the beginning of the first upkeep, each opponent mills seven cards";
 
     private static final FilterCard filter = new FilterCard("instant or sorcery card from an opponent's graveyard");
 
@@ -41,7 +38,7 @@ public final class ChancellorOfTheSpires extends CardImpl {
     }
 
     public ChancellorOfTheSpires(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{U}{U}{U}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}{U}{U}");
         this.subtype.add(SubType.SPHINX);
 
         this.power = new MageInt(5);
@@ -58,7 +55,7 @@ public final class ChancellorOfTheSpires extends CardImpl {
         this.addAbility(ability);
     }
 
-    public ChancellorOfTheSpires(final ChancellorOfTheSpires card) {
+    private ChancellorOfTheSpires(final ChancellorOfTheSpires card) {
         super(card);
     }
 
@@ -70,11 +67,11 @@ public final class ChancellorOfTheSpires extends CardImpl {
 
 class ChancellorOfTheSpiresDelayedTriggeredAbility extends DelayedTriggeredAbility {
 
-    ChancellorOfTheSpiresDelayedTriggeredAbility () {
-        super(new ChancellorOfTheSpiresEffect());
+    ChancellorOfTheSpiresDelayedTriggeredAbility() {
+        super(new PutTopCardOfLibraryIntoGraveEachPlayerEffect(7, TargetController.OPPONENT));
     }
 
-    ChancellorOfTheSpiresDelayedTriggeredAbility(ChancellorOfTheSpiresDelayedTriggeredAbility ability) {
+    private ChancellorOfTheSpiresDelayedTriggeredAbility(ChancellorOfTheSpiresDelayedTriggeredAbility ability) {
         super(ability);
     }
 
@@ -82,43 +79,14 @@ class ChancellorOfTheSpiresDelayedTriggeredAbility extends DelayedTriggeredAbili
     public boolean checkEventType(GameEvent event, Game game) {
         return event.getType() == EventType.UPKEEP_STEP_PRE;
     }
-    
+
     @Override
     public boolean checkTrigger(GameEvent event, Game game) {
         return true;
     }
+
     @Override
     public ChancellorOfTheSpiresDelayedTriggeredAbility copy() {
         return new ChancellorOfTheSpiresDelayedTriggeredAbility(this);
     }
 }
-
-class ChancellorOfTheSpiresEffect extends OneShotEffect {
-
-    ChancellorOfTheSpiresEffect () {
-        super(Outcome.Benefit);
-        staticText = "each opponent puts the top seven cards of their library into their graveyard";
-    }
-
-    ChancellorOfTheSpiresEffect(ChancellorOfTheSpiresEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        for (UUID opponentId : game.getOpponents(source.getControllerId())) {
-            Player opponent = game.getPlayer(opponentId);
-            if (opponent != null) {
-                opponent.moveCards(opponent.getLibrary().getTopCards(game, 7), Zone.GRAVEYARD, source, game);
-            }
-        }
-        return true;
-    }
-
-    @Override
-    public ChancellorOfTheSpiresEffect copy() {
-        return new ChancellorOfTheSpiresEffect(this);
-    }
-
-}
-
diff --git a/Mage.Sets/src/mage/cards/c/ChillOfForeboding.java b/Mage.Sets/src/mage/cards/c/ChillOfForeboding.java
index 5f7d4323eb..98f9bbc67d 100644
--- a/Mage.Sets/src/mage/cards/c/ChillOfForeboding.java
+++ b/Mage.Sets/src/mage/cards/c/ChillOfForeboding.java
@@ -1,36 +1,32 @@
-
 package mage.cards.c;
 
-import java.util.UUID;
-import mage.abilities.Ability;
 import mage.abilities.costs.mana.ManaCostsImpl;
-import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.PutTopCardOfLibraryIntoGraveEachPlayerEffect;
 import mage.abilities.keyword.FlashbackAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.Outcome;
+import mage.constants.TargetController;
 import mage.constants.TimingRule;
-import mage.constants.Zone;
-import mage.game.Game;
-import mage.players.Player;
+
+import java.util.UUID;
 
 /**
- *
  * @author North
  */
 public final class ChillOfForeboding extends CardImpl {
 
     public ChillOfForeboding(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{U}");
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}");
 
         // Each player puts the top five cards of their library into their graveyard.
-        this.getSpellAbility().addEffect(new ChillOfForebodingEffect());
+        this.getSpellAbility().addEffect(new PutTopCardOfLibraryIntoGraveEachPlayerEffect(5, TargetController.ANY));
+
         // Flashback {7}{U}
         this.addAbility(new FlashbackAbility(new ManaCostsImpl("{7}{U}"), TimingRule.SORCERY));
     }
 
-    public ChillOfForeboding(final ChillOfForeboding card) {
+    private ChillOfForeboding(final ChillOfForeboding card) {
         super(card);
     }
 
@@ -39,32 +35,3 @@ public final class ChillOfForeboding extends CardImpl {
         return new ChillOfForeboding(this);
     }
 }
-
-class ChillOfForebodingEffect extends OneShotEffect {
-
-    public ChillOfForebodingEffect() {
-        super(Outcome.Detriment);
-        this.staticText = "Each player puts the top five cards of their library into their graveyard";
-    }
-
-    public ChillOfForebodingEffect(final ChillOfForebodingEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public ChillOfForebodingEffect copy() {
-        return new ChillOfForebodingEffect(this);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        Player sourcePlayer = game.getPlayer(source.getControllerId());
-        for (UUID playerId : game.getState().getPlayersInRange(sourcePlayer.getId(), game)) {
-            Player player = game.getPlayer(playerId);
-            if (player != null) {
-                player.moveCards(player.getLibrary().getTopCards(game, 5), Zone.GRAVEYARD, source, game);
-            }
-        }
-        return true;
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/c/ChronicFlooding.java b/Mage.Sets/src/mage/cards/c/ChronicFlooding.java
index 62f452388d..131327bd24 100644
--- a/Mage.Sets/src/mage/cards/c/ChronicFlooding.java
+++ b/Mage.Sets/src/mage/cards/c/ChronicFlooding.java
@@ -91,6 +91,6 @@ class ChronicFloodingAbility extends TriggeredAbilityImpl {
  
     @Override
     public String getRule() {
-        return "Whenever enchanted land becomes tapped, its controller puts the top three cards of their library into their graveyard.";
+        return "Whenever enchanted land becomes tapped, its controller mills three cards.";
     }
 }
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/c/CloudhoofKirin.java b/Mage.Sets/src/mage/cards/c/CloudhoofKirin.java
index c0fc199a8c..ea015a23d3 100644
--- a/Mage.Sets/src/mage/cards/c/CloudhoofKirin.java
+++ b/Mage.Sets/src/mage/cards/c/CloudhoofKirin.java
@@ -1,7 +1,6 @@
 
 package mage.cards.c;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.SpellCastControllerTriggeredAbility;
@@ -9,11 +8,7 @@ import mage.abilities.effects.OneShotEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.CardType;
-import mage.constants.SubType;
-import mage.constants.Outcome;
-import mage.constants.SuperType;
-import mage.constants.Zone;
+import mage.constants.*;
 import mage.filter.StaticFilters;
 import mage.game.Game;
 import mage.game.stack.Spell;
@@ -21,8 +16,9 @@ import mage.players.Player;
 import mage.target.Target;
 import mage.target.TargetPlayer;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class CloudhoofKirin extends CardImpl {
@@ -44,7 +40,7 @@ public final class CloudhoofKirin extends CardImpl {
         this.addAbility(ability);
     }
 
-    public CloudhoofKirin(final CloudhoofKirin card) {
+    private CloudhoofKirin(final CloudhoofKirin card) {
         super(card);
     }
 
@@ -56,12 +52,12 @@ public final class CloudhoofKirin extends CardImpl {
 
 class CloudhoofKirinEffect extends OneShotEffect {
 
-    public CloudhoofKirinEffect() {
+    CloudhoofKirinEffect() {
         super(Outcome.Detriment);
-        this.staticText = "you may have target player put the top X cards of their library into their graveyard, where X is that spell's converted mana cost";
+        this.staticText = "have target player mill X cards, where X is that spell's converted mana cost";
     }
 
-    public CloudhoofKirinEffect(final CloudhoofKirinEffect effect) {
+    private CloudhoofKirinEffect(final CloudhoofKirinEffect effect) {
         super(effect);
     }
 
@@ -80,8 +76,10 @@ class CloudhoofKirinEffect extends OneShotEffect {
                     targetPlayer = game.getPlayer(target.getFirstTarget());
                 }
             }
-            if (targetPlayer != null) {
-                return targetPlayer.moveCards(targetPlayer.getLibrary().getTopCards(game, spell.getConvertedManaCost()), Zone.GRAVEYARD, source, game);
+            int cmc = spell.getConvertedManaCost();
+            if (targetPlayer != null && cmc > 0) {
+                targetPlayer.millCards(cmc, source, game);
+                return true;
             }
         }
         return false;
diff --git a/Mage.Sets/src/mage/cards/c/CoercedConfession.java b/Mage.Sets/src/mage/cards/c/CoercedConfession.java
index 128ddb264f..db45358213 100644
--- a/Mage.Sets/src/mage/cards/c/CoercedConfession.java
+++ b/Mage.Sets/src/mage/cards/c/CoercedConfession.java
@@ -1,10 +1,10 @@
-
 package mage.cards.c;
 
-import java.util.UUID;
+import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
-import mage.cards.*;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.constants.Zone;
@@ -12,22 +12,23 @@ import mage.game.Game;
 import mage.players.Player;
 import mage.target.TargetPlayer;
 
+import java.util.Objects;
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class CoercedConfession extends CardImpl {
 
     public CoercedConfession(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{4}{U/B}");
-
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{U/B}");
 
         // Target player puts the top four cards of their library into their graveyard. You draw a card for each creature card put into a graveyard this way.
         getSpellAbility().addEffect(new CoercedConfessionMillEffect());
         getSpellAbility().addTarget(new TargetPlayer());
     }
 
-    public CoercedConfession(final CoercedConfession card) {
+    private CoercedConfession(final CoercedConfession card) {
         super(card);
     }
 
@@ -39,12 +40,12 @@ public final class CoercedConfession extends CardImpl {
 
 class CoercedConfessionMillEffect extends OneShotEffect {
 
-    public CoercedConfessionMillEffect() {
+    CoercedConfessionMillEffect() {
         super(Outcome.DrawCard);
-        this.staticText = "Target player puts the top four cards of their library into their graveyard. You draw a card for each creature card put into a graveyard this way";
+        this.staticText = "Target player mills four cards. You draw a card for each creature card put into their graveyard this way";
     }
 
-    public CoercedConfessionMillEffect(final CoercedConfessionMillEffect effect) {
+    private CoercedConfessionMillEffect(final CoercedConfessionMillEffect effect) {
         super(effect);
     }
 
@@ -56,24 +57,26 @@ class CoercedConfessionMillEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player player = game.getPlayer(targetPointer.getFirst(game, source));
-        if (player != null) {
-            int foundCreatures = 0;
-            Cards cards = new CardsImpl();
-            for(Card card: player.getLibrary().getTopCards(game, 4)) {
-                cards.add(card);
-                if (card.isCreature()) {
-                    ++foundCreatures;
-                }
-            }
-            player.moveCards(cards, Zone.GRAVEYARD, source, game);
-            if (foundCreatures > 0) {
-                Player controller = game.getPlayer(source.getControllerId());
-                if (controller != null) {
-                    controller.drawCards(foundCreatures, source.getSourceId(), game);
-                }
-            }
+        if (player == null) {
+            return false;
+        }
+        int creaturesMilled = player
+                .millCards(4, source, game)
+                .getCards(game)
+                .stream()
+                .filter(Objects::nonNull)
+                .filter(card -> game.getState().getZone(card.getId()) == Zone.GRAVEYARD)
+                .filter(MageObject::isCreature)
+                .mapToInt(x -> 1)
+                .sum();
+        if (creaturesMilled < 1) {
             return true;
         }
-        return false;
+        Player controller = game.getPlayer(source.getControllerId());
+        if (controller == null) {
+            return true;
+        }
+        controller.drawCards(creaturesMilled, source.getSourceId(), game);
+        return true;
     }
 }
diff --git a/Mage.Sets/src/mage/cards/c/CombustibleGearhulk.java b/Mage.Sets/src/mage/cards/c/CombustibleGearhulk.java
index f8d33deabe..dbc8f48801 100644
--- a/Mage.Sets/src/mage/cards/c/CombustibleGearhulk.java
+++ b/Mage.Sets/src/mage/cards/c/CombustibleGearhulk.java
@@ -1,24 +1,22 @@
 package mage.cards.c;
 
 import mage.MageInt;
+import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.abilities.keyword.FirstStrikeAbility;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.constants.SubType;
-import mage.constants.Zone;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
 import mage.players.Player;
 import mage.target.common.TargetOpponent;
 
-import java.util.Set;
 import java.util.UUID;
 
 /**
@@ -97,7 +95,7 @@ class CombustibleGearhulkMillAndDamageEffect extends OneShotEffect {
 
     public CombustibleGearhulkMillAndDamageEffect() {
         super(Outcome.Damage);
-        staticText = "put the top three cards of your library into your graveyard, then {this} deals damage to that player equal to the total converted mana cost of those cards.";
+        staticText = "mill three cards, then {this} deals damage to that player equal to the total converted mana cost of those cards.";
     }
 
     public CombustibleGearhulkMillAndDamageEffect(final CombustibleGearhulkMillAndDamageEffect effect) {
@@ -108,13 +106,12 @@ class CombustibleGearhulkMillAndDamageEffect extends OneShotEffect {
     public boolean apply(Game game, Ability source) {
         Player controller = game.getPlayer(source.getControllerId());
         if (controller != null) {
-            int sumCMC = 0;
-            Set<Card> cardList = controller.getLibrary().getTopCards(game, 3);
-            for (Card card : cardList) {
-                int test = card.getConvertedManaCost();
-                sumCMC += test;
-            }
-            controller.moveCards(cardList, Zone.GRAVEYARD, source, game);
+            int sumCMC = controller
+                    .millCards(3, source, game)
+                    .getCards(game)
+                    .stream()
+                    .mapToInt(MageObject::getConvertedManaCost)
+                    .sum();
             Player targetPlayer = game.getPlayer(targetPointer.getFirst(game, source));
             if (targetPlayer != null) {
                 targetPlayer.damage(sumCMC, source.getSourceId(), game);
diff --git a/Mage.Sets/src/mage/cards/c/Countermand.java b/Mage.Sets/src/mage/cards/c/Countermand.java
index 5c20b25a40..d2dcff8740 100644
--- a/Mage.Sets/src/mage/cards/c/Countermand.java
+++ b/Mage.Sets/src/mage/cards/c/Countermand.java
@@ -1,28 +1,27 @@
 
 package mage.cards.c;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.Outcome;
-import mage.constants.Zone;
 import mage.filter.StaticFilters;
 import mage.game.Game;
 import mage.game.stack.StackObject;
 import mage.players.Player;
 import mage.target.TargetSpell;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class Countermand extends CardImpl {
 
     public Countermand(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{U}{U}");
+        super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}{U}");
 
 
         // Counter target spell. Its controller puts the top four cards of their library into their graveyard.
@@ -39,6 +38,7 @@ public final class Countermand extends CardImpl {
         return new Countermand(this);
     }
 }
+
 class CountermandEffect extends OneShotEffect {
 
     public CountermandEffect() {
@@ -65,7 +65,7 @@ class CountermandEffect extends OneShotEffect {
         if (stackObject != null) {
             Player controller = game.getPlayer(stackObject.getControllerId());
             if (controller != null) {
-                controller.moveCards(controller.getLibrary().getTopCards(game, 4), Zone.GRAVEYARD, source, game);
+                controller.millCards(4, source, game);
             }
         }
         return countered;
diff --git a/Mage.Sets/src/mage/cards/c/CrosstownCourier.java b/Mage.Sets/src/mage/cards/c/CrosstownCourier.java
index 093357a261..bb29e3e698 100644
--- a/Mage.Sets/src/mage/cards/c/CrosstownCourier.java
+++ b/Mage.Sets/src/mage/cards/c/CrosstownCourier.java
@@ -80,7 +80,7 @@ public final class CrosstownCourier extends CardImpl {
 
         @Override
         public String getRule() {
-            return "Whenever {this} deals combat damage to a player, that player puts that many cards from the top of their library into their graveyard.";
+            return "Whenever {this} deals combat damage to a player, that player mills that many cards.";
         }
     }
 }
diff --git a/Mage.Sets/src/mage/cards/c/CurseOfTheBloodyTome.java b/Mage.Sets/src/mage/cards/c/CurseOfTheBloodyTome.java
index 1d12b1daf5..b1c0bd8ba6 100644
--- a/Mage.Sets/src/mage/cards/c/CurseOfTheBloodyTome.java
+++ b/Mage.Sets/src/mage/cards/c/CurseOfTheBloodyTome.java
@@ -88,6 +88,6 @@ class CurseOfTheBloodyTomeAbility extends TriggeredAbilityImpl {
 
     @Override
     public String getRule() {
-        return "At the beginning of enchanted player's upkeep, that player puts the top two cards of their library into their graveyard.";
+        return "At the beginning of enchanted player's upkeep, that player mills two cards.";
     }
 }
diff --git a/Mage.Sets/src/mage/cards/d/DeepSpawn.java b/Mage.Sets/src/mage/cards/d/DeepSpawn.java
index 1c54645f9c..3a78baa9fb 100644
--- a/Mage.Sets/src/mage/cards/d/DeepSpawn.java
+++ b/Mage.Sets/src/mage/cards/d/DeepSpawn.java
@@ -39,7 +39,7 @@ public final class DeepSpawn extends CardImpl {
         this.addAbility(TrampleAbility.getInstance());
         // At the beginning of your upkeep, sacrifice Deep Spawn unless you put the top two cards of your library into your graveyard.
         Effect effect = new SacrificeSourceUnlessPaysEffect(new PutTopCardOfYourLibraryToGraveyardCost(2));
-        effect.setText("sacrifice {this} unless you put the top two cards of your library into your graveyard");
+        effect.setText("sacrifice {this} unless you mill two cards");
         this.addAbility(new BeginningOfUpkeepTriggeredAbility(effect, TargetController.YOU, false));
         // {U}: Deep Spawn gains shroud until end of turn and doesn't untap during your next untap step. Tap Deep Spawn.
         Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainAbilitySourceEffect(
diff --git a/Mage.Sets/src/mage/cards/d/DidntSayPlease.java b/Mage.Sets/src/mage/cards/d/DidntSayPlease.java
index f4e6a186b5..96f5edc363 100644
--- a/Mage.Sets/src/mage/cards/d/DidntSayPlease.java
+++ b/Mage.Sets/src/mage/cards/d/DidntSayPlease.java
@@ -8,7 +8,6 @@ import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.Outcome;
-import mage.constants.Zone;
 import mage.game.Game;
 import mage.players.Player;
 import mage.target.TargetSpell;
@@ -44,8 +43,7 @@ class DidntSayPleaseEffect extends OneShotEffect {
 
     DidntSayPleaseEffect() {
         super(Outcome.Benefit);
-        staticText = "Counter target spell. Its controller puts " +
-                "the top three cards of their library into their graveyard.";
+        staticText = "Counter target spell. Its controller mills three cards.";
     }
 
     private DidntSayPleaseEffect(final DidntSayPleaseEffect effect) {
@@ -63,7 +61,7 @@ class DidntSayPleaseEffect extends OneShotEffect {
         if (player == null) {
             return false;
         }
-        player.moveCards(player.getLibrary().getTopCards(game, 3), Zone.GRAVEYARD, source, game);
+        player.millCards(3, source, game);
         return effect.apply(game, source);
     }
 }
diff --git a/Mage.Sets/src/mage/cards/d/DreadSummons.java b/Mage.Sets/src/mage/cards/d/DreadSummons.java
index 0367d259c0..8a2812efbf 100644
--- a/Mage.Sets/src/mage/cards/d/DreadSummons.java
+++ b/Mage.Sets/src/mage/cards/d/DreadSummons.java
@@ -1,23 +1,22 @@
-
 package mage.cards.d;
 
-import java.util.Set;
-import java.util.UUID;
+import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
-import mage.abilities.effects.common.CreateTokenEffect;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.constants.Zone;
 import mage.game.Game;
+import mage.game.permanent.token.Token;
 import mage.game.permanent.token.ZombieToken;
 import mage.players.Player;
 
+import java.util.Objects;
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class DreadSummons extends CardImpl {
@@ -26,10 +25,10 @@ public final class DreadSummons extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{B}{B}");
 
         // Each player puts the top X cards of their library into their graveyard. For each creature card put into a graveyard this way, you create a tapped 2/2 black Zombie creature token.
-        getSpellAbility().addEffect(new DreadSummonsEffect());
+        this.getSpellAbility().addEffect(new DreadSummonsEffect());
     }
 
-    public DreadSummons(final DreadSummons card) {
+    private DreadSummons(final DreadSummons card) {
         super(card);
     }
 
@@ -41,12 +40,15 @@ public final class DreadSummons extends CardImpl {
 
 class DreadSummonsEffect extends OneShotEffect {
 
-    public DreadSummonsEffect() {
+    private static final Token token = new ZombieToken();
+
+    DreadSummonsEffect() {
         super(Outcome.PutCreatureInPlay);
-        this.staticText = "Each player puts the top X cards of their library into their graveyard. For each creature card put into a graveyard this way, you create a tapped 2/2 black Zombie creature token";
+        this.staticText = "each player mills X cards. For each creature card put into a graveyard this way, " +
+                "you create a tapped 2/2 black Zombie creature token";
     }
 
-    public DreadSummonsEffect(final DreadSummonsEffect effect) {
+    private DreadSummonsEffect(final DreadSummonsEffect effect) {
         super(effect);
     }
 
@@ -57,28 +59,24 @@ class DreadSummonsEffect extends OneShotEffect {
 
     @Override
     public boolean apply(Game game, Ability source) {
-        Player controller = game.getPlayer(source.getControllerId());
-        if (controller != null) {
-            int numberOfCards = source.getManaCostsToPay().getX();
-            if (numberOfCards > 0) {
-                int numberOfCreatureCards = 0;
-                for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) {
-                    Player player = game.getPlayer(playerId);
-                    if (player != null) {
-                        Set<Card> movedCards = player.moveCardsToGraveyardWithInfo(player.getLibrary().getTopCards(game, numberOfCards), source, game, Zone.LIBRARY);
-                        for (Card card : movedCards) {
-                            if (card.isCreature()) {
-                                numberOfCreatureCards++;
-                            }
-                        }
-                    }
-                }
-                if (numberOfCreatureCards > 0) {
-                    return new CreateTokenEffect(new ZombieToken(), numberOfCreatureCards, true, false).apply(game, source);
-                }
+        int creatureCount = 0;
+        for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) {
+            Player player = game.getPlayer(playerId);
+            if (player == null) {
+                continue;
             }
-            return true;
+            creatureCount += player
+                    .millCards(source.getManaCostsToPay().getX(), source, game)
+                    .getCards(game)
+                    .stream()
+                    .filter(Objects::nonNull)
+                    .filter(card -> game.getState().getZone(card.getId()) == Zone.GRAVEYARD)
+                    .filter(MageObject::isCreature)
+                    .count();
         }
-        return false;
+        if (creatureCount > 0) {
+            token.putOntoBattlefield(creatureCount, game, source.getSourceId(), source.getControllerId());
+        }
+        return true;
     }
 }
diff --git a/Mage.Sets/src/mage/cards/d/DreambornMuse.java b/Mage.Sets/src/mage/cards/d/DreambornMuse.java
index 169107a55c..1ea48de2e1 100644
--- a/Mage.Sets/src/mage/cards/d/DreambornMuse.java
+++ b/Mage.Sets/src/mage/cards/d/DreambornMuse.java
@@ -1,7 +1,5 @@
-
 package mage.cards.d;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
 import mage.abilities.dynamicvalue.common.CardsInTargetPlayerHandCount;
@@ -12,26 +10,26 @@ import mage.constants.CardType;
 import mage.constants.SubType;
 import mage.constants.TargetController;
 
+import java.util.UUID;
+
 /**
- *
  * @author cbrianhill
  */
 public final class DreambornMuse extends CardImpl {
 
     public DreambornMuse(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{U}{U}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}{U}");
         this.subtype.add(SubType.SPIRIT);
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
-        
+
         // At the beginning of each player's upkeep, that player puts the top X cards of their library into their graveyard, where X is the number of cards in their hand.
         PutLibraryIntoGraveTargetEffect effect = new PutLibraryIntoGraveTargetEffect(CardsInTargetPlayerHandCount.instance);
-        effect.setText("that player puts the top X cards of their library into their graveyard, where X is the number of cards in their hand.");
+        effect.setText("that player mills X cards, where X is the number of cards in their hand");
         this.addAbility(new BeginningOfUpkeepTriggeredAbility(effect, TargetController.ANY, false));
-        
     }
 
-    public DreambornMuse(final DreambornMuse card) {
+    private DreambornMuse(final DreambornMuse card) {
         super(card);
     }
 
diff --git a/Mage.Sets/src/mage/cards/d/DrownInFilth.java b/Mage.Sets/src/mage/cards/d/DrownInFilth.java
index c9befad3dd..5513828419 100644
--- a/Mage.Sets/src/mage/cards/d/DrownInFilth.java
+++ b/Mage.Sets/src/mage/cards/d/DrownInFilth.java
@@ -28,7 +28,7 @@ public final class DrownInFilth extends CardImpl {
         // Choose target creature. Put the top four cards of your library into your graveyard, then that creature gets -1/-1 until end of turn for each land card in your graveyard.
         this.getSpellAbility().addTarget(new TargetCreaturePermanent());
         Effect effect = new PutTopCardOfLibraryIntoGraveControllerEffect(4);
-        effect.setText("Choose target creature. Put the top four cards of your library into your graveyard");
+        effect.setText("Choose target creature. Mill four cards");
         this.getSpellAbility().addEffect(effect);
         DynamicValue landCards = new SignInversionDynamicValue(new CardsInControllerGraveyardCount(new FilterLandCard()));
         this.getSpellAbility().addEffect(new BoostTargetEffect(landCards, landCards, Duration.EndOfTurn));
diff --git a/Mage.Sets/src/mage/cards/e/EnterTheGodEternals.java b/Mage.Sets/src/mage/cards/e/EnterTheGodEternals.java
index fe6bdebf9b..5db22b3b0b 100644
--- a/Mage.Sets/src/mage/cards/e/EnterTheGodEternals.java
+++ b/Mage.Sets/src/mage/cards/e/EnterTheGodEternals.java
@@ -46,7 +46,7 @@ class EnterTheGodEternalsEffect extends OneShotEffect {
     EnterTheGodEternalsEffect() {
         super(Outcome.Benefit);
         staticText = "{this} deals 4 damage to target creature and you gain life equal to the damage dealt this way. " +
-                "Target player puts the top four cards of their library into their graveyard. Amass 4.";
+                "Target player mills four cards. Amass 4.";
     }
 
     private EnterTheGodEternalsEffect(final EnterTheGodEternalsEffect effect) {
diff --git a/Mage.Sets/src/mage/cards/e/ExtractFromDarkness.java b/Mage.Sets/src/mage/cards/e/ExtractFromDarkness.java
index 60fa0cc682..92e77183b5 100644
--- a/Mage.Sets/src/mage/cards/e/ExtractFromDarkness.java
+++ b/Mage.Sets/src/mage/cards/e/ExtractFromDarkness.java
@@ -1,12 +1,13 @@
 package mage.cards.e;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.PutTopCardOfLibraryIntoGraveEachPlayerEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.Outcome;
+import mage.constants.TargetController;
 import mage.constants.Zone;
 import mage.filter.StaticFilters;
 import mage.game.Game;
@@ -14,8 +15,9 @@ import mage.players.Player;
 import mage.target.Target;
 import mage.target.common.TargetCardInGraveyard;
 
+import java.util.UUID;
+
 /**
- *
  * @author fireshoes
  */
 public final class ExtractFromDarkness extends CardImpl {
@@ -24,12 +26,13 @@ public final class ExtractFromDarkness extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{U}{B}");
 
         // Each player puts the top two cards of their library into their graveyard.
-        this.getSpellAbility().addEffect(new ExtractFromDarknessMillEffect());
+        this.getSpellAbility().addEffect(new PutTopCardOfLibraryIntoGraveEachPlayerEffect(2, TargetController.ANY));
+
         // Then put a creature card from a graveyard onto the battlefield under your control.
-        this.getSpellAbility().addEffect(new ExtractFromDarknessReturnFromGraveyardToBattlefieldEffect());
+        this.getSpellAbility().addEffect(new ExtractFromDarknessEffect());
     }
 
-    public ExtractFromDarkness(final ExtractFromDarkness card) {
+    private ExtractFromDarkness(final ExtractFromDarkness card) {
         super(card);
     }
 
@@ -39,48 +42,20 @@ public final class ExtractFromDarkness extends CardImpl {
     }
 }
 
-class ExtractFromDarknessMillEffect extends OneShotEffect {
+class ExtractFromDarknessEffect extends OneShotEffect {
 
-    ExtractFromDarknessMillEffect() {
-        super(Outcome.Detriment);
-        staticText = "Each player puts the top two cards of their library into their graveyard";
-    }
-
-    ExtractFromDarknessMillEffect(final ExtractFromDarknessMillEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) {
-            Player player = game.getPlayer(playerId);
-            if (player != null) {
-                player.moveCards(player.getLibrary().getTopCards(game, 2), Zone.GRAVEYARD, source, game);
-            }
-        }
-        return true;
-    }
-
-    @Override
-    public ExtractFromDarknessMillEffect copy() {
-        return new ExtractFromDarknessMillEffect(this);
-    }
-}
-
-class ExtractFromDarknessReturnFromGraveyardToBattlefieldEffect extends OneShotEffect {
-
-    public ExtractFromDarknessReturnFromGraveyardToBattlefieldEffect() {
+    ExtractFromDarknessEffect() {
         super(Outcome.PutCreatureInPlay);
         staticText = "Put a creature card from a graveyard onto the battlefield under your control";
     }
 
-    public ExtractFromDarknessReturnFromGraveyardToBattlefieldEffect(final ExtractFromDarknessReturnFromGraveyardToBattlefieldEffect effect) {
+    private ExtractFromDarknessEffect(final ExtractFromDarknessEffect effect) {
         super(effect);
     }
 
     @Override
-    public ExtractFromDarknessReturnFromGraveyardToBattlefieldEffect copy() {
-        return new ExtractFromDarknessReturnFromGraveyardToBattlefieldEffect(this);
+    public ExtractFromDarknessEffect copy() {
+        return new ExtractFromDarknessEffect(this);
     }
 
     @Override
diff --git a/Mage.Sets/src/mage/cards/e/EyeCollector.java b/Mage.Sets/src/mage/cards/e/EyeCollector.java
index 708c9bec08..757aa98757 100644
--- a/Mage.Sets/src/mage/cards/e/EyeCollector.java
+++ b/Mage.Sets/src/mage/cards/e/EyeCollector.java
@@ -1,22 +1,16 @@
 package mage.cards.e;
 
 import mage.MageInt;
-import mage.abilities.Ability;
 import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
-import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.PutTopCardOfLibraryIntoGraveEachPlayerEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.cards.CardsImpl;
 import mage.constants.CardType;
-import mage.constants.Outcome;
 import mage.constants.SubType;
-import mage.constants.Zone;
-import mage.game.Game;
-import mage.players.Player;
+import mage.constants.TargetController;
 
 import java.util.UUID;
-import java.util.stream.Collectors;
 
 /**
  * @author TheElk801
@@ -34,7 +28,7 @@ public final class EyeCollector extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
 
         // Whenever Eye Collector deals combat damage to a player, each player puts the top card of their library into their graveyard.
-        this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new EyeCollectorEffect(), false));
+        this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new PutTopCardOfLibraryIntoGraveEachPlayerEffect(1, TargetController.ANY), false));
     }
 
     private EyeCollector(final EyeCollector card) {
@@ -46,37 +40,3 @@ public final class EyeCollector extends CardImpl {
         return new EyeCollector(this);
     }
 }
-
-class EyeCollectorEffect extends OneShotEffect {
-
-    EyeCollectorEffect() {
-        super(Outcome.Benefit);
-        staticText = "each player puts the top card of their library into their graveyard";
-    }
-
-    private EyeCollectorEffect(final EyeCollectorEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public EyeCollectorEffect copy() {
-        return new EyeCollectorEffect(this);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        Player controller = game.getPlayer(source.getControllerId());
-        if (controller == null) {
-            return false;
-        }
-        return controller.moveCards(new CardsImpl(game.getState()
-                .getPlayersInRange(controller.getId(), game)
-                .stream()
-                .map(game::getPlayer)
-                .filter(player -> player != null)
-                .map(Player::getLibrary)
-                .map(library -> library.getFromTop(game))
-                .collect(Collectors.toSet())
-        ), Zone.GRAVEYARD, source, game);
-    }
-}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/f/FlintGolem.java b/Mage.Sets/src/mage/cards/f/FlintGolem.java
index f78c9e2e67..53fd61c3ae 100644
--- a/Mage.Sets/src/mage/cards/f/FlintGolem.java
+++ b/Mage.Sets/src/mage/cards/f/FlintGolem.java
@@ -46,7 +46,7 @@ class FlintGolemEffect extends OneShotEffect {
 
     public FlintGolemEffect() {
         super(Outcome.Detriment);
-        this.staticText = "defending player puts the top three cards of their library into their graveyard";
+        this.staticText = "defending player mills three cards";
     }
 
     public FlintGolemEffect(final FlintGolemEffect effect) {
diff --git a/Mage.Sets/src/mage/cards/f/FolioOfFancies.java b/Mage.Sets/src/mage/cards/f/FolioOfFancies.java
index 191fe2c06d..1f81cc0be1 100644
--- a/Mage.Sets/src/mage/cards/f/FolioOfFancies.java
+++ b/Mage.Sets/src/mage/cards/f/FolioOfFancies.java
@@ -9,18 +9,16 @@ import mage.abilities.dynamicvalue.common.ManacostVariableValue;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.DrawCardAllEffect;
 import mage.abilities.effects.common.continuous.MaximumHandSizeControllerEffect;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.*;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.Outcome;
+import mage.constants.TargetController;
 import mage.game.Game;
 import mage.players.Player;
 
-import java.util.Collection;
-import java.util.Objects;
-import java.util.Set;
 import java.util.UUID;
-import java.util.stream.Collectors;
 
 /**
  * @author TheElk801
@@ -63,8 +61,7 @@ class FolioOfFanciesEffect extends OneShotEffect {
 
     FolioOfFanciesEffect() {
         super(Outcome.Benefit);
-        staticText = "Each opponent puts a number of cards equal to the number of cards in their hand " +
-                "from the top of their library into their graveyard.";
+        staticText = "each opponent mills cards equal to the number of cards in their hand";
     }
 
     private FolioOfFanciesEffect(final FolioOfFanciesEffect effect) {
@@ -82,14 +79,13 @@ class FolioOfFanciesEffect extends OneShotEffect {
         if (controller == null) {
             return false;
         }
-        Set<Card> cards = game.getOpponents(source.getControllerId())
-                .stream()
-                .map(game::getPlayer)
-                .filter(Objects::nonNull)
-                .filter(player -> !player.getHand().isEmpty())
-                .map(player -> player.getLibrary().getTopCards(game, player.getHand().size()))
-                .flatMap(Collection::stream)
-                .collect(Collectors.toSet());
-        return controller.moveCards(cards, Zone.GRAVEYARD, source, game);
+        for (UUID playerId : game.getOpponents(source.getControllerId())) {
+            Player player = game.getPlayer(playerId);
+            if (player == null) {
+                continue;
+            }
+            player.millCards(player.getHand().size(), source, game);
+        }
+        return true;
     }
 }
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/f/Foreshadow.java b/Mage.Sets/src/mage/cards/f/Foreshadow.java
index b3f778e9b3..2ac311b6f3 100644
--- a/Mage.Sets/src/mage/cards/f/Foreshadow.java
+++ b/Mage.Sets/src/mage/cards/f/Foreshadow.java
@@ -11,7 +11,6 @@ import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.Outcome;
-import mage.constants.Zone;
 import mage.game.Game;
 import mage.players.Player;
 import mage.target.common.TargetOpponent;
@@ -49,12 +48,12 @@ public final class Foreshadow extends CardImpl {
 
 class ForeshadowEffect extends OneShotEffect {
 
-    public ForeshadowEffect() {
+    ForeshadowEffect() {
         super(Outcome.DrawCard);
-        this.staticText = "target opponent puts the top card of their library into their graveyard. If that card has the chosen name, you draw a card";
+        this.staticText = "target opponent mills a card. If that card has the chosen name, you draw a card";
     }
 
-    public ForeshadowEffect(final ForeshadowEffect effect) {
+    private ForeshadowEffect(final ForeshadowEffect effect) {
         super(effect);
     }
 
@@ -68,17 +67,16 @@ class ForeshadowEffect extends OneShotEffect {
         Player controller = game.getPlayer(source.getControllerId());
         Player targetPlayer = game.getPlayer(source.getFirstTarget());
         String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY);
-        if (controller != null && targetPlayer != null && cardName != null && !cardName.isEmpty()) {
-            Card card = targetPlayer.getLibrary().getFromTop(game);
-            if (card != null) {
-                controller.moveCards(card, Zone.GRAVEYARD, source, game);
-                if (CardUtil.haveSameNames(card, cardName, game)) {
-                    controller.drawCards(1, source.getSourceId(), game);
-                }
-            }
-            return true;
+        if (controller == null || targetPlayer == null || cardName == null || cardName.isEmpty()) {
+            return false;
         }
-        return false;
+        for (Card card : targetPlayer.millCards(1, source, game).getCards(game)) {
+            if (CardUtil.haveSameNames(card, cardName, game)) {
+                controller.drawCards(1, source.getSourceId(), game);
+                break;
+            }
+        }
+        return true;
     }
 
 }
diff --git a/Mage.Sets/src/mage/cards/f/FrayingSanity.java b/Mage.Sets/src/mage/cards/f/FrayingSanity.java
index 56f444ed24..92ea0d9933 100644
--- a/Mage.Sets/src/mage/cards/f/FrayingSanity.java
+++ b/Mage.Sets/src/mage/cards/f/FrayingSanity.java
@@ -83,7 +83,7 @@ class FrayingSanityTriggeredAbility extends TriggeredAbilityImpl {
 
     @Override
     public String getRule() {
-        return "At the beginning of each end step, enchanted player puts the top X cards of their library into their graveyard, where X is the total number of cards put into their graveyard from anywhere this turn.";
+        return "At the beginning of each end step, enchanted player mills X cards, where X is the total number of cards put into their graveyard from anywhere this turn.";
     }
 }
 
@@ -117,8 +117,8 @@ class FrayingSanityEffect extends OneShotEffect {
             if (watcher != null) {
                 xAmount = watcher.getAmountCardsPutToGraveyard(enchantedPlayer.getId());
             }
-            Set<Card> topXCardsFromLibrary = enchantedPlayer.getLibrary().getTopCards(game, xAmount);
-            return enchantedPlayer.moveCards(topXCardsFromLibrary, Zone.GRAVEYARD, source, game, false, false, true, null);
+            enchantedPlayer.millCards(xAmount, source, game);
+            return true;
         }
         return false;
     }
diff --git a/Mage.Sets/src/mage/cards/g/GethLordOfTheVault.java b/Mage.Sets/src/mage/cards/g/GethLordOfTheVault.java
index 3db2f0e8b7..6d96b9f29f 100644
--- a/Mage.Sets/src/mage/cards/g/GethLordOfTheVault.java
+++ b/Mage.Sets/src/mage/cards/g/GethLordOfTheVault.java
@@ -1,6 +1,5 @@
 package mage.cards.g;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleActivatedAbility;
@@ -10,12 +9,7 @@ import mage.abilities.keyword.IntimidateAbility;
 import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.CardType;
-import mage.constants.SubType;
-import mage.constants.Outcome;
-import mage.constants.SuperType;
-import mage.constants.TargetController;
-import mage.constants.Zone;
+import mage.constants.*;
 import mage.filter.FilterCard;
 import mage.filter.predicate.Predicates;
 import mage.game.Game;
@@ -23,6 +17,8 @@ import mage.players.Player;
 import mage.target.common.TargetCardInGraveyard;
 import mage.target.targetadjustment.XCMCGraveyardAdjuster;
 
+import java.util.UUID;
+
 /**
  * @author nantuko
  */
@@ -70,7 +66,7 @@ class GethLordOfTheVaultEffect extends OneShotEffect {
 
     public GethLordOfTheVaultEffect() {
         super(Outcome.Benefit);
-        staticText = "Put target artifact or creature card with converted mana cost X from an opponent's graveyard onto the battlefield under your control tapped. Then that player puts the top X cards of their library into their graveyard";
+        staticText = "Put target artifact or creature card with converted mana cost X from an opponent's graveyard onto the battlefield under your control tapped. Then that player mills X cards";
     }
 
     public GethLordOfTheVaultEffect(final GethLordOfTheVaultEffect effect) {
@@ -80,18 +76,19 @@ class GethLordOfTheVaultEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player controller = game.getPlayer(source.getControllerId());
-        if (controller != null) {
-            Card card = game.getCard(getTargetPointer().getFirst(game, source));
-            if (card != null) {
-                controller.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, false, null);
-                Player player = game.getPlayer(card.getOwnerId());
-                if (player != null) {
-                    player.moveCards(player.getLibrary().getTopCards(game, card.getConvertedManaCost()), Zone.GRAVEYARD, source, game);
-                }
-            }
+        if (controller == null) {
+            return false;
+        }
+        Card card = game.getCard(getTargetPointer().getFirst(game, source));
+        if (card == null) {
             return true;
         }
-        return false;
+        controller.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, false, null);
+        Player player = game.getPlayer(card.getOwnerId());
+        if (player != null) {
+            player.millCards(card.getConvertedManaCost(), source, game);
+        }
+        return true;
     }
 
     @Override
diff --git a/Mage.Sets/src/mage/cards/g/GhoulcallersBell.java b/Mage.Sets/src/mage/cards/g/GhoulcallersBell.java
index 6ad15678fb..e7638cd5de 100644
--- a/Mage.Sets/src/mage/cards/g/GhoulcallersBell.java
+++ b/Mage.Sets/src/mage/cards/g/GhoulcallersBell.java
@@ -1,22 +1,16 @@
-
 package mage.cards.g;
 
-import java.util.UUID;
-import mage.abilities.Ability;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.common.TapSourceCost;
-import mage.abilities.effects.OneShotEffect;
-import mage.cards.Card;
+import mage.abilities.effects.common.PutTopCardOfLibraryIntoGraveEachPlayerEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.Outcome;
-import mage.constants.Zone;
-import mage.game.Game;
-import mage.players.Player;
+import mage.constants.TargetController;
+
+import java.util.UUID;
 
 /**
- *
  * @author North
  */
 public final class GhoulcallersBell extends CardImpl {
@@ -25,10 +19,10 @@ public final class GhoulcallersBell extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}");
 
         // {T}: Each player puts the top card of their library into their graveyard.
-        this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new GhoulcallersBellEffect(), new TapSourceCost()));
+        this.addAbility(new SimpleActivatedAbility(new PutTopCardOfLibraryIntoGraveEachPlayerEffect(1, TargetController.ANY), new TapSourceCost()));
     }
 
-    public GhoulcallersBell(final GhoulcallersBell card) {
+    private GhoulcallersBell(final GhoulcallersBell card) {
         super(card);
     }
 
@@ -37,34 +31,3 @@ public final class GhoulcallersBell extends CardImpl {
         return new GhoulcallersBell(this);
     }
 }
-
-class GhoulcallersBellEffect extends OneShotEffect {
-
-    public GhoulcallersBellEffect() {
-        super(Outcome.Discard);
-        this.staticText = "Each player puts the top card of their library into their graveyard";
-    }
-
-    public GhoulcallersBellEffect(final GhoulcallersBellEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public GhoulcallersBellEffect copy() {
-        return new GhoulcallersBellEffect(this);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) {
-            Player player = game.getPlayer(playerId);
-            if (player != null) {
-                Card card = player.getLibrary().getFromTop(game);
-                if (card != null) {
-                    player.moveCards(card, Zone.GRAVEYARD, source, game);
-                }
-            }
-        }
-        return true;
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/g/GorgingVulture.java b/Mage.Sets/src/mage/cards/g/GorgingVulture.java
index 5f042ced90..1c35c788b6 100644
--- a/Mage.Sets/src/mage/cards/g/GorgingVulture.java
+++ b/Mage.Sets/src/mage/cards/g/GorgingVulture.java
@@ -5,7 +5,9 @@ import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.keyword.FlyingAbility;
-import mage.cards.*;
+import mage.cards.Card;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.constants.SubType;
@@ -48,8 +50,7 @@ class GorgingVultureEffect extends OneShotEffect {
 
     GorgingVultureEffect() {
         super(Outcome.Benefit);
-        staticText = "put the top four cards of your library into your graveyard. " +
-                "You gain 1 life for each creature card put into your graveyard this way.";
+        staticText = "mill four cards. You gain 1 life for each creature card put into your graveyard this way.";
     }
 
     private GorgingVultureEffect(final GorgingVultureEffect effect) {
@@ -67,9 +68,8 @@ class GorgingVultureEffect extends OneShotEffect {
         if (player == null) {
             return false;
         }
-        Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, 4));
-        player.moveCards(cards, Zone.GRAVEYARD, source, game);
-        int lifeToGain = cards
+        int lifeToGain = player
+                .millCards(4, source, game)
                 .getCards(game)
                 .stream()
                 .filter(Card::isCreature)
diff --git a/Mage.Sets/src/mage/cards/g/GraveStrength.java b/Mage.Sets/src/mage/cards/g/GraveStrength.java
index 229eef94cb..84bdb9d410 100644
--- a/Mage.Sets/src/mage/cards/g/GraveStrength.java
+++ b/Mage.Sets/src/mage/cards/g/GraveStrength.java
@@ -24,7 +24,7 @@ public final class GraveStrength extends CardImpl {
         // Choose target creature. Put the top three cards of your library into your graveyard, then put a +1/+1 counter on that creature for each creature card in your graveyard.
         this.getSpellAbility().addTarget(new TargetCreaturePermanent());
         Effect effect = new PutTopCardOfLibraryIntoGraveControllerEffect(3);
-        effect.setText("Choose target creature. Put the top three cards of your library into your graveyard");
+        effect.setText("Choose target creature. Mill three cards");
         this.getSpellAbility().addEffect(effect);
         effect = new AddCountersTargetEffect(CounterType.P1P1.createInstance(0), new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_CREATURE));
         effect.setText(", then put a +1/+1 counter on that creature for each creature card in your graveyard");
diff --git a/Mage.Sets/src/mage/cards/g/Grindclock.java b/Mage.Sets/src/mage/cards/g/Grindclock.java
index 0a873bff09..26b5840587 100644
--- a/Mage.Sets/src/mage/cards/g/Grindclock.java
+++ b/Mage.Sets/src/mage/cards/g/Grindclock.java
@@ -1,7 +1,6 @@
 
 package mage.cards.g;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.common.TapSourceCost;
@@ -18,8 +17,9 @@ import mage.game.permanent.Permanent;
 import mage.players.Player;
 import mage.target.TargetPlayer;
 
+import java.util.UUID;
+
 /**
- *
  * @author Loki
  */
 public final class Grindclock extends CardImpl {
@@ -62,7 +62,7 @@ class GrindclockEffect extends OneShotEffect {
             int amount = sourceObject.getCounters(game).getCount(CounterType.CHARGE);
             Player targetPlayer = game.getPlayer(source.getFirstTarget());
             if (targetPlayer != null) {
-                targetPlayer.moveCards(targetPlayer.getLibrary().getTopCards(game, amount), Zone.GRAVEYARD, source, game);
+                targetPlayer.millCards(amount, source, game);
                 return true;
             }
         }
diff --git a/Mage.Sets/src/mage/cards/g/Grindstone.java b/Mage.Sets/src/mage/cards/g/Grindstone.java
index 1b7c1e738d..2e5d271aac 100644
--- a/Mage.Sets/src/mage/cards/g/Grindstone.java
+++ b/Mage.Sets/src/mage/cards/g/Grindstone.java
@@ -1,22 +1,25 @@
-
 package mage.cards.g;
 
-import java.util.UUID;
+import mage.ObjectColor;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.common.TapSourceCost;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.OneShotEffect;
-import mage.cards.*;
+import mage.cards.Card;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.Outcome;
-import mage.constants.Zone;
 import mage.game.Game;
 import mage.players.Player;
 import mage.target.TargetPlayer;
 
+import java.util.List;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
 /**
- *
  * @author LevelX2
  */
 public final class Grindstone extends CardImpl {
@@ -25,11 +28,10 @@ public final class Grindstone extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}");
 
         // {3}, {T}: Target player puts the top two cards of their library into their graveyard. If both cards share a color, repeat this process.
-        Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new GrindstoneEffect(), new ManaCostsImpl("{3}"));
+        Ability ability = new SimpleActivatedAbility(new GrindstoneEffect(), new ManaCostsImpl("{3}"));
         ability.addCost(new TapSourceCost());
         ability.addTarget(new TargetPlayer());
         this.addAbility(ability);
-
     }
 
     public Grindstone(final Grindstone card) {
@@ -44,12 +46,12 @@ public final class Grindstone extends CardImpl {
 
 class GrindstoneEffect extends OneShotEffect {
 
-    public GrindstoneEffect() {
+    GrindstoneEffect() {
         super(Outcome.Benefit);
-        this.staticText = "Target player puts the top two cards of their library into their graveyard. If both cards share a color, repeat this process";
+        this.staticText = "target player mills two cards. If two cards that share a color were milled this way, repeat this process.";
     }
 
-    public GrindstoneEffect(final GrindstoneEffect effect) {
+    private GrindstoneEffect(final GrindstoneEffect effect) {
         super(effect);
     }
 
@@ -77,18 +79,33 @@ class GrindstoneEffect extends OneShotEffect {
                     return true;
                 }
                 colorShared = false;
-                Card card1 = null;
-                Cards toGraveyard = new CardsImpl();
-                for (Card card : targetPlayer.getLibrary().getCards(game)) {
-                    toGraveyard.add(card);
-                    if (card1 == null) {
-                        card1 = card;
-                    } else {
-                        colorShared = card1.getColor(game).shares(card.getColor(game));
+                List<Card> cards = targetPlayer
+                        .millCards(2, source, game)
+                        .getCards(game)
+                        .stream()
+                        .collect(Collectors.toList());
+                if (cards.size() < 2) {
+                    break;
+                }
+                for (int i = 0; i < cards.size(); i++) {
+                    if (colorShared) {
                         break;
                     }
+                    ObjectColor color1 = cards.get(i).getColor(game);
+                    if (color1.isColorless()) {
+                        continue;
+                    }
+                    for (int j = 0; j < cards.size(); j++) {
+                        if (i >= j) {
+                            continue;
+                        }
+                        ObjectColor color2 = cards.get(j).getColor(game);
+                        if (color1.shares(color2)) {
+                            colorShared = true;
+                            break;
+                        }
+                    }
                 }
-                targetPlayer.moveCards(toGraveyard, Zone.GRAVEYARD, source, game);
             } while (colorShared);
             return true;
         }
diff --git a/Mage.Sets/src/mage/cards/g/GrislySpectacle.java b/Mage.Sets/src/mage/cards/g/GrislySpectacle.java
index 074d37f0f3..c2d21bb0d5 100644
--- a/Mage.Sets/src/mage/cards/g/GrislySpectacle.java
+++ b/Mage.Sets/src/mage/cards/g/GrislySpectacle.java
@@ -54,7 +54,7 @@ class GrislySpectacleEffect extends OneShotEffect {
 
     public GrislySpectacleEffect() {
         super(Outcome.DestroyPermanent);
-        this.staticText = "Its controller puts a number of cards equal to that creature's power from the top of their library into their graveyard";
+        this.staticText = "Its controller mills cards equal to that creature's power";
     }
 
     public GrislySpectacleEffect(final GrislySpectacleEffect effect) {
diff --git a/Mage.Sets/src/mage/cards/g/GrizzledAngler.java b/Mage.Sets/src/mage/cards/g/GrizzledAngler.java
index b1715bac1f..4726349725 100644
--- a/Mage.Sets/src/mage/cards/g/GrizzledAngler.java
+++ b/Mage.Sets/src/mage/cards/g/GrizzledAngler.java
@@ -1,7 +1,6 @@
 
 package mage.cards.g;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleActivatedAbility;
@@ -12,22 +11,23 @@ import mage.abilities.keyword.TransformAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.SubType;
 import mage.constants.Outcome;
+import mage.constants.SubType;
 import mage.constants.Zone;
 import mage.filter.common.FilterCreatureCard;
 import mage.filter.predicate.mageobject.ColorlessPredicate;
 import mage.game.Game;
 import mage.players.Player;
 
+import java.util.UUID;
+
 /**
- *
  * @author fireshoes
  */
 public final class GrizzledAngler extends CardImpl {
 
     public GrizzledAngler(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{U}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}");
         this.subtype.add(SubType.HUMAN);
         this.power = new MageInt(2);
         this.toughness = new MageInt(3);
@@ -60,7 +60,7 @@ class GrizzledAnglerEffect extends OneShotEffect {
 
     public GrizzledAnglerEffect() {
         super(Outcome.Benefit);
-        staticText = "Put the top two cards of your library into your graveyard. Then if there is a colorless creature card in your graveyard, transform {this}";
+        staticText = "Mill two cards. Then if there is a colorless creature card in your graveyard, transform {this}";
     }
 
     public GrizzledAnglerEffect(final GrizzledAnglerEffect effect) {
@@ -76,7 +76,7 @@ class GrizzledAnglerEffect extends OneShotEffect {
     public boolean apply(Game game, Ability source) {
         Player controller = game.getPlayer(source.getControllerId());
         if (controller != null) {
-            controller.moveCards(controller.getLibrary().getTopCards(game, 2), Zone.GRAVEYARD, source, game);
+            controller.millCards(2, source, game);
             if (controller.getGraveyard().count(filter, source.getSourceId(), source.getControllerId(), game) >= 1) {
                 return new TransformSourceEffect(true).apply(game, source);
             }
diff --git a/Mage.Sets/src/mage/cards/g/GyrudaDoomOfDepths.java b/Mage.Sets/src/mage/cards/g/GyrudaDoomOfDepths.java
index f96ad330fa..effc47bf9a 100644
--- a/Mage.Sets/src/mage/cards/g/GyrudaDoomOfDepths.java
+++ b/Mage.Sets/src/mage/cards/g/GyrudaDoomOfDepths.java
@@ -17,7 +17,6 @@ import mage.players.Player;
 import mage.target.TargetCard;
 import mage.target.common.TargetCardInGraveyard;
 
-import java.util.Collection;
 import java.util.Set;
 import java.util.UUID;
 
@@ -81,9 +80,8 @@ class GyrudaDoomOfDepthsEffect extends OneShotEffect {
 
     GyrudaDoomOfDepthsEffect() {
         super(Outcome.Benefit);
-        staticText = "each player puts the top four cards of the library into their graveyard. " +
-                "Put a creature card with an even converted mana cost from among those cards " +
-                "onto the battlefield under your control.";
+        staticText = "each player mills four cards. Put a creature card with an even converted mana cost " +
+                "from among the milled cards onto the battlefield under your control";
     }
 
     private GyrudaDoomOfDepthsEffect(final GyrudaDoomOfDepthsEffect effect) {
@@ -102,15 +100,13 @@ class GyrudaDoomOfDepthsEffect extends OneShotEffect {
             return false;
         }
         Cards cards = new CardsImpl();
-        game.getState()
-                .getPlayersInRange(source.getControllerId(), game)
-                .stream()
-                .map(game::getPlayer)
-                .map(Player::getLibrary)
-                .map(library -> library.getTopCards(game, 4))
-                .flatMap(Collection::stream)
-                .forEach(cards::add);
-        controller.moveCards(cards, Zone.GRAVEYARD, source, game);
+        for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) {
+            Player player = game.getPlayer(playerId);
+            if (player == null) {
+                continue;
+            }
+            cards.addAll(player.millCards(4, source, game));
+        }
         cards.removeIf(cardId -> game.getState().getZone(cardId) != Zone.GRAVEYARD
                 && game.getState().getZone(cardId) != Zone.EXILED);
         if (cards.isEmpty()) {
diff --git a/Mage.Sets/src/mage/cards/h/HeedTheMists.java b/Mage.Sets/src/mage/cards/h/HeedTheMists.java
index 105251cecb..bc92038248 100644
--- a/Mage.Sets/src/mage/cards/h/HeedTheMists.java
+++ b/Mage.Sets/src/mage/cards/h/HeedTheMists.java
@@ -1,21 +1,20 @@
 
 package mage.cards.h;
 
-import java.util.UUID;
+import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.constants.SubType;
-import mage.constants.Zone;
 import mage.game.Game;
 import mage.players.Player;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class HeedTheMists extends CardImpl {
@@ -41,7 +40,7 @@ public final class HeedTheMists extends CardImpl {
 
         public HeedTheMistsEffect() {
             super(Outcome.DrawCard);
-            staticText = "Put the top card of your library into your graveyard, then draw cards equal to that card's converted mana cost";
+            staticText = "Mill a card, then draw cards equal to that card's converted mana cost";
         }
 
         public HeedTheMistsEffect(HeedTheMistsEffect effect) {
@@ -52,15 +51,14 @@ public final class HeedTheMists extends CardImpl {
         public boolean apply(Game game, Ability source) {
             boolean result = false;
             Player controller = game.getPlayer(source.getControllerId());
-            if (controller != null) {
-                Card card = controller.getLibrary().getFromTop(game);
-                if (card != null) {
-                    int cmc = card.getConvertedManaCost();
-                    controller.moveCards(card, Zone.GRAVEYARD, source, game);
-                    controller.drawCards(cmc, source.getSourceId(), game);
-                }
-            }
-            return result;
+            int totalCMC = controller
+                    .millCards(1, source, game)
+                    .getCards(game)
+                    .stream()
+                    .mapToInt(MageObject::getConvertedManaCost)
+                    .sum();
+            controller.millCards(totalCMC, source, game);
+            return true;
         }
 
         @Override
diff --git a/Mage.Sets/src/mage/cards/h/HelmOfObedience.java b/Mage.Sets/src/mage/cards/h/HelmOfObedience.java
index bad8be2592..cfa04ab003 100644
--- a/Mage.Sets/src/mage/cards/h/HelmOfObedience.java
+++ b/Mage.Sets/src/mage/cards/h/HelmOfObedience.java
@@ -1,7 +1,6 @@
-
 package mage.cards.h;
 
-import java.util.UUID;
+import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.common.TapSourceCost;
@@ -11,6 +10,7 @@ import mage.abilities.effects.OneShotEffect;
 import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
+import mage.cards.Cards;
 import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.constants.Zone;
@@ -19,8 +19,12 @@ import mage.game.permanent.Permanent;
 import mage.players.Player;
 import mage.target.common.TargetOpponent;
 
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
 /**
- *
  * @author Plopman
  */
 public final class HelmOfObedience extends CardImpl {
@@ -31,13 +35,13 @@ public final class HelmOfObedience extends CardImpl {
         // {X}, {T}: Target opponent puts cards from the top of their library into their graveyard until a creature card or X cards are put into that graveyard this way, whichever comes first. If a creature card is put into that graveyard this way, sacrifice Helm of Obedience and put that card onto the battlefield under your control. X can't be 0.
         VariableManaCost xCosts = new VariableManaCost();
         xCosts.setMinX(1);
-        SimpleActivatedAbility abilitiy = new SimpleActivatedAbility(Zone.BATTLEFIELD, new HelmOfObedienceEffect(), xCosts);
+        SimpleActivatedAbility abilitiy = new SimpleActivatedAbility(new HelmOfObedienceEffect(), xCosts);
         abilitiy.addCost(new TapSourceCost());
         abilitiy.addTarget(new TargetOpponent());
         this.addAbility(abilitiy);
     }
 
-    public HelmOfObedience(final HelmOfObedience card) {
+    private HelmOfObedience(final HelmOfObedience card) {
         super(card);
     }
 
@@ -51,12 +55,15 @@ class HelmOfObedienceEffect extends OneShotEffect {
 
     private static final ManacostVariableValue amount = ManacostVariableValue.instance;
 
-    public HelmOfObedienceEffect() {
+    HelmOfObedienceEffect() {
         super(Outcome.Detriment);
-        staticText = "Target opponent puts cards from the top of their library into their graveyard until a creature card or X cards are put into that graveyard this way, whichever comes first. If a creature card is put into that graveyard this way, sacrifice {this} and put that card onto the battlefield under your control. X can't be 0";
+        staticText = "Target opponent mills a card, then repeats this process until a creature card " +
+                "or X cards have been put into their graveyard this way, whichever comes first. " +
+                "If one or more creature cards were put into that graveyard this way, " +
+                "sacrifice {this} and put one of them onto the battlefield under your control. X can't be 0";
     }
 
-    public HelmOfObedienceEffect(final HelmOfObedienceEffect effect) {
+    private HelmOfObedienceEffect(final HelmOfObedienceEffect effect) {
         super(effect);
     }
 
@@ -70,32 +77,32 @@ class HelmOfObedienceEffect extends OneShotEffect {
         Player controller = game.getPlayer(source.getControllerId());
         Player targetOpponent = game.getPlayer(targetPointer.getFirst(game, source));
         int max = amount.calculate(game, source, this);
-        if (targetOpponent != null && controller != null && max > 0) {
-            int numberOfCard = 0;
-            for (Card card : targetOpponent.getLibrary().getCards(game)) {
-                if (card != null) {
-                    if (targetOpponent.moveCards(card, Zone.GRAVEYARD, source, game)) {
-                        if (card.isCreature()) {
-                            // If a creature card is put into that graveyard this way, sacrifice Helm of Obedience
-                            // and put that card onto the battlefield under your control.
-                            Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game);
-                            if (sourcePermanent != null) {
-                                sourcePermanent.sacrifice(source.getSourceId(), game);
-                            }
-                            controller.moveCards(card, Zone.BATTLEFIELD, source, game);
-                            break;
-                        } else {
-                            numberOfCard++;
-                            if (numberOfCard >= max) {
-                                break;
-                            }
-                        }
-                    }
-                }
-            }
-            return true;
+        if (targetOpponent == null || controller == null || max <= 0) {
+            return false;
         }
-        return false;
+        int numberOfCard = 0;
+        while (targetOpponent.getLibrary().hasCards()) {
+            Cards cards = targetOpponent.millCards(1, source, game);
+            cards.removeIf(uuid -> game.getState().getZone(uuid) != Zone.GRAVEYARD);
+            numberOfCard += cards.size();
+            Set<Card> creatures = cards
+                    .getCards(game)
+                    .stream()
+                    .filter(Objects::nonNull)
+                    .filter(MageObject::isCreature)
+                    .collect(Collectors.toSet());
+            if (!creatures.isEmpty()) {
+                controller.moveCards(creatures, Zone.BATTLEFIELD, source, game);
+            }
+            if (!creatures.isEmpty() || numberOfCard >= max) {
+                Permanent permanent = game.getPermanent(source.getSourceId());
+                if (permanent != null) {
+                    permanent.sacrifice(source.getSourceId(), game);
+                }
+                break;
+            }
+        }
+        return true;
     }
 
 }
diff --git a/Mage.Sets/src/mage/cards/h/HereticsPunishment.java b/Mage.Sets/src/mage/cards/h/HereticsPunishment.java
index 71c4bbe179..87439fa4df 100644
--- a/Mage.Sets/src/mage/cards/h/HereticsPunishment.java
+++ b/Mage.Sets/src/mage/cards/h/HereticsPunishment.java
@@ -1,21 +1,20 @@
 package mage.cards.h;
 
+import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.OneShotEffect;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.Outcome;
-import mage.constants.Zone;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
 import mage.players.Player;
 import mage.target.common.TargetAnyTarget;
 
-import java.util.Set;
+import java.util.Objects;
 import java.util.UUID;
 
 /**
@@ -27,7 +26,7 @@ public final class HereticsPunishment extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{R}");
 
         // {3}{R}: Choose any target, then put the top three cards of your library into your graveyard. Heretic's Punishment deals damage to that creature or player equal to the highest converted mana cost among those cards.
-        Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new HereticsPunishmentEffect(), new ManaCostsImpl("{3}{R}"));
+        Ability ability = new SimpleActivatedAbility(new HereticsPunishmentEffect(), new ManaCostsImpl("{3}{R}"));
         ability.addTarget(new TargetAnyTarget());
         this.addAbility(ability);
     }
@@ -46,7 +45,7 @@ class HereticsPunishmentEffect extends OneShotEffect {
 
     public HereticsPunishmentEffect() {
         super(Outcome.Damage);
-        staticText = "Choose any target, then put the top three cards of your library into your graveyard. {this} deals damage to that permanent or player equal to the highest converted mana cost among those cards";
+        staticText = "Choose any target, then mill three cards. {this} deals damage to that permanent or player equal to the highest converted mana cost among the milled cards";
     }
 
     public HereticsPunishmentEffect(final HereticsPunishmentEffect effect) {
@@ -56,26 +55,26 @@ class HereticsPunishmentEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player controller = game.getPlayer(source.getControllerId());
-        if (controller != null) {
-            int maxCost = 0;
-            Set<Card> cardList = controller.getLibrary().getTopCards(game, 3);
-            for (Card card : cardList) {
-                int test = card.getConvertedManaCost();
-                if (test > maxCost) {
-                    maxCost = test;
-                }
-            }
-            controller.moveCards(cardList, Zone.GRAVEYARD, source, game);
-            Permanent permanent = game.getPermanent(targetPointer.getFirst(game, source));
-            if (permanent != null) {
-                permanent.damage(maxCost, source.getSourceId(), game, false, true);
-                return true;
-            }
-            Player targetPlayer = game.getPlayer(targetPointer.getFirst(game, source));
-            if (targetPlayer != null) {
-                targetPlayer.damage(maxCost, source.getSourceId(), game);
-                return true;
-            }
+        if (controller == null) {
+            return false;
+        }
+        int maxCost = controller
+                .millCards(3, source, game)
+                .getCards(game)
+                .stream()
+                .filter(Objects::nonNull)
+                .mapToInt(MageObject::getConvertedManaCost)
+                .max()
+                .orElse(0);
+        Permanent permanent = game.getPermanent(targetPointer.getFirst(game, source));
+        if (permanent != null) {
+            permanent.damage(maxCost, source.getSourceId(), game, false, true);
+            return true;
+        }
+        Player targetPlayer = game.getPlayer(targetPointer.getFirst(game, source));
+        if (targetPlayer != null) {
+            targetPlayer.damage(maxCost, source.getSourceId(), game);
+            return true;
         }
         return false;
     }
diff --git a/Mage.Sets/src/mage/cards/i/IcebergCancrix.java b/Mage.Sets/src/mage/cards/i/IcebergCancrix.java
index c0a59ad7b7..0f2cb53c50 100644
--- a/Mage.Sets/src/mage/cards/i/IcebergCancrix.java
+++ b/Mage.Sets/src/mage/cards/i/IcebergCancrix.java
@@ -40,7 +40,7 @@ public final class IcebergCancrix extends CardImpl {
         Ability ability = new EntersBattlefieldControlledTriggeredAbility(
                 Zone.BATTLEFIELD, new PutLibraryIntoGraveTargetEffect(2), filter,
                 true, "Whenever another snow permanent enters the battlefield under your control, " +
-                "you may have target player put the top two cards of their library into their graveyard."
+                "you may have target player mill two cards."
         );
         ability.addTarget(new TargetPlayer());
         this.addAbility(ability);
diff --git a/Mage.Sets/src/mage/cards/i/IncreasingConfusion.java b/Mage.Sets/src/mage/cards/i/IncreasingConfusion.java
index 909f4ec7c0..3e9ca55f5f 100644
--- a/Mage.Sets/src/mage/cards/i/IncreasingConfusion.java
+++ b/Mage.Sets/src/mage/cards/i/IncreasingConfusion.java
@@ -46,7 +46,7 @@ class IncreasingConfusionEffect extends OneShotEffect {
 
     public IncreasingConfusionEffect() {
         super(Outcome.Detriment);
-        staticText = "Target player puts the top X cards of their library into their graveyard. If this spell was cast from a graveyard, that player puts twice that many cards into their graveyard instead";
+        staticText = "Target player mills X cards. If this spell was cast from a graveyard, that player mills twice that many cards";
     }
 
     public IncreasingConfusionEffect(final IncreasingConfusionEffect effect) {
@@ -63,7 +63,7 @@ class IncreasingConfusionEffect extends OneShotEffect {
                 if (spell.getFromZone() == Zone.GRAVEYARD) {
                     amount *= 2;
                 }
-                player.moveCards(player.getLibrary().getTopCards(game, amount), Zone.GRAVEYARD, source, game);
+                player.millCards(amount, source, game);
                 return true;
             }
         }
diff --git a/Mage.Sets/src/mage/cards/i/InduceParanoia.java b/Mage.Sets/src/mage/cards/i/InduceParanoia.java
index d8862fa168..eb699cc84e 100644
--- a/Mage.Sets/src/mage/cards/i/InduceParanoia.java
+++ b/Mage.Sets/src/mage/cards/i/InduceParanoia.java
@@ -51,7 +51,7 @@ class InduceParanoiaEffect extends OneShotEffect {
 
     InduceParanoiaEffect() {
         super(Outcome.Detriment);
-        this.staticText = "Counter target spell. If {B} was spent to cast this spell, that spell's controller puts the top X cards of their library into their graveyard, where X is the spell's converted mana cost.";
+        this.staticText = "Counter target spell. If {B} was spent to cast this spell, that spell's controller mills X cards, where X is the spell's converted mana cost.";
     }
 
     InduceParanoiaEffect(final InduceParanoiaEffect effect) {
@@ -71,7 +71,7 @@ class InduceParanoiaEffect extends OneShotEffect {
             int spellCMC = spell.getConvertedManaCost();
             Player player = game.getPlayer(spell.getControllerId());
             if (player != null) {
-                player.moveCards(player.getLibrary().getTopCards(game, spellCMC), Zone.GRAVEYARD, source, game);
+                player.millCards(spellCMC, source, game);
                 return true;
             }
         }
diff --git a/Mage.Sets/src/mage/cards/i/InfernalGenesis.java b/Mage.Sets/src/mage/cards/i/InfernalGenesis.java
index daa9e51bfd..2fac791347 100644
--- a/Mage.Sets/src/mage/cards/i/InfernalGenesis.java
+++ b/Mage.Sets/src/mage/cards/i/InfernalGenesis.java
@@ -1,20 +1,23 @@
-
 package mage.cards.i;
 
-import java.util.UUID;
+import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.*;
+import mage.constants.CardType;
+import mage.constants.Outcome;
+import mage.constants.TargetController;
 import mage.game.Game;
 import mage.game.permanent.token.MinionToken2;
+import mage.game.permanent.token.Token;
 import mage.players.Player;
 
+import java.util.Objects;
+import java.util.UUID;
+
 /**
- *
  * @author jeffwadsworth
  */
 public final class InfernalGenesis extends CardImpl {
@@ -39,28 +42,30 @@ public final class InfernalGenesis extends CardImpl {
 
 class InfernalGenesisEffect extends OneShotEffect {
 
+    private static final Token token = new MinionToken2();
+
     InfernalGenesisEffect() {
         super(Outcome.PutCreatureInPlay);
-        staticText = "that player puts the top card of their library into their graveyard. " +
-                "Then they create X 1/1 black Minion creature tokens, where X is that card's converted mana cost";
+        staticText = "that player mills a card. Then they create X 1/1 black Minion creature tokens, " +
+                "where X is the milled card's converted mana cost.";
     }
 
-    InfernalGenesisEffect(final InfernalGenesisEffect effect) {
+    private InfernalGenesisEffect(final InfernalGenesisEffect effect) {
         super(effect);
     }
 
     @Override
     public boolean apply(Game game, Ability source) {
         Player player = game.getPlayer(getTargetPointer().getFirst(game, source));
-        if (player != null) {
-            Card card = player.getLibrary().getFromTop(game);
-            if (card != null) {
-                if (player.moveCards(card, Zone.GRAVEYARD, source, game)) {
-                    int cmc = card.getConvertedManaCost();
-                    MinionToken2 token = new MinionToken2();
-                    token.putOntoBattlefield(cmc, game, source.getSourceId(), player.getId());
-                }
-            }
+        int totalCMC = player
+                .millCards(1, source, game)
+                .getCards(game)
+                .stream()
+                .filter(Objects::nonNull)
+                .mapToInt(MageObject::getConvertedManaCost)
+                .sum();
+        if (totalCMC > 0) {
+            token.putOntoBattlefield(totalCMC, game, source.getSourceId(), player.getId());
         }
         return true;
     }
diff --git a/Mage.Sets/src/mage/cards/j/JacesMindseeker.java b/Mage.Sets/src/mage/cards/j/JacesMindseeker.java
index dd0f2354fe..cc4e59d68f 100644
--- a/Mage.Sets/src/mage/cards/j/JacesMindseeker.java
+++ b/Mage.Sets/src/mage/cards/j/JacesMindseeker.java
@@ -1,19 +1,12 @@
 package mage.cards.j;
 
-import java.util.HashSet;
-import java.util.Set;
-import java.util.UUID;
 import mage.MageInt;
 import mage.MageObjectReference;
 import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.keyword.FlyingAbility;
-import mage.cards.Card;
-import mage.cards.CardImpl;
-import mage.cards.CardSetInfo;
-import mage.cards.Cards;
-import mage.cards.CardsImpl;
+import mage.cards.*;
 import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.constants.SubType;
@@ -25,8 +18,10 @@ import mage.players.Player;
 import mage.target.TargetCard;
 import mage.target.common.TargetOpponent;
 
+import java.util.Set;
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class JacesMindseeker extends CardImpl {
@@ -65,9 +60,8 @@ class JaceMindseekerEffect extends OneShotEffect {
 
     public JaceMindseekerEffect() {
         super(Outcome.PlayForFree);
-        this.staticText = "target opponent puts the top five cards of their "
-                + "library into their graveyard. You may cast an instant or "
-                + "sorcery card from among them without paying its mana cost";
+        this.staticText = "target opponent mills five cards. You may cast an instant or sorcery spell " +
+                "from among them without paying its mana cost.";
     }
 
     public JaceMindseekerEffect(final JaceMindseekerEffect effect) {
@@ -84,10 +78,7 @@ class JaceMindseekerEffect extends OneShotEffect {
         Cards cardsToCast = new CardsImpl();
         Player targetOpponent = game.getPlayer(targetPointer.getFirst(game, source));
         if (targetOpponent != null) {
-            Set<Card> allCards = targetOpponent.getLibrary().getTopCards(game, 5);
-            Set<Card> toMove = new HashSet<>();
-            toMove.addAll(allCards);
-            targetOpponent.moveCards(toMove, Zone.GRAVEYARD, source, game);
+            Set<Card> allCards = targetOpponent.millCards(5, source, game).getCards(game);
             for (Card card : allCards) {
                 if (filter.match(card, game)) {
                     Zone zone = game.getState().getZone(card.getId());
diff --git a/Mage.Sets/src/mage/cards/k/KeeningStone.java b/Mage.Sets/src/mage/cards/k/KeeningStone.java
index 9ed4d7fbec..11a4de0547 100644
--- a/Mage.Sets/src/mage/cards/k/KeeningStone.java
+++ b/Mage.Sets/src/mage/cards/k/KeeningStone.java
@@ -1,7 +1,6 @@
 
 package mage.cards.k;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.common.TapSourceCost;
@@ -11,22 +10,22 @@ import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.Outcome;
-import mage.constants.Zone;
 import mage.game.Game;
 import mage.players.Player;
 import mage.target.TargetPlayer;
 
+import java.util.UUID;
+
 /**
- *
  * @author North
  */
 public final class KeeningStone extends CardImpl {
 
     public KeeningStone(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{6}");
+        super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{6}");
 
         // {5}, {tap}: Target player puts the top X cards of their library into their graveyard, where X is the number of cards in that player's graveyard.
-        Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new KeeningStoneEffect(), new GenericManaCost(5));
+        Ability ability = new SimpleActivatedAbility(new KeeningStoneEffect(), new GenericManaCost(5));
         ability.addCost(new TapSourceCost());
         ability.addTarget(new TargetPlayer());
         this.addAbility(ability);
@@ -46,7 +45,7 @@ class KeeningStoneEffect extends OneShotEffect {
 
     public KeeningStoneEffect() {
         super(Outcome.Neutral);
-        this.staticText = "Target player puts the top X cards of their library into their graveyard, where X is the number of cards in that player's graveyard";
+        this.staticText = "Target player mills X cards, where X is the number of cards in that player's graveyard";
     }
 
     public KeeningStoneEffect(final KeeningStoneEffect effect) {
@@ -62,7 +61,7 @@ class KeeningStoneEffect extends OneShotEffect {
     public boolean apply(Game game, Ability source) {
         Player player = game.getPlayer(source.getFirstTarget());
         if (player != null) {
-            player.moveCards(player.getLibrary().getTopCards(game, player.getGraveyard().size()), Zone.GRAVEYARD, source, game);
+            player.millCards(player.getGraveyard().size(), source, game);
             return true;
         }
         return false;
diff --git a/Mage.Sets/src/mage/cards/l/LammastideWeave.java b/Mage.Sets/src/mage/cards/l/LammastideWeave.java
index b8f979f472..fc9365efdb 100644
--- a/Mage.Sets/src/mage/cards/l/LammastideWeave.java
+++ b/Mage.Sets/src/mage/cards/l/LammastideWeave.java
@@ -9,7 +9,6 @@ import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.Outcome;
-import mage.constants.Zone;
 import mage.game.Game;
 import mage.players.Player;
 import mage.target.TargetPlayer;
@@ -35,7 +34,7 @@ public final class LammastideWeave extends CardImpl {
 
     }
 
-    public LammastideWeave(final LammastideWeave card) {
+    private LammastideWeave(final LammastideWeave card) {
         super(card);
     }
 
@@ -47,13 +46,13 @@ public final class LammastideWeave extends CardImpl {
 
 class LammastideWeaveEffect extends OneShotEffect {
 
-    public LammastideWeaveEffect() {
+    LammastideWeaveEffect() {
         super(Outcome.DrawCard);
-        this.staticText = ", then target player puts the top card of their library into their graveyard. "
-                + "If that card has the chosen name, you gain life equal to its converted mana cost.";
+        this.staticText = ", then target player mills a card. If a card with the chosen name was milled this way, " +
+                "you gain life equal to its converted mana cost.";
     }
 
-    public LammastideWeaveEffect(final LammastideWeaveEffect effect) {
+    private LammastideWeaveEffect(final LammastideWeaveEffect effect) {
         super(effect);
     }
 
@@ -67,17 +66,14 @@ class LammastideWeaveEffect extends OneShotEffect {
         Player controller = game.getPlayer(source.getControllerId());
         Player targetPlayer = game.getPlayer(source.getFirstTarget());
         String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY);
-        if (controller != null && targetPlayer != null && cardName != null && !cardName.isEmpty()) {
-            Card card = targetPlayer.getLibrary().getFromTop(game);
-            if (card != null) {
-                controller.moveCards(card, Zone.GRAVEYARD, source, game);
-                if (CardUtil.haveSameNames(card, cardName, game)) {
-                    controller.gainLife(card.getConvertedManaCost(), game, source);
-                }
-            }
-            return true;
+        if (controller == null || targetPlayer == null || cardName == null || cardName.isEmpty()) {
+            return false;
         }
-        return false;
+        for (Card card : targetPlayer.millCards(1, source, game).getCards(game)) {
+            if (CardUtil.haveSameNames(card, cardName, game)) {
+                controller.gainLife(card.getConvertedManaCost(), game, source);
+            }
+        }
+        return true;
     }
-
 }
diff --git a/Mage.Sets/src/mage/cards/l/LilianaUntouchedByDeath.java b/Mage.Sets/src/mage/cards/l/LilianaUntouchedByDeath.java
index f131e898ac..dde813dd58 100644
--- a/Mage.Sets/src/mage/cards/l/LilianaUntouchedByDeath.java
+++ b/Mage.Sets/src/mage/cards/l/LilianaUntouchedByDeath.java
@@ -1,7 +1,5 @@
 package mage.cards.l;
 
-import java.util.Set;
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.LoyaltyAbility;
 import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
@@ -12,22 +10,17 @@ import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.LoseLifeOpponentsEffect;
 import mage.abilities.effects.common.continuous.BoostTargetEffect;
 import mage.cards.Card;
-import mage.constants.SubType;
-import mage.constants.SuperType;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.AsThoughEffectType;
-import mage.constants.CardType;
-import mage.constants.Duration;
-import mage.constants.Outcome;
-import mage.constants.Zone;
+import mage.constants.*;
 import mage.filter.common.FilterControlledCreaturePermanent;
 import mage.game.Game;
 import mage.players.Player;
 import mage.target.common.TargetCreaturePermanent;
 
+import java.util.UUID;
+
 /**
- *
  * @author TheElk801
  */
 public final class LilianaUntouchedByDeath extends CardImpl {
@@ -73,8 +66,7 @@ class LilianaUntouchedByDeathEffect extends OneShotEffect {
 
     public LilianaUntouchedByDeathEffect() {
         super(Outcome.Benefit);
-        this.staticText = "put the top three cards of your library into your graveyard. "
-                + "If at least one of them is a Zombie card, each opponent loses 2 life and you gain 2 life";
+        this.staticText = "mill three cards. If at least one of them is a Zombie card, each opponent loses 2 life and you gain 2 life";
     }
 
     public LilianaUntouchedByDeathEffect(final LilianaUntouchedByDeathEffect effect) {
@@ -92,15 +84,11 @@ class LilianaUntouchedByDeathEffect extends OneShotEffect {
         if (player == null) {
             return false;
         }
-        boolean doEffect = false;
-        Set<Card> top3 = player.moveCardsToGraveyardWithInfo(player.getLibrary().getTopCards(game, 3), source, game, Zone.LIBRARY);
-        for (Card card : top3) {
-            if (card != null && card.hasSubtype(SubType.ZOMBIE, game)) {
-                doEffect = true;
-                break;
-            }
-        }
-        if (doEffect) {
+        if (player
+                .millCards(3, source, game)
+                .getCards(game)
+                .stream()
+                .anyMatch(card -> card.hasSubtype(SubType.ZOMBIE, game))) {
             new LoseLifeOpponentsEffect(2).apply(game, source);
             player.gainLife(2, game, source);
         }
diff --git a/Mage.Sets/src/mage/cards/l/LilianasIndignation.java b/Mage.Sets/src/mage/cards/l/LilianasIndignation.java
index 67d0d59c1c..3456d56f29 100644
--- a/Mage.Sets/src/mage/cards/l/LilianasIndignation.java
+++ b/Mage.Sets/src/mage/cards/l/LilianasIndignation.java
@@ -1,24 +1,21 @@
 package mage.cards.l;
 
-import java.util.Set;
-import java.util.UUID;
+import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.cards.Cards;
-import mage.cards.CardsImpl;
 import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.constants.Zone;
-import mage.filter.StaticFilters;
 import mage.game.Game;
 import mage.players.Player;
 import mage.target.TargetPlayer;
 
+import java.util.Objects;
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class LilianasIndignation extends CardImpl {
@@ -45,7 +42,7 @@ class LilianasIndignationEffect extends OneShotEffect {
 
     public LilianasIndignationEffect() {
         super(Outcome.LoseLife);
-        this.staticText = "Put the top X cards of your library into your graveyard. Target player loses 2 life for each creature card put into your graveyard this way";
+        this.staticText = "Mill X cards. Target player loses 2 life for each creature card put into your graveyard this way";
     }
 
     public LilianasIndignationEffect(final LilianasIndignationEffect effect) {
@@ -60,27 +57,28 @@ class LilianasIndignationEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player controller = game.getPlayer(source.getControllerId());
-        if (controller != null) {
-            int x = source.getManaCostsToPay().getX();
-            if (x > 0) {
-                Cards cardsToGraveyard = new CardsImpl();
-                cardsToGraveyard.addAll(controller.getLibrary().getTopCards(game, x));
-                if (!cardsToGraveyard.isEmpty()) {
-                    Set<Card> movedCards = controller.moveCardsToGraveyardWithInfo(cardsToGraveyard.getCards(game), source, game, Zone.LIBRARY);
-                    Cards cardsMoved = new CardsImpl();
-                    cardsMoved.addAll(movedCards);
-                    int creatures = cardsMoved.count(StaticFilters.FILTER_CARD_CREATURE, game);
-                    if (creatures > 0) {
-                        Player targetPlayer = game.getPlayer(getTargetPointer().getFirst(game, source));
-                        if (targetPlayer != null) {
-                            targetPlayer.loseLife(creatures * 2, game, false);
-                        }
-
-                    }
-                }
-            }
+        if (controller == null) {
+            return false;
+        }
+        int xValue = source.getManaCostsToPay().getX();
+        if (xValue < 1) {
             return true;
         }
-        return false;
+        int creatures = controller
+                .millCards(xValue, source, game)
+                .getCards(game)
+                .stream()
+                .filter(Objects::nonNull)
+                .filter(card -> game.getState().getZone(card.getId()) == Zone.GRAVEYARD)
+                .filter(MageObject::isCreature)
+                .mapToInt(x -> 2)
+                .sum();
+        if (creatures > 0) {
+            Player targetPlayer = game.getPlayer(source.getFirstTarget());
+            if (targetPlayer != null) {
+                targetPlayer.loseLife(creatures, game, false);
+            }
+        }
+        return true;
     }
 }
diff --git a/Mage.Sets/src/mage/cards/l/LoafingGiant.java b/Mage.Sets/src/mage/cards/l/LoafingGiant.java
index 0bffafbc68..2cd86e58f7 100644
--- a/Mage.Sets/src/mage/cards/l/LoafingGiant.java
+++ b/Mage.Sets/src/mage/cards/l/LoafingGiant.java
@@ -1,29 +1,31 @@
 package mage.cards.l;
 
-import java.util.UUID;
 import mage.MageInt;
+import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.common.AttacksOrBlocksTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.PreventCombatDamageBySourceEffect;
-import mage.abilities.effects.common.combat.CantBeBlockedByAllSourceEffect;
-import mage.cards.Card;
-import mage.constants.*;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.filter.StaticFilters;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.Outcome;
+import mage.constants.SubType;
 import mage.game.Game;
 import mage.players.Player;
 
+import java.util.Objects;
+import java.util.UUID;
+
 /**
- *
  * @author noahg
  */
 public final class LoafingGiant extends CardImpl {
 
     public LoafingGiant(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}");
-        
+
         this.subtype.add(SubType.GIANT);
         this.power = new MageInt(4);
         this.toughness = new MageInt(6);
@@ -46,7 +48,7 @@ class LoafingGiantEffect extends OneShotEffect {
 
     public LoafingGiantEffect() {
         super(Outcome.UnboostCreature);
-        this.staticText = "Put the top card of your library into your graveyard. If that card is a land card, prevent all combat damage {this} would deal this turn.";
+        this.staticText = "Mill a card. If a land card was milled this way, prevent all combat damage {this} would deal this turn.";
     }
 
     public LoafingGiantEffect(final LoafingGiantEffect effect) {
@@ -61,14 +63,14 @@ class LoafingGiantEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player player = game.getPlayer(source.getControllerId());
-        if (player != null) {
-            Card card = player.getLibrary().getFromTop(game);
-            if (card != null) {
-                player.moveCards(card, Zone.GRAVEYARD, source, game);
-                if (card.isLand()) {
-                    game.addEffect(new PreventCombatDamageBySourceEffect(Duration.EndOfTurn), source);
-                }
-            }
+        if (player != null
+                && player
+                .millCards(1, source, game)
+                .getCards(game)
+                .stream()
+                .filter(Objects::nonNull)
+                .anyMatch(MageObject::isLand)) {
+            game.addEffect(new PreventCombatDamageBySourceEffect(Duration.EndOfTurn), source);
             return true;
         }
         return false;
diff --git a/Mage.Sets/src/mage/cards/m/ManicScribe.java b/Mage.Sets/src/mage/cards/m/ManicScribe.java
index 26db45a69d..775f264a51 100644
--- a/Mage.Sets/src/mage/cards/m/ManicScribe.java
+++ b/Mage.Sets/src/mage/cards/m/ManicScribe.java
@@ -1,7 +1,5 @@
 package mage.cards.m;
 
-import java.util.UUID;
-
 import mage.MageInt;
 import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
@@ -17,6 +15,8 @@ import mage.constants.SubType;
 import mage.constants.TargetController;
 import mage.constants.Zone;
 
+import java.util.UUID;
+
 /**
  * @author fireshoes
  */
@@ -30,19 +30,22 @@ public final class ManicScribe extends CardImpl {
         this.toughness = new MageInt(3);
 
         // When Manic Scribe enters the battlefield, each opponent puts the top three cards of their library into their graveyard.
-        this.addAbility(new EntersBattlefieldTriggeredAbility(new PutTopCardOfLibraryIntoGraveEachPlayerEffect(3, TargetController.OPPONENT), false));
+        this.addAbility(new EntersBattlefieldTriggeredAbility(
+                new PutTopCardOfLibraryIntoGraveEachPlayerEffect(3, TargetController.OPPONENT), false
+        ));
 
         // <i>Delirium</i> &mdash; At the beginning of each opponent's upkeep, if there are four or more card types among cards in your graveyard,
         // that player puts the top three cards of their library into their graveyard.
         this.addAbility(new ConditionalInterveningIfTriggeredAbility(
-                new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new PutTopCardOfLibraryIntoGraveTargetEffect(3), TargetController.OPPONENT, false, true),
-                DeliriumCondition.instance,
-                "<i>Delirium</i> &mdash; At the beginning of each opponent's upkeep, if there are four or more card types among cards in your graveyard, "
-                        + "that player puts the top three cards of their library into their graveyard.")
-                .addHint(DeliriumHint.instance));
+                new BeginningOfUpkeepTriggeredAbility(
+                        Zone.BATTLEFIELD, new PutTopCardOfLibraryIntoGraveTargetEffect(3),
+                        TargetController.OPPONENT, false, true
+                ), DeliriumCondition.instance, "<i>Delirium</i> &mdash; At the beginning of each opponent's upkeep, " +
+                "if there are four or more card types among cards in your graveyard, that player mills three cards."
+        ).addHint(DeliriumHint.instance));
     }
 
-    public ManicScribe(final ManicScribe card) {
+    private ManicScribe(final ManicScribe card) {
         super(card);
     }
 
diff --git a/Mage.Sets/src/mage/cards/m/MemoryErosion.java b/Mage.Sets/src/mage/cards/m/MemoryErosion.java
index dc6bfd720c..5aa6273b58 100644
--- a/Mage.Sets/src/mage/cards/m/MemoryErosion.java
+++ b/Mage.Sets/src/mage/cards/m/MemoryErosion.java
@@ -68,7 +68,7 @@ class SpellCastTriggeredAbility extends TriggeredAbilityImpl {
 
     @Override
     public String getRule() {
-        return "Whenever an opponent casts a spell, that player puts the top two cards of their library into their graveyard";
+        return "Whenever an opponent casts a spell, that player mills two cards";
     }
 
     @Override
diff --git a/Mage.Sets/src/mage/cards/m/MesmericOrb.java b/Mage.Sets/src/mage/cards/m/MesmericOrb.java
index 6b8608259b..9dae5bf385 100644
--- a/Mage.Sets/src/mage/cards/m/MesmericOrb.java
+++ b/Mage.Sets/src/mage/cards/m/MesmericOrb.java
@@ -1,9 +1,7 @@
 
 package mage.cards.m;
 
-import java.util.UUID;
 import mage.abilities.TriggeredAbilityImpl;
-import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.PutTopCardOfLibraryIntoGraveTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -15,22 +13,21 @@ import mage.game.events.GameEvent.EventType;
 import mage.game.permanent.Permanent;
 import mage.target.targetpointer.FixedTarget;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class MesmericOrb extends CardImpl {
 
     public MesmericOrb(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{2}");
+        super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}");
 
         // Whenever a permanent becomes untapped, that permanent's controller puts the top card of their library into their graveyard.
-        Effect effect = new PutTopCardOfLibraryIntoGraveTargetEffect(1);
-        effect.setText("that permanent's controller puts the top card of their library into their graveyard");
-        this.addAbility(new BecomesUntappedPermanentTriggeredAbility(effect, false));
+        this.addAbility(new MesmericOrbTriggeredAbility());
     }
 
-    public MesmericOrb(final MesmericOrb card) {
+    private MesmericOrb(final MesmericOrb card) {
         super(card);
     }
 
@@ -40,19 +37,19 @@ public final class MesmericOrb extends CardImpl {
     }
 }
 
-class BecomesUntappedPermanentTriggeredAbility extends TriggeredAbilityImpl{
+class MesmericOrbTriggeredAbility extends TriggeredAbilityImpl {
 
-    public BecomesUntappedPermanentTriggeredAbility(Effect effect, boolean optional) {
-        super(Zone.BATTLEFIELD, effect, optional);
+    MesmericOrbTriggeredAbility() {
+        super(Zone.BATTLEFIELD, new PutTopCardOfLibraryIntoGraveTargetEffect(1), false);
     }
 
-    public BecomesUntappedPermanentTriggeredAbility(final BecomesUntappedPermanentTriggeredAbility ability) {
+    private MesmericOrbTriggeredAbility(final MesmericOrbTriggeredAbility ability) {
         super(ability);
     }
 
     @Override
-    public BecomesUntappedPermanentTriggeredAbility copy() {
-        return new BecomesUntappedPermanentTriggeredAbility(this);
+    public MesmericOrbTriggeredAbility copy() {
+        return new MesmericOrbTriggeredAbility(this);
     }
 
     @Override
@@ -72,7 +69,7 @@ class BecomesUntappedPermanentTriggeredAbility extends TriggeredAbilityImpl{
 
     @Override
     public String getRule() {
-        return "Whenever a permanent becomes untapped, " + super.getRule();
+        return "Whenever a permanent becomes untapped, that permanent's controller mills a card.";
     }
 
 }
diff --git a/Mage.Sets/src/mage/cards/m/Mindcrank.java b/Mage.Sets/src/mage/cards/m/Mindcrank.java
index 435ee718a6..f2610c32dc 100644
--- a/Mage.Sets/src/mage/cards/m/Mindcrank.java
+++ b/Mage.Sets/src/mage/cards/m/Mindcrank.java
@@ -104,7 +104,7 @@ class MindcrankEffect extends OneShotEffect {
             if (amount == null) {
                 amount = 0;
             }
-            targetPlayer.moveCards(targetPlayer.getLibrary().getTopCards(game, amount), Zone.GRAVEYARD, source, game);
+            targetPlayer.millCards(amount, source, game);
         }
         return true;
     }
diff --git a/Mage.Sets/src/mage/cards/m/Mindshrieker.java b/Mage.Sets/src/mage/cards/m/Mindshrieker.java
index b8effb0479..ad0be56807 100644
--- a/Mage.Sets/src/mage/cards/m/Mindshrieker.java
+++ b/Mage.Sets/src/mage/cards/m/Mindshrieker.java
@@ -1,20 +1,24 @@
 package mage.cards.m;
 
 import mage.MageInt;
+import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleActivatedAbility;
-import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.costs.mana.GenericManaCost;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.continuous.BoostSourceEffect;
 import mage.abilities.keyword.FlyingAbility;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.*;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.Outcome;
+import mage.constants.SubType;
 import mage.game.Game;
 import mage.players.Player;
 import mage.target.TargetPlayer;
 
+import java.util.Objects;
 import java.util.UUID;
 
 /**
@@ -34,10 +38,9 @@ public final class Mindshrieker extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
 
         // {2}: Target player puts the top card of their library into their graveyard. Mindshrieker gets +X/+X until end of turn, where X is that card's converted mana cost.
-        Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new MindshriekerEffect(), new ManaCostsImpl("{2}"));
+        Ability ability = new SimpleActivatedAbility(new MindshriekerEffect(), new GenericManaCost(2));
         ability.addTarget(new TargetPlayer());
         this.addAbility(ability);
-
     }
 
     public Mindshrieker(final Mindshrieker card) {
@@ -52,30 +55,31 @@ public final class Mindshrieker extends CardImpl {
 
 class MindshriekerEffect extends OneShotEffect {
 
-    public MindshriekerEffect() {
+    MindshriekerEffect() {
         super(Outcome.Detriment);
-        staticText = "Target player puts the top card of their library into their graveyard. {this} gets +X/+X until end of turn, where X is that card's converted mana cost";
+        staticText = "Target player mills a card. {this} gets +X/+X until end of turn, " +
+                "where X is the milled card's converted mana cost";
     }
 
-    public MindshriekerEffect(final MindshriekerEffect effect) {
+    private MindshriekerEffect(final MindshriekerEffect effect) {
         super(effect);
     }
 
     @Override
     public boolean apply(Game game, Ability source) {
         Player targetPlayer = game.getPlayer(source.getFirstTarget());
-        if (targetPlayer != null) {
-            if (targetPlayer.getLibrary().hasCards()) {
-                Card card = targetPlayer.getLibrary().getFromTop(game);
-                if (card != null) {
-                    targetPlayer.moveCards(card, Zone.GRAVEYARD, source, game);
-                    int amount = card.getConvertedManaCost();
-                    if (amount > 0) {
-                        game.addEffect(new BoostSourceEffect(amount, amount, Duration.EndOfTurn), source);
-                    }
-                }
-            }
-            return true;
+        if (targetPlayer == null) {
+            return false;
+        }
+        int totalCMC = targetPlayer
+                .millCards(1, source, game)
+                .getCards(game)
+                .stream()
+                .filter(Objects::nonNull)
+                .mapToInt(MageObject::getConvertedManaCost)
+                .sum();
+        if (totalCMC > 0) {
+            game.addEffect(new BoostSourceEffect(totalCMC, totalCMC, Duration.EndOfTurn), source);
         }
         return false;
     }
diff --git a/Mage.Sets/src/mage/cards/n/NavigatorsRuin.java b/Mage.Sets/src/mage/cards/n/NavigatorsRuin.java
index 2b3ecb389c..d9fe043d27 100644
--- a/Mage.Sets/src/mage/cards/n/NavigatorsRuin.java
+++ b/Mage.Sets/src/mage/cards/n/NavigatorsRuin.java
@@ -1,7 +1,5 @@
-
 package mage.cards.n;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.common.BeginningOfEndStepTriggeredAbility;
 import mage.abilities.condition.common.RaidCondition;
@@ -14,8 +12,9 @@ import mage.constants.TargetController;
 import mage.target.common.TargetOpponent;
 import mage.watchers.common.PlayerAttackedWatcher;
 
+import java.util.UUID;
+
 /**
- *
  * @author TheElk801
  */
 public final class NavigatorsRuin extends CardImpl {
@@ -25,14 +24,16 @@ public final class NavigatorsRuin extends CardImpl {
 
         // Raid - At the beginning of your end step, if you attacked with a creature this turm, target opponent puts the top four cards of their library into their graveyard.
         Ability ability = new ConditionalInterveningIfTriggeredAbility(
-                new BeginningOfEndStepTriggeredAbility(new PutLibraryIntoGraveTargetEffect(4), TargetController.YOU, false),
-                RaidCondition.instance,
-                "<i>Raid</i> &mdash; At the beginning of your end step, if you attacked this turn, target opponent puts the top four cards of their library into their graveyard.");
+                new BeginningOfEndStepTriggeredAbility(
+                        new PutLibraryIntoGraveTargetEffect(4), TargetController.YOU, false
+                ), RaidCondition.instance, "<i>Raid</i> &mdash; At the beginning of your end step, " +
+                "if you attacked this turn, target opponent mills four cards."
+        );
         ability.addTarget(new TargetOpponent());
         this.addAbility(ability, new PlayerAttackedWatcher());
     }
 
-    public NavigatorsRuin(final NavigatorsRuin card) {
+    private NavigatorsRuin(final NavigatorsRuin card) {
         super(card);
     }
 
diff --git a/Mage.Sets/src/mage/cards/n/NemesisOfReason.java b/Mage.Sets/src/mage/cards/n/NemesisOfReason.java
index e3dd3529c9..bb8296c7b6 100644
--- a/Mage.Sets/src/mage/cards/n/NemesisOfReason.java
+++ b/Mage.Sets/src/mage/cards/n/NemesisOfReason.java
@@ -2,7 +2,6 @@
 
 package mage.cards.n;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.TriggeredAbilityImpl;
 import mage.abilities.effects.Effect;
@@ -16,27 +15,26 @@ import mage.game.Game;
 import mage.game.events.GameEvent;
 import mage.target.targetpointer.FixedTarget;
 
+import java.util.UUID;
+
 /**
- *
  * @author Loki
  */
 public final class NemesisOfReason extends CardImpl {
 
-    public NemesisOfReason (UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{U}{B}");
+    public NemesisOfReason(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{B}");
         this.subtype.add(SubType.LEVIATHAN);
         this.subtype.add(SubType.HORROR);
-        
+
         this.power = new MageInt(3);
         this.toughness = new MageInt(7);
-        
+
         // Whenever Nemesis of Reason attacks, defending player puts the top ten cards of their library into their graveyard.
-        Effect effect = new PutLibraryIntoGraveTargetEffect(10);
-        effect.setText("defending player puts the top ten cards of their library into their graveyard");
-        this.addAbility(new NemesisOfReasonTriggeredAbility(effect));
+        this.addAbility(new NemesisOfReasonTriggeredAbility(new PutLibraryIntoGraveTargetEffect(10)));
     }
 
-    public NemesisOfReason (final NemesisOfReason card) {
+    public NemesisOfReason(final NemesisOfReason card) {
         super(card);
     }
 
@@ -47,12 +45,12 @@ public final class NemesisOfReason extends CardImpl {
 }
 
 class NemesisOfReasonTriggeredAbility extends TriggeredAbilityImpl {
-    
+
     NemesisOfReasonTriggeredAbility(Effect effect) {
         super(Zone.BATTLEFIELD, effect);
     }
 
-    NemesisOfReasonTriggeredAbility(final NemesisOfReasonTriggeredAbility ability) {
+    private NemesisOfReasonTriggeredAbility(final NemesisOfReasonTriggeredAbility ability) {
         super(ability);
     }
 
@@ -68,9 +66,9 @@ class NemesisOfReasonTriggeredAbility extends TriggeredAbilityImpl {
 
     @Override
     public boolean checkTrigger(GameEvent event, Game game) {
-        if (event.getSourceId().equals(this.getSourceId()) ) {
+        if (event.getSourceId().equals(this.getSourceId())) {
             UUID defenderId = game.getCombat().getDefendingPlayerId(this.getSourceId(), game);
-            for (Effect effect : this.getEffects()) {                
+            for (Effect effect : this.getEffects()) {
                 effect.setTargetPointer(new FixedTarget(defenderId));
             }
             return true;
@@ -80,6 +78,6 @@ class NemesisOfReasonTriggeredAbility extends TriggeredAbilityImpl {
 
     @Override
     public String getRule() {
-        return "Whenever {this} attacks, defending player puts the top ten cards of their library into their graveyard.";
+        return "Whenever {this} attacks, defending player mills ten cards.";
     }
 }
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/p/PatientRebuilding.java b/Mage.Sets/src/mage/cards/p/PatientRebuilding.java
index 130c1182c3..9f4b2ee7bc 100644
--- a/Mage.Sets/src/mage/cards/p/PatientRebuilding.java
+++ b/Mage.Sets/src/mage/cards/p/PatientRebuilding.java
@@ -1,7 +1,5 @@
 package mage.cards.p;
 
-import java.util.Set;
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
@@ -11,13 +9,14 @@ import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.constants.TargetController;
-import mage.constants.Zone;
 import mage.game.Game;
 import mage.players.Player;
 import mage.target.common.TargetOpponent;
 
+import java.util.Set;
+import java.util.UUID;
+
 /**
- *
  * @author TheElk801
  */
 public final class PatientRebuilding extends CardImpl {
@@ -49,7 +48,7 @@ class PatientRebuildingEffect extends OneShotEffect {
 
     public PatientRebuildingEffect() {
         super(Outcome.DrawCard);
-        this.staticText = "target opponent puts the top three cards of their library into their graveyard, "
+        this.staticText = "target opponent mills three cards, "
                 + "then you draw a card for each land card put into that graveyard this way";
     }
 
@@ -70,7 +69,7 @@ class PatientRebuildingEffect extends OneShotEffect {
             return false;
         }
         int numberOfLandCards = 0;
-        Set<Card> movedCards = player.moveCardsToGraveyardWithInfo(player.getLibrary().getTopCards(game, 3), source, game, Zone.LIBRARY);
+        Set<Card> movedCards = player.millCards(3, source, game).getCards(game);
         for (Card card : movedCards) {
             if (card.isLand()) {
                 numberOfLandCards++;
diff --git a/Mage.Sets/src/mage/cards/p/PhenaxGodOfDeception.java b/Mage.Sets/src/mage/cards/p/PhenaxGodOfDeception.java
index ec36c25ab3..decaf844f2 100644
--- a/Mage.Sets/src/mage/cards/p/PhenaxGodOfDeception.java
+++ b/Mage.Sets/src/mage/cards/p/PhenaxGodOfDeception.java
@@ -45,14 +45,12 @@ public final class PhenaxGodOfDeception extends CardImpl {
         // Creatures you control have "{T}: Target player puts the top X cards of their library into their graveyard, where X is this creature's toughness."
         Ability ability = new SimpleActivatedAbility(
                 new PutTopCardOfLibraryIntoGraveTargetEffect(SourcePermanentToughnessValue.getInstance())
-                        .setText("Target player puts the top X cards of their library into their graveyard, " +
-                                "where X is this creature's toughness"), new TapSourceCost());
+                        .setText("Target player mills X cards, where X is this creature's toughness"), new TapSourceCost());
         ability.addTarget(new TargetPlayer());
         this.addAbility(new SimpleStaticAbility(
                 new GainAbilityControlledEffect(
                         ability, Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURES, false
-                ).setText("Creatures you control have \"{T}: Target player puts the top X cards of their library " +
-                        "into their graveyard, where X is this creature's toughness.\"")
+                ).setText("Creatures you control have \"{T}: Target player mills X cards, where X is this creature's toughness.\"")
         ));
     }
 
diff --git a/Mage.Sets/src/mage/cards/p/Predict.java b/Mage.Sets/src/mage/cards/p/Predict.java
index e4f98c5a95..d53aa652fd 100644
--- a/Mage.Sets/src/mage/cards/p/Predict.java
+++ b/Mage.Sets/src/mage/cards/p/Predict.java
@@ -8,7 +8,6 @@ import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.Outcome;
-import mage.constants.Zone;
 import mage.game.Game;
 import mage.players.Player;
 import mage.target.TargetPlayer;
@@ -30,7 +29,7 @@ public final class Predict extends CardImpl {
         this.getSpellAbility().addTarget(new TargetPlayer());
     }
 
-    public Predict(final Predict card) {
+    private Predict(final Predict card) {
         super(card);
     }
 
@@ -42,13 +41,13 @@ public final class Predict extends CardImpl {
 
 class PredictEffect extends OneShotEffect {
 
-    public PredictEffect() {
+    PredictEffect() {
         super(Outcome.DrawCard);
-        this.staticText = ", then target player puts the top card of their library into their graveyard. "
-                + "If that card has the chosen name, you draw two cards. Otherwise, you draw a card.";
+        this.staticText = ", then target player mills a card. If a card with the chosen name was milled this way, " +
+                "you draw two cards. Otherwise, you draw a card.";
     }
 
-    public PredictEffect(final PredictEffect effect) {
+    private PredictEffect(final PredictEffect effect) {
         super(effect);
     }
 
@@ -62,19 +61,17 @@ class PredictEffect extends OneShotEffect {
         Player controller = game.getPlayer(source.getControllerId());
         Player targetPlayer = game.getPlayer(source.getFirstTarget());
         String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY);
-        if (controller != null && targetPlayer != null && cardName != null && !cardName.isEmpty()) {
-            int amount = 1;
-            Card card = targetPlayer.getLibrary().getFromTop(game);
-            if (card != null) {
-                controller.moveCards(card, Zone.GRAVEYARD, source, game);
-                if (CardUtil.haveSameNames(card, cardName, game)) {
-                    amount = 2;
-                }
-            }
-            controller.drawCards(amount, source.getSourceId(), game);
-            return true;
+        if (controller == null || targetPlayer == null || cardName == null || cardName.isEmpty()) {
+            return false;
         }
-        return false;
+        int toDraw = 1;
+        for (Card card : targetPlayer.millCards(1, source, game).getCards(game)) {
+            if (CardUtil.haveSameNames(card, cardName, game)) {
+                toDraw = 2;
+                break;
+            }
+        }
+        controller.drawCards(toDraw, source.getSourceId(), game);
+        return true;
     }
-
 }
diff --git a/Mage.Sets/src/mage/cards/p/PsychicSpiral.java b/Mage.Sets/src/mage/cards/p/PsychicSpiral.java
index f142d79042..6595fa697e 100644
--- a/Mage.Sets/src/mage/cards/p/PsychicSpiral.java
+++ b/Mage.Sets/src/mage/cards/p/PsychicSpiral.java
@@ -63,7 +63,7 @@ class PsychicSpiralEffect extends OneShotEffect {
             if (cardsInGraveyard > 0) {
                 Player targetPlayer = game.getPlayer(source.getFirstTarget());
                 if (targetPlayer != null) {
-                    targetPlayer.moveCards(targetPlayer.getLibrary().getTopCards(game, cardsInGraveyard), Zone.GRAVEYARD, source, game);
+                    targetPlayer.millCards(cardsInGraveyard, source, game);
                 }
             }
             return true;
diff --git a/Mage.Sets/src/mage/cards/p/PsychicStrike.java b/Mage.Sets/src/mage/cards/p/PsychicStrike.java
index f5c63b1c1f..344c345efe 100644
--- a/Mage.Sets/src/mage/cards/p/PsychicStrike.java
+++ b/Mage.Sets/src/mage/cards/p/PsychicStrike.java
@@ -66,7 +66,7 @@ class PsychicStrikeEffect extends OneShotEffect {
         if (stackObject != null) {
             Player controller = game.getPlayer(stackObject.getControllerId());
             if (controller != null) {
-                controller.moveCards(controller.getLibrary().getTopCards(game, 2), Zone.GRAVEYARD, source, game);
+                controller.millCards(2, source, game);
             }
         }
         return countered;
diff --git a/Mage.Sets/src/mage/cards/r/RakshasasSecret.java b/Mage.Sets/src/mage/cards/r/RakshasasSecret.java
index 0b62846b48..2f2ea93bda 100644
--- a/Mage.Sets/src/mage/cards/r/RakshasasSecret.java
+++ b/Mage.Sets/src/mage/cards/r/RakshasasSecret.java
@@ -1,8 +1,5 @@
-
 package mage.cards.r;
 
-import java.util.UUID;
-import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.PutTopCardOfLibraryIntoGraveControllerEffect;
 import mage.abilities.effects.common.discard.DiscardTargetEffect;
 import mage.cards.CardImpl;
@@ -10,25 +7,23 @@ import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.target.common.TargetOpponent;
 
+import java.util.UUID;
+
 /**
- *
  * @author emerald000
  */
 public final class RakshasasSecret extends CardImpl {
 
     public RakshasasSecret(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{B}");
-
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}");
 
         // Target opponent discards two cards. Put the top two cards of your library into your graveyard.
         this.getSpellAbility().addEffect(new DiscardTargetEffect(2));
         this.getSpellAbility().addTarget(new TargetOpponent());
-        Effect effect = new PutTopCardOfLibraryIntoGraveControllerEffect(2);
-        effect.setText("Put the top two cards of your library into your graveyard");
-        this.getSpellAbility().addEffect(effect);
+        this.getSpellAbility().addEffect(new PutTopCardOfLibraryIntoGraveControllerEffect(2).setText("Mill two cards."));
     }
 
-    public RakshasasSecret(final RakshasasSecret card) {
+    private RakshasasSecret(final RakshasasSecret card) {
         super(card);
     }
 
diff --git a/Mage.Sets/src/mage/cards/r/ReefPirates.java b/Mage.Sets/src/mage/cards/r/ReefPirates.java
index 970faaf4ad..b74cbde8fe 100644
--- a/Mage.Sets/src/mage/cards/r/ReefPirates.java
+++ b/Mage.Sets/src/mage/cards/r/ReefPirates.java
@@ -26,7 +26,7 @@ public final class ReefPirates extends CardImpl {
 
         // Whenever Reef Pirates deals damage to an opponent, that player puts the top card of their library into their graveyard.
         Effect effect = new PutLibraryIntoGraveTargetEffect(1);
-        effect.setText("that player puts the top card of their library into their graveyard");
+        effect.setText("that player mills a card");
         this.addAbility(new DealsDamageToAPlayerTriggeredAbility(effect, false, true));
     }
 
diff --git a/Mage.Sets/src/mage/cards/r/Riddlekeeper.java b/Mage.Sets/src/mage/cards/r/Riddlekeeper.java
index 2310fa20ee..d6d08f9799 100644
--- a/Mage.Sets/src/mage/cards/r/Riddlekeeper.java
+++ b/Mage.Sets/src/mage/cards/r/Riddlekeeper.java
@@ -28,7 +28,7 @@ public final class Riddlekeeper extends CardImpl {
 
         // Whenever a creature attacks you or a planeswalker you control, that creature's controller puts the top two cards of their library into their graveyard.
         Effect effect = new PutTopCardOfLibraryIntoGraveTargetEffect(2);
-        effect.setText("that creature's controller puts the top two cards of their library into their graveyard");
+        effect.setText("that creature's controller mills two cards");
         this.addAbility(new AttacksAllTriggeredAbility(effect, false, StaticFilters.FILTER_PERMANENT_CREATURE, SetTargetPointer.PLAYER, true, true));
     }
 
diff --git a/Mage.Sets/src/mage/cards/s/SanityGrinding.java b/Mage.Sets/src/mage/cards/s/SanityGrinding.java
index a63afd36f9..ae141fcb70 100644
--- a/Mage.Sets/src/mage/cards/s/SanityGrinding.java
+++ b/Mage.Sets/src/mage/cards/s/SanityGrinding.java
@@ -1,33 +1,26 @@
-
 package mage.cards.s;
 
-import java.util.UUID;
 import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.dynamicvalue.DynamicValue;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.OneShotEffect;
-import mage.cards.Card;
-import mage.cards.CardImpl;
-import mage.cards.CardSetInfo;
-import mage.cards.Cards;
-import mage.cards.CardsImpl;
+import mage.cards.*;
 import mage.constants.CardType;
 import mage.constants.Outcome;
-import mage.constants.Zone;
 import mage.game.Game;
 import mage.players.Player;
 import mage.target.common.TargetOpponent;
 
+import java.util.UUID;
+
 /**
- *
  * @author jeffwadsworth
- *
  */
 public final class SanityGrinding extends CardImpl {
 
     public SanityGrinding(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{U}{U}{U}");
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{U}{U}{U}");
 
         // Chroma - Reveal the top ten cards of your library. For each blue mana symbol in the mana costs of the revealed cards, target opponent puts the top card of their library into their graveyard. Then put the cards you revealed this way on the bottom of your library in any order.
         this.getSpellAbility().addEffect(new SanityGrindingEffect());
@@ -49,7 +42,9 @@ class SanityGrindingEffect extends OneShotEffect {
 
     public SanityGrindingEffect() {
         super(Outcome.Neutral);
-        staticText = "<i>Chroma</i> &mdash; Reveal the top ten cards of your library. For each blue mana symbol in the mana costs of the revealed cards, target opponent puts the top card of their library into their graveyard. Then put the cards you revealed this way on the bottom of your library in any order";
+        staticText = "<i>Chroma</i> &mdash; Reveal the top ten cards of your library. " +
+                "For each blue mana symbol in the mana costs of the revealed cards, target opponent mills a card. " +
+                "Then put the cards you revealed this way on the bottom of your library in any order";
     }
 
     public SanityGrindingEffect(final SanityGrindingEffect effect) {
@@ -68,8 +63,7 @@ class SanityGrindingEffect extends OneShotEffect {
         controller.revealCards(sourceObject.getIdName(), revealed, game);
         Player targetOpponent = game.getPlayer(source.getFirstTarget());
         if (targetOpponent != null) {
-            targetOpponent.moveCards(targetOpponent.getLibrary().getTopCards(game, new ChromaSanityGrindingCount(revealed).calculate(game, source, this)),
-                    Zone.GRAVEYARD, source, game);
+            targetOpponent.millCards(new ChromaSanityGrindingCount(revealed).calculate(game, source, this), source, game);
         }
         return controller.putCardsOnBottomOfLibrary(revealed, game, source, true);
     }
diff --git a/Mage.Sets/src/mage/cards/s/SaprazzanBreaker.java b/Mage.Sets/src/mage/cards/s/SaprazzanBreaker.java
index df66369b2e..336099b071 100644
--- a/Mage.Sets/src/mage/cards/s/SaprazzanBreaker.java
+++ b/Mage.Sets/src/mage/cards/s/SaprazzanBreaker.java
@@ -1,27 +1,24 @@
 
 package mage.cards.s;
 
-import java.util.UUID;
 import mage.MageInt;
+import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.combat.CantBeBlockedByAllSourceEffect;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.CardType;
-import mage.constants.SubType;
-import mage.constants.Duration;
-import mage.constants.Outcome;
-import mage.constants.Zone;
+import mage.constants.*;
 import mage.filter.StaticFilters;
 import mage.game.Game;
 import mage.players.Player;
 
+import java.util.Objects;
+import java.util.UUID;
+
 /**
- *
  * @author L_J
  */
 public final class SaprazzanBreaker extends CardImpl {
@@ -36,7 +33,7 @@ public final class SaprazzanBreaker extends CardImpl {
         this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new SaprazzanBreakerEffect(), new ManaCostsImpl("{U}")));
     }
 
-    public SaprazzanBreaker(final SaprazzanBreaker card) {
+    private SaprazzanBreaker(final SaprazzanBreaker card) {
         super(card);
     }
 
@@ -48,12 +45,12 @@ public final class SaprazzanBreaker extends CardImpl {
 
 class SaprazzanBreakerEffect extends OneShotEffect {
 
-    public SaprazzanBreakerEffect() {
+    SaprazzanBreakerEffect() {
         super(Outcome.Benefit);
-        this.staticText = "Put the top card of your library into your graveyard. If that card is a land card, {this} can't be blocked this turn";
+        this.staticText = "Mill a card. If a land card was milled this way, {this} can't be blocked this turn";
     }
 
-    public SaprazzanBreakerEffect(final SaprazzanBreakerEffect effect) {
+    private SaprazzanBreakerEffect(final SaprazzanBreakerEffect effect) {
         super(effect);
     }
 
@@ -65,16 +62,12 @@ class SaprazzanBreakerEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player player = game.getPlayer(source.getControllerId());
-        if (player != null) {
-            Card card = player.getLibrary().getFromTop(game);
-            if (card != null) {
-                player.moveCards(card, Zone.GRAVEYARD, source, game);
-                if (card.isLand()) {
-                    game.addEffect(new CantBeBlockedByAllSourceEffect(StaticFilters.FILTER_PERMANENT_CREATURES, Duration.EndOfTurn), source);
-                }
-            }
-            return true;
+        if (player == null) {
+            return false;
         }
-        return false;
+        if (player.millCards(1, source, game).getCards(game).stream().filter(Objects::nonNull).anyMatch(MageObject::isLand)) {
+            game.addEffect(new CantBeBlockedByAllSourceEffect(StaticFilters.FILTER_PERMANENT_CREATURES, Duration.EndOfTurn), source);
+        }
+        return true;
     }
 }
diff --git a/Mage.Sets/src/mage/cards/s/ScreamingShield.java b/Mage.Sets/src/mage/cards/s/ScreamingShield.java
index a31cb67e01..0e37473c59 100644
--- a/Mage.Sets/src/mage/cards/s/ScreamingShield.java
+++ b/Mage.Sets/src/mage/cards/s/ScreamingShield.java
@@ -37,7 +37,7 @@ public final class ScreamingShield extends CardImpl {
         Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(0, 3));
         ability.addEffect(new GainAbilityAttachedEffect(
                 toAdd, AttachmentType.EQUIPMENT
-        ).setText("and has \"{2}, {T}: Target player puts the top three cards of their library into their graveyard.\""));
+        ).setText("and has \"{2}, {T}: Target player mills three cards.\""));
         this.addAbility(ability);
 
         // Equip {3}
diff --git a/Mage.Sets/src/mage/cards/s/ScreechingSilcaw.java b/Mage.Sets/src/mage/cards/s/ScreechingSilcaw.java
index da04611f60..87a8f4c538 100644
--- a/Mage.Sets/src/mage/cards/s/ScreechingSilcaw.java
+++ b/Mage.Sets/src/mage/cards/s/ScreechingSilcaw.java
@@ -1,7 +1,6 @@
 
 package mage.cards.s;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.TriggeredAbility;
 import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
@@ -14,16 +13,15 @@ import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.SubType;
 
+import java.util.UUID;
+
 /**
- *
  * @author ayratn
  */
 public final class ScreechingSilcaw extends CardImpl {
 
-    private static final String rule = "<i>Metalcraft</i> &mdash; Whenever Screeching Silcaw deals combat damage to a player, if you control three or more artifacts, that player puts the top four cards of their library into their graveyard.";
-
     public ScreechingSilcaw(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}");
         this.subtype.add(SubType.BIRD);
 
         this.power = new MageInt(1);
@@ -33,8 +31,11 @@ public final class ScreechingSilcaw extends CardImpl {
 
         //"<i>Metalcraft</i> &mdash; Whenever Screeching Silcaw deals combat damage to a player, if you control three or more artifacts, that player puts the top four cards of their library into their graveyard.
         TriggeredAbility conditional = new ConditionalInterveningIfTriggeredAbility(
-                new DealsCombatDamageToAPlayerTriggeredAbility(new PutLibraryIntoGraveTargetEffect(4), false, true),
-                MetalcraftCondition.instance, rule);
+                new DealsCombatDamageToAPlayerTriggeredAbility(
+                        new PutLibraryIntoGraveTargetEffect(4), false, true
+                ), MetalcraftCondition.instance, "<i>Metalcraft</i> &mdash; Whenever {this} " +
+                "deals combat damage to a player, if you control three or more artifacts, that player mills four cards."
+        );
         this.addAbility(conditional);
     }
 
diff --git a/Mage.Sets/src/mage/cards/s/ScreechingSliver.java b/Mage.Sets/src/mage/cards/s/ScreechingSliver.java
index 908ef9eda5..c91946e029 100644
--- a/Mage.Sets/src/mage/cards/s/ScreechingSliver.java
+++ b/Mage.Sets/src/mage/cards/s/ScreechingSliver.java
@@ -37,7 +37,7 @@ public final class ScreechingSliver extends CardImpl {
 
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
                 new GainAbilityAllEffect(ability, Duration.WhileOnBattlefield,
-                        allSliversFilter, "All Slivers have \"{T}: Target player puts the top card of their library into their graveyard.\"")));
+                        allSliversFilter, "All Slivers have \"{T}: Target player mills a card.\"")));
     }
 
     public ScreechingSliver(final ScreechingSliver card) {
diff --git a/Mage.Sets/src/mage/cards/s/SewerNemesis.java b/Mage.Sets/src/mage/cards/s/SewerNemesis.java
index 7bfc1fa2fc..612a44b550 100644
--- a/Mage.Sets/src/mage/cards/s/SewerNemesis.java
+++ b/Mage.Sets/src/mage/cards/s/SewerNemesis.java
@@ -114,7 +114,7 @@ class SewerNemesisTriggeredAbility extends TriggeredAbilityImpl {
 
     @Override
     public String getRule() {
-        return "Whenever the chosen player casts a spell, that player puts the top card of their library into their graveyard.";
+        return "Whenever the chosen player casts a spell, that player mills a card.";
     }
 
     @Override
diff --git a/Mage.Sets/src/mage/cards/s/SharedTrauma.java b/Mage.Sets/src/mage/cards/s/SharedTrauma.java
index 915bbbeeb1..2f82c35a9b 100644
--- a/Mage.Sets/src/mage/cards/s/SharedTrauma.java
+++ b/Mage.Sets/src/mage/cards/s/SharedTrauma.java
@@ -43,7 +43,7 @@ class SharedTraumaEffect extends OneShotEffect {
 
     public SharedTraumaEffect() {
         super(Outcome.Detriment);
-        this.staticText = "<i>Join forces</i> &mdash; Starting with you, each player may pay any amount of mana. Each player puts the top X cards of their library into their graveyard, where X is the total amount of mana paid this way";
+        this.staticText = "<i>Join forces</i> &mdash; Starting with you, each player may pay any amount of mana. Each player mills X cards, where X is the total amount of mana paid this way";
     }
 
     public SharedTraumaEffect(final SharedTraumaEffect effect) {
diff --git a/Mage.Sets/src/mage/cards/s/Shriekgeist.java b/Mage.Sets/src/mage/cards/s/Shriekgeist.java
index fb41104d80..9c0b1c8c0d 100644
--- a/Mage.Sets/src/mage/cards/s/Shriekgeist.java
+++ b/Mage.Sets/src/mage/cards/s/Shriekgeist.java
@@ -80,6 +80,6 @@ class ShriekgeistTriggeredAbility extends TriggeredAbilityImpl {
 
     @Override
     public String getRule() {
-        return "Whenever {this} deals combat damage to a player, that player puts the top two cards of their library into their graveyard.";
+        return "Whenever {this} deals combat damage to a player, that player mills two cards.";
     }
 }
diff --git a/Mage.Sets/src/mage/cards/s/SirenOfTheSilentSong.java b/Mage.Sets/src/mage/cards/s/SirenOfTheSilentSong.java
index 9bb077784a..9bde3310b1 100644
--- a/Mage.Sets/src/mage/cards/s/SirenOfTheSilentSong.java
+++ b/Mage.Sets/src/mage/cards/s/SirenOfTheSilentSong.java
@@ -34,7 +34,7 @@ public final class SirenOfTheSilentSong extends CardImpl {
         // <i>Inspired</i> &mdash; Whenever Siren of the Silent Song becomes untapped, each opponent discards a card, then puts the top card of their library into their graveyard.
         Ability ability = new InspiredAbility(new DiscardEachPlayerEffect(TargetController.OPPONENT));
         Effect effect = new PutTopCardOfLibraryIntoGraveEachPlayerEffect(1, TargetController.OPPONENT);
-        effect.setText(", then puts the top card of their library into their graveyard");
+        effect.setText(", then mills a card");
         ability.addEffect(effect);
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/s/SongOfBlood.java b/Mage.Sets/src/mage/cards/s/SongOfBlood.java
index d786f476dd..9838614994 100644
--- a/Mage.Sets/src/mage/cards/s/SongOfBlood.java
+++ b/Mage.Sets/src/mage/cards/s/SongOfBlood.java
@@ -1,22 +1,17 @@
 package mage.cards.s;
 
-import java.util.Set;
-import java.util.UUID;
+import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.DelayedTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.continuous.BoostTargetEffect;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.cards.Cards;
-import mage.cards.CardsImpl;
 import mage.constants.CardType;
 import mage.constants.Duration;
 import mage.constants.Outcome;
 import mage.constants.Zone;
-import mage.filter.StaticFilters;
 import mage.game.Game;
 import mage.game.events.GameEvent;
 import mage.game.events.GameEvent.EventType;
@@ -24,8 +19,10 @@ import mage.game.permanent.Permanent;
 import mage.players.Player;
 import mage.target.targetpointer.FixedTarget;
 
+import java.util.Objects;
+import java.util.UUID;
+
 /**
- *
  * @author spjspj
  */
 public final class SongOfBlood extends CardImpl {
@@ -38,7 +35,7 @@ public final class SongOfBlood extends CardImpl {
         this.getSpellAbility().addEffect(new SongOfBloodEffect());
     }
 
-    public SongOfBlood(final SongOfBlood card) {
+    private SongOfBlood(final SongOfBlood card) {
         super(card);
     }
 
@@ -50,13 +47,14 @@ public final class SongOfBlood extends CardImpl {
 
 class SongOfBloodEffect extends OneShotEffect {
 
-    public SongOfBloodEffect() {
+    SongOfBloodEffect() {
         super(Outcome.LoseLife);
-        this.staticText = "Put the top four cards of your library into your graveyard.";
+        this.staticText = "Mill four cards. Whenever a creature attacks this turn, " +
+                "it gets +1/+0 until end of turn for each creature card put into your graveyard this way.";
 
     }
 
-    public SongOfBloodEffect(final SongOfBloodEffect effect) {
+    private SongOfBloodEffect(final SongOfBloodEffect effect) {
         super(effect);
     }
 
@@ -68,36 +66,35 @@ class SongOfBloodEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player controller = game.getPlayer(source.getControllerId());
-        if (controller != null) {
-            Cards cardsToGraveyard = new CardsImpl();
-            cardsToGraveyard.addAll(controller.getLibrary().getTopCards(game, 4));
-            if (!cardsToGraveyard.isEmpty()) {
-                Set<Card> movedCards = controller.moveCardsToGraveyardWithInfo(cardsToGraveyard.getCards(game), source, game, Zone.LIBRARY);
-                Cards cardsMoved = new CardsImpl();
-                cardsMoved.addAll(movedCards);
-                int creatures = cardsMoved.count(StaticFilters.FILTER_CARD_CREATURE, game);
-                if (creatures > 0) {
-                    // Setup a delayed trigger to give +X/+0 to any creature attacking this turn..
-                    DelayedTriggeredAbility delayedAbility = new SongOfBloodTriggeredAbility(creatures);
-                    game.addDelayedTriggeredAbility(delayedAbility, source);
-                }
-            }
-            return true;
+        if (controller == null) {
+            return false;
         }
-        return false;
+        int creatures = controller
+                .millCards(4, source, game)
+                .getCards(game)
+                .stream()
+                .filter(Objects::nonNull)
+                .filter(card -> game.getState().getZone(card.getId()) == Zone.GRAVEYARD)
+                .filter(MageObject::isCreature)
+                .mapToInt(x -> 1)
+                .sum();
+        // Setup a delayed trigger to give +X/+0 to any creature attacking this turn..
+        DelayedTriggeredAbility delayedAbility = new SongOfBloodTriggeredAbility(creatures);
+        game.addDelayedTriggeredAbility(delayedAbility, source);
+        return true;
     }
 }
 
 class SongOfBloodTriggeredAbility extends DelayedTriggeredAbility {
 
-    int booster;
+    private final int booster;
 
-    public SongOfBloodTriggeredAbility(int booster) {
+    SongOfBloodTriggeredAbility(int booster) {
         super(new BoostTargetEffect(booster, 0, Duration.EndOfTurn), Duration.EndOfTurn, false);
         this.booster = booster;
     }
 
-    public SongOfBloodTriggeredAbility(SongOfBloodTriggeredAbility ability) {
+    private SongOfBloodTriggeredAbility(SongOfBloodTriggeredAbility ability) {
         super(ability);
         this.booster = ability.booster;
     }
@@ -126,6 +123,6 @@ class SongOfBloodTriggeredAbility extends DelayedTriggeredAbility {
 
     @Override
     public String getRule() {
-        return "Whenever a creature attacks this turn, it gets +1/+0 (+" + booster + "/0) until end of turn for each creature card put into your graveyard this way.";
+        return "Whenever a creature attacks this turn, it gets +" + booster + "/0 until end of turn.";
     }
 }
diff --git a/Mage.Sets/src/mage/cards/s/SphinxsTutelage.java b/Mage.Sets/src/mage/cards/s/SphinxsTutelage.java
index 1b5868ca4e..372f47facf 100644
--- a/Mage.Sets/src/mage/cards/s/SphinxsTutelage.java
+++ b/Mage.Sets/src/mage/cards/s/SphinxsTutelage.java
@@ -1,7 +1,7 @@
 
 package mage.cards.s;
 
-import java.util.UUID;
+import mage.ObjectColor;
 import mage.abilities.Ability;
 import mage.abilities.common.DrawCardControllerTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
@@ -11,8 +11,6 @@ import mage.abilities.effects.common.DrawDiscardControllerEffect;
 import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.cards.Cards;
-import mage.cards.CardsImpl;
 import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.constants.Zone;
@@ -20,8 +18,11 @@ import mage.game.Game;
 import mage.players.Player;
 import mage.target.common.TargetOpponent;
 
+import java.util.List;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
 /**
- *
  * @author LevelX2
  */
 public final class SphinxsTutelage extends CardImpl {
@@ -52,7 +53,7 @@ class SphinxsTutelageEffect extends OneShotEffect {
 
     public SphinxsTutelageEffect() {
         super(Outcome.Benefit);
-        this.staticText = "target opponent puts the top two cards of their library into their graveyard. If they're both nonland cards that share a color, repeat this process";
+        this.staticText = "target opponent mills two cards. If two nonland cards that share a color were milled this way, repeat this process.";
     }
 
     public SphinxsTutelageEffect(final SphinxsTutelageEffect effect) {
@@ -67,10 +68,11 @@ class SphinxsTutelageEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player targetPlayer = game.getPlayer(this.getTargetPointer().getFirst(game, source));
-        boolean colorShared;
+
         if (targetPlayer != null) {
             int possibleIterations = targetPlayer.getLibrary().size() / 2;
             int iteration = 0;
+            boolean colorShared;
             do {
                 iteration++;
                 if (iteration > possibleIterations + 20) {
@@ -82,20 +84,35 @@ class SphinxsTutelageEffect extends OneShotEffect {
                     return true;
                 }
                 colorShared = false;
-                Cards cards = new CardsImpl(targetPlayer.getLibrary().getTopCards(game, 2));
-                Card card1 = null;
-                for (Card card : cards.getCards(game)) {
-                    if (card.isLand()) {
+                List<Card> cards = targetPlayer
+                        .millCards(2, source, game)
+                        .getCards(game)
+                        .stream()
+                        .filter(card -> !card.isLand())
+                        .collect(Collectors.toList());
+                if (cards.size() < 2) {
+                    break;
+                }
+                for (int i = 0; i < cards.size(); i++) {
+                    if (colorShared) {
                         break;
                     }
-                    if (card1 == null) {
-                        card1 = card;
-                    } else {
-                        colorShared = card1.getColor(game).shares(card.getColor(game));
+                    ObjectColor color1 = cards.get(i).getColor(game);
+                    if (color1.isColorless()) {
+                        continue;
+                    }
+                    for (int j = 0; j < cards.size(); j++) {
+                        if (i >= j) {
+                            continue;
+                        }
+                        ObjectColor color2 = cards.get(j).getColor(game);
+                        if (color1.shares(color2)) {
+                            colorShared = true;
+                            break;
+                        }
                     }
                 }
-                targetPlayer.moveCards(cards, Zone.GRAVEYARD, source, game);
-            } while (colorShared && targetPlayer.isInGame());
+            } while (colorShared);
             return true;
         }
         return false;
diff --git a/Mage.Sets/src/mage/cards/s/SternMentor.java b/Mage.Sets/src/mage/cards/s/SternMentor.java
index f920d8448e..997d6be148 100644
--- a/Mage.Sets/src/mage/cards/s/SternMentor.java
+++ b/Mage.Sets/src/mage/cards/s/SternMentor.java
@@ -22,7 +22,8 @@ import mage.target.TargetPlayer;
  */
 public final class SternMentor extends CardImpl {
 
-    private static final String ruleText = "As long as {this} is paired with another creature, each of those creatures has \"{T}: Target player puts the top two cards of their library into their graveyard.\"";
+    private static final String ruleText = "As long as {this} is paired with another creature, " +
+            "each of those creatures has \"{T}: Target player mills two cards.\"";
 
     public SternMentor(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{U}");
diff --git a/Mage.Sets/src/mage/cards/s/StitcherGeralf.java b/Mage.Sets/src/mage/cards/s/StitcherGeralf.java
index 9c84a668d3..bbfc44d0d0 100644
--- a/Mage.Sets/src/mage/cards/s/StitcherGeralf.java
+++ b/Mage.Sets/src/mage/cards/s/StitcherGeralf.java
@@ -56,7 +56,7 @@ class StitcherGeralfEffect extends OneShotEffect {
 
     public StitcherGeralfEffect() {
         super(Outcome.PutCreatureInPlay);
-        this.staticText = "Each player puts the top three cards of their library into their graveyard. Exile up to two creature cards put into graveyards this way. Create an X/X blue Zombie creature token, where X is the total power of the cards exiled this way";
+        this.staticText = "Each player mills three cards. Exile up to two creature cards put into graveyards this way. Create an X/X blue Zombie creature token, where X is the total power of the cards exiled this way";
     }
 
     public StitcherGeralfEffect(final StitcherGeralfEffect effect) {
@@ -76,10 +76,10 @@ class StitcherGeralfEffect extends OneShotEffect {
             for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
                 Player player = game.getPlayer(playerId);
                 if (player != null) {
-                    cards.addAll(player.getLibrary().getTopCards(game, 3));
+                    cards.addAll(player.millCards(3,source,game));
                 }
             }
-            controller.moveCards(cards, Zone.GRAVEYARD, source, game);
+            cards.removeIf(uuid -> game.getState().getZone(uuid)!=Zone.GRAVEYARD);
             TargetCard target = new TargetCard(0, 2, Zone.GRAVEYARD, new FilterCreatureCard("creature cards to exile"));
             controller.chooseTarget(outcome, cards, target, source, game);
             int power = 0;
diff --git a/Mage.Sets/src/mage/cards/s/SwordOfBodyAndMind.java b/Mage.Sets/src/mage/cards/s/SwordOfBodyAndMind.java
index e412f9e57a..ead24ed438 100644
--- a/Mage.Sets/src/mage/cards/s/SwordOfBodyAndMind.java
+++ b/Mage.Sets/src/mage/cards/s/SwordOfBodyAndMind.java
@@ -100,6 +100,6 @@ class SwordOfBodyAndMindAbility extends TriggeredAbilityImpl {
 
     @Override
     public String getRule() {
-        return "Whenever equipped creature deals combat damage to a player, you create a 2/2 green Wolf creature token and that player puts the top ten cards of their library into their graveyard.";
+        return "Whenever equipped creature deals combat damage to a player, you create a 2/2 green Wolf creature token and that player mills ten cards.";
     }
 }
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/s/SzadekLordOfSecrets.java b/Mage.Sets/src/mage/cards/s/SzadekLordOfSecrets.java
index 893008fa81..7feea17314 100644
--- a/Mage.Sets/src/mage/cards/s/SzadekLordOfSecrets.java
+++ b/Mage.Sets/src/mage/cards/s/SzadekLordOfSecrets.java
@@ -54,7 +54,7 @@ class SzadekLordOfSecretsEffect extends ReplacementEffectImpl {
 
     SzadekLordOfSecretsEffect() {
         super(Duration.WhileOnBattlefield, Outcome.Benefit);
-        staticText = "If {this} would deal combat damage to a player, instead put that many +1/+1 counters on {this} and that player puts that many cards from the top of their library into their graveyard";
+        staticText = "If {this} would deal combat damage to a player, instead put that many +1/+1 counters on {this} and that player mills that many cards";
     }
 
     SzadekLordOfSecretsEffect(final SzadekLordOfSecretsEffect effect) {
@@ -71,7 +71,7 @@ class SzadekLordOfSecretsEffect extends ReplacementEffectImpl {
             if (permanent != null) {
                 permanent.addCounters(CounterType.P1P1.createInstance(damageEvent.getAmount()), source, game);
                 if (damagedPlayer != null) {
-                    damagedPlayer.moveCards(damagedPlayer.getLibrary().getTopCards(game, damageEvent.getAmount()), Zone.GRAVEYARD, source, game);
+                    damagedPlayer.millCards(damageEvent.getAmount(), source, game);
                 }
             }
         }
diff --git a/Mage.Sets/src/mage/cards/t/TheMendingOfDominaria.java b/Mage.Sets/src/mage/cards/t/TheMendingOfDominaria.java
index bfd4f42c4d..3438e2374b 100644
--- a/Mage.Sets/src/mage/cards/t/TheMendingOfDominaria.java
+++ b/Mage.Sets/src/mage/cards/t/TheMendingOfDominaria.java
@@ -56,7 +56,7 @@ class TheMendingOfDominariaFirstEffect extends OneShotEffect {
 
     public TheMendingOfDominariaFirstEffect() {
         super(Outcome.ReturnToHand);
-        this.staticText = "Put the top two cards of your library into your graveyard, then you may return a creature card from your graveyard to your hand";
+        this.staticText = "Mill two cards, then you may return a creature card from your graveyard to your hand";
     }
 
     public TheMendingOfDominariaFirstEffect(final TheMendingOfDominariaFirstEffect effect) {
diff --git a/Mage.Sets/src/mage/cards/t/ThoughtCollapse.java b/Mage.Sets/src/mage/cards/t/ThoughtCollapse.java
index ed4faf06f7..dae6de6149 100644
--- a/Mage.Sets/src/mage/cards/t/ThoughtCollapse.java
+++ b/Mage.Sets/src/mage/cards/t/ThoughtCollapse.java
@@ -8,7 +8,6 @@ import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.Outcome;
-import mage.constants.Zone;
 import mage.game.Game;
 import mage.players.Player;
 import mage.target.TargetSpell;
@@ -44,8 +43,7 @@ class ThoughtCollapseEffect extends OneShotEffect {
 
     ThoughtCollapseEffect() {
         super(Outcome.Benefit);
-        staticText = "Counter target spell. Its controller puts " +
-                "the top three cards of their library into their graveyard.";
+        staticText = "Counter target spell. Its controller mills three cards";
     }
 
     private ThoughtCollapseEffect(final ThoughtCollapseEffect effect) {
@@ -63,7 +61,7 @@ class ThoughtCollapseEffect extends OneShotEffect {
         if (player == null) {
             return false;
         }
-        player.moveCards(player.getLibrary().getTopCards(game, 3), Zone.GRAVEYARD, source, game);
+        player.millCards(3, source, game);
         return effect.apply(game, source);
     }
-}
\ No newline at end of file
+}
diff --git a/Mage.Sets/src/mage/cards/t/ToweringWaveMystic.java b/Mage.Sets/src/mage/cards/t/ToweringWaveMystic.java
index ba686dda8f..e6efedf261 100644
--- a/Mage.Sets/src/mage/cards/t/ToweringWaveMystic.java
+++ b/Mage.Sets/src/mage/cards/t/ToweringWaveMystic.java
@@ -76,7 +76,6 @@ class ToweringWaveMysticTriggeredAbility extends TriggeredAbilityImpl {
 
     @Override
     public String getRule() {
-        return "Whenever {this} deals damage, target player puts that many cards " +
-                "from the top of their library into their graveyard.";
+        return "Whenever {this} deals damage, target player mills that many cards.";
     }
 }
diff --git a/Mage.Sets/src/mage/cards/t/TymaretCallsTheDead.java b/Mage.Sets/src/mage/cards/t/TymaretCallsTheDead.java
index 8ed90c07f2..9086b58083 100644
--- a/Mage.Sets/src/mage/cards/t/TymaretCallsTheDead.java
+++ b/Mage.Sets/src/mage/cards/t/TymaretCallsTheDead.java
@@ -71,8 +71,7 @@ class TymaretCallsTheDeadFirstEffect extends OneShotEffect {
 
     TymaretCallsTheDeadFirstEffect() {
         super(Benefit);
-        staticText = "put the top three cards of your library into your graveyard. " +
-                "Then you may exile a creature or enchantment card from your graveyard. " +
+        staticText = "mill three cards. Then you may exile a creature or enchantment card from your graveyard. " +
                 "If you do, create a 2/2 black Zombie creature token";
     }
 
diff --git a/Mage.Sets/src/mage/cards/u/UndeadAlchemist.java b/Mage.Sets/src/mage/cards/u/UndeadAlchemist.java
index 2953587af9..a61b5fd197 100644
--- a/Mage.Sets/src/mage/cards/u/UndeadAlchemist.java
+++ b/Mage.Sets/src/mage/cards/u/UndeadAlchemist.java
@@ -24,13 +24,12 @@ import mage.target.targetpointer.FixedTarget;
 import java.util.UUID;
 
 /**
- *
  * @author BetaSteward
  */
 public final class UndeadAlchemist extends CardImpl {
 
     public UndeadAlchemist(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{U}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}");
         this.subtype.add(SubType.ZOMBIE);
 
         this.power = new MageInt(4);
@@ -97,10 +96,10 @@ class UndeadAlchemistEffect extends ReplacementEffectImpl {
 
     UndeadAlchemistEffect() {
         super(Duration.WhileOnBattlefield, Outcome.RedirectDamage);
-        staticText = "If a Zombie you control would deal combat damage to a player, instead that player puts that many cards from the top of their library into their graveyard";
+        staticText = "If a Zombie you control would deal combat damage to a player, instead that player mills that many cards";
     }
 
-    UndeadAlchemistEffect(final UndeadAlchemistEffect effect) {
+    private UndeadAlchemistEffect(final UndeadAlchemistEffect effect) {
         super(effect);
     }
 
@@ -108,7 +107,8 @@ class UndeadAlchemistEffect extends ReplacementEffectImpl {
     public boolean replaceEvent(GameEvent event, Ability source, Game game) {
         Player player = game.getPlayer(event.getTargetId());
         if (player != null) {
-            return player.moveCards(player.getLibrary().getTopCards(game, event.getAmount()), Zone.GRAVEYARD, source, game);
+            player.millCards(event.getAmount(), source, game);
+            return true;
         }
         return true;
     }
diff --git a/Mage.Sets/src/mage/cards/u/UrzaAcademyHeadmaster.java b/Mage.Sets/src/mage/cards/u/UrzaAcademyHeadmaster.java
index 48d020b4d2..137e2b4653 100644
--- a/Mage.Sets/src/mage/cards/u/UrzaAcademyHeadmaster.java
+++ b/Mage.Sets/src/mage/cards/u/UrzaAcademyHeadmaster.java
@@ -268,7 +268,7 @@ class UrzaAcademyHeadmasterRandomEffect extends OneShotEffect {
                                 effects.add(new UrzaAcademyHeadmasterBrainstormEffect());
                                 break;
                             case 8: // JACE MEMORY ADEPT 2
-                                sb.append("Target player puts the top ten cards of their library into their graveyard.");
+                                sb.append("Target player mills ten cards.");
                                 effects.add(new PutLibraryIntoGraveTargetEffect(10));
                                 target = new TargetPlayer();
                                 break;
diff --git a/Mage.Sets/src/mage/cards/v/ViciousRumors.java b/Mage.Sets/src/mage/cards/v/ViciousRumors.java
index 245ce9a78e..34e937b174 100644
--- a/Mage.Sets/src/mage/cards/v/ViciousRumors.java
+++ b/Mage.Sets/src/mage/cards/v/ViciousRumors.java
@@ -31,8 +31,7 @@ public final class ViciousRumors extends CardImpl {
         this.getSpellAbility().addEffect(
                 new PutTopCardOfLibraryIntoGraveEachPlayerEffect(
                         1, TargetController.OPPONENT
-                ).setText(", then puts the top card of their library "
-                        + "into their graveyard")
+                ).setText(", then mills a card")
         );
         this.getSpellAbility().addEffect(new GainLifeEffect(1));
     }
diff --git a/Mage.Sets/src/mage/cards/w/Whetstone.java b/Mage.Sets/src/mage/cards/w/Whetstone.java
index b151b288e2..84f564588e 100644
--- a/Mage.Sets/src/mage/cards/w/Whetstone.java
+++ b/Mage.Sets/src/mage/cards/w/Whetstone.java
@@ -1,34 +1,30 @@
-
 package mage.cards.w;
 
-import java.util.UUID;
-import mage.abilities.Ability;
 import mage.abilities.common.SimpleActivatedAbility;
-import mage.abilities.costs.mana.ManaCostsImpl;
-import mage.abilities.effects.OneShotEffect;
+import mage.abilities.costs.mana.GenericManaCost;
+import mage.abilities.effects.common.PutTopCardOfLibraryIntoGraveEachPlayerEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.Outcome;
-import mage.constants.Zone;
-import mage.game.Game;
-import mage.players.Player;
+import mage.constants.TargetController;
+
+import java.util.UUID;
 
 /**
- *
  * @author Backfir3
  */
 public final class Whetstone extends CardImpl {
 
     public Whetstone(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}");
+        super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}");
 
         //{3}: Each player puts the top two cards of their library into their graveyard.
-        SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new WhetstoneEffect(), new ManaCostsImpl("{3}"));
-        this.addAbility(ability);
+        this.addAbility(new SimpleActivatedAbility(new PutTopCardOfLibraryIntoGraveEachPlayerEffect(
+                2, TargetController.ANY
+        ), new GenericManaCost(3)));
     }
 
-    public Whetstone(final Whetstone card) {
+    private Whetstone(final Whetstone card) {
         super(card);
     }
 
@@ -37,31 +33,3 @@ public final class Whetstone extends CardImpl {
         return new Whetstone(this);
     }
 }
-
-class WhetstoneEffect extends OneShotEffect {
-
-    WhetstoneEffect() {
-        super(Outcome.Detriment);
-        staticText = "Each player puts the top two cards of their library into their graveyard";
-    }
-
-    WhetstoneEffect(final WhetstoneEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) {
-            Player player = game.getPlayer(playerId);
-            if (player != null) {
-                player.moveCards(player.getLibrary().getTopCards(game, 2), Zone.GRAVEYARD, source, game);
-            }
-        }
-        return true;
-    }
-
-    @Override
-    public WhetstoneEffect copy() {
-        return new WhetstoneEffect(this);
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/w/WorryBeads.java b/Mage.Sets/src/mage/cards/w/WorryBeads.java
index 47423bd82f..6f206e45f4 100644
--- a/Mage.Sets/src/mage/cards/w/WorryBeads.java
+++ b/Mage.Sets/src/mage/cards/w/WorryBeads.java
@@ -1,7 +1,5 @@
-
 package mage.cards.w;
 
-import java.util.UUID;
 import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
 import mage.abilities.effects.common.PutLibraryIntoGraveTargetEffect;
 import mage.cards.CardImpl;
@@ -10,8 +8,9 @@ import mage.constants.CardType;
 import mage.constants.TargetController;
 import mage.constants.Zone;
 
+import java.util.UUID;
+
 /**
- *
  * @author TheElk801
  */
 public final class WorryBeads extends CardImpl {
@@ -20,12 +19,13 @@ public final class WorryBeads extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}");
 
         // At the beginning of each player's upkeep, that player puts the top card of their library into their graveyard.
-        this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD,
-                new PutLibraryIntoGraveTargetEffect(1).setText("that player puts the top card of their library into their graveyard"),
-                TargetController.ANY, false, true));
+        this.addAbility(new BeginningOfUpkeepTriggeredAbility(
+                Zone.BATTLEFIELD, new PutLibraryIntoGraveTargetEffect(1).setText("that player mills a card"),
+                TargetController.ANY, false, true
+        ));
     }
 
-    public WorryBeads(final WorryBeads card) {
+    private WorryBeads(final WorryBeads card) {
         super(card);
     }
 
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/GrindstoneTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/GrindstoneTest.java
index db664d3bf5..604df87eeb 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/GrindstoneTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/GrindstoneTest.java
@@ -8,7 +8,6 @@ import org.junit.Test;
 import org.mage.test.serverside.base.CardTestPlayerBase;
 
 /**
- *
  * @author LevelX2
  */
 public class GrindstoneTest extends CardTestPlayerBase {
@@ -34,7 +33,7 @@ public class GrindstoneTest extends CardTestPlayerBase {
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Painter's Servant");
         setChoice(playerA, "Blue");
 
-        activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{3}, {T}: Target player puts the top two cards of their library into their graveyard. If both cards share a color, repeat this process.");
+        activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{3}, {T}: Target player mills");
         addTarget(playerA, playerB);
 
         setStopAt(1, PhaseStep.END_TURN);
@@ -65,7 +64,7 @@ public class GrindstoneTest extends CardTestPlayerBase {
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Painter's Servant");
         setChoice(playerA, "Blue");
 
-        activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{3}, {T}: Target player puts the top two cards of their library into their graveyard. If both cards share a color, repeat this process.");
+        activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{3}, {T}: Target player mills");
         addTarget(playerA, playerB);
 
         setStopAt(1, PhaseStep.END_TURN);
@@ -98,7 +97,7 @@ public class GrindstoneTest extends CardTestPlayerBase {
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Painter's Servant");
         setChoice(playerA, "Blue");
 
-        activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{3}, {T}: Target player puts the top two cards of their library into their graveyard. If both cards share a color, repeat this process.");
+        activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{3}, {T}: Target player mills");
         addTarget(playerA, playerB);
 
         setStopAt(1, PhaseStep.END_TURN);
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/LeylineOfTheVoidTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/LeylineOfTheVoidTest.java
index b35f8571c0..627877016a 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/LeylineOfTheVoidTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/LeylineOfTheVoidTest.java
@@ -33,7 +33,7 @@ public class LeylineOfTheVoidTest extends CardTestPlayerBase {
         // {X}, {T}: Target opponent puts cards from the top of their library into their graveyard until a creature card or X cards are put into that graveyard this way, whichever comes first. If a creature card is put into that graveyard this way, sacrifice Helm of Obedience and put that card onto the battlefield under your control. X can't be 0.
         addCard(Zone.BATTLEFIELD, playerA, "Helm of Obedience");
 
-        activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{X}, {T}: Target opponent puts cards", playerB);
+        activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{X}, {T}: Target opponent mills", playerB);
         setChoice(playerA, "X=1");
 
         setStopAt(1, PhaseStep.END_TURN);
diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
index e4cf1bae82..7e424803a8 100644
--- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
+++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
@@ -3421,6 +3421,11 @@ public class TestPlayer implements Player {
         return computerPlayer.moveCardToCommandWithInfo(card, sourceId, game, fromZone);
     }
 
+    @Override
+    public Cards millCards(int toMill, Ability source, Game game) {
+        return computerPlayer.millCards(toMill, source, game);
+    }
+
     @Override
     public boolean hasOpponent(UUID playerToCheckId, Game game) {
         return computerPlayer.hasOpponent(playerToCheckId, game);
diff --git a/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java b/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java
index 652a654636..358d9ee621 100644
--- a/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java
+++ b/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java
@@ -896,7 +896,7 @@ public class PlayerStub implements Player {
     public boolean putCardsOnTopOfLibrary(Card card, Game game, Ability source, boolean anyOrder) {
         return false;
     }
-    
+
     @Override
     public boolean shuffleCardsToLibrary(Cards cards, Game game, Ability source) {
         return false;
@@ -906,7 +906,7 @@ public class PlayerStub implements Player {
     public boolean shuffleCardsToLibrary(Card card, Game game, Ability source) {
         return false;
     }
-    
+
     @Override
     public boolean putCardOnTopXOfLibrary(Card card, Game game, Ability source, int xFromTheTop) {
         return true;
@@ -1202,6 +1202,11 @@ public class PlayerStub implements Player {
         return false;
     }
 
+    @Override
+    public Cards millCards(int toMill, Ability source, Game game) {
+        return null;
+    }
+
     @Override
     public boolean hasOpponent(UUID playerToCheckId, Game game) {
         return false;
diff --git a/Mage/src/main/java/mage/abilities/costs/common/PutTopCardOfYourLibraryToGraveyardCost.java b/Mage/src/main/java/mage/abilities/costs/common/PutTopCardOfYourLibraryToGraveyardCost.java
index ecefd2df46..821e46283f 100644
--- a/Mage/src/main/java/mage/abilities/costs/common/PutTopCardOfYourLibraryToGraveyardCost.java
+++ b/Mage/src/main/java/mage/abilities/costs/common/PutTopCardOfYourLibraryToGraveyardCost.java
@@ -4,7 +4,6 @@ import mage.abilities.Ability;
 import mage.abilities.costs.Cost;
 import mage.abilities.costs.CostImpl;
 import mage.cards.Card;
-import mage.constants.Zone;
 import mage.game.Game;
 import mage.players.Player;
 import mage.util.CardUtil;
@@ -38,8 +37,7 @@ public class PutTopCardOfYourLibraryToGraveyardCost extends CostImpl {
         Player player = game.getPlayer(controllerId);
         if (player != null && player.getLibrary().size() >= numberOfCards) {
             paid = true;
-            this.cardsMovedToGraveyard.addAll(player.getLibrary().getTopCards(game, numberOfCards));
-            player.moveCards(player.getLibrary().getTopCards(game, numberOfCards), Zone.GRAVEYARD, ability, game);
+            this.cardsMovedToGraveyard.addAll(player.millCards(numberOfCards, ability, game).getCards(game));
         }
         return paid;
     }
diff --git a/Mage/src/main/java/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java
index 06c3743a18..fe77559134 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java
@@ -6,7 +6,6 @@ import mage.abilities.dynamicvalue.DynamicValue;
 import mage.abilities.dynamicvalue.common.StaticValue;
 import mage.abilities.effects.OneShotEffect;
 import mage.constants.Outcome;
-import mage.constants.Zone;
 import mage.game.Game;
 import mage.players.Player;
 import mage.util.CardUtil;
@@ -45,7 +44,7 @@ public class PutLibraryIntoGraveTargetEffect extends OneShotEffect {
     public boolean apply(Game game, Ability source) {
         Player player = game.getPlayer(targetPointer.getFirst(game, source));
         if (player != null) {
-            player.moveCards(player.getLibrary().getTopCards(game, amount.calculate(game, source, this)), Zone.GRAVEYARD, source, game);
+            player.millCards(amount.calculate(game, source, this), source, game);
             return true;
         }
         return false;
diff --git a/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveControllerEffect.java
index b944fba65b..423daa126e 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveControllerEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveControllerEffect.java
@@ -3,7 +3,6 @@ package mage.abilities.effects.common;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
 import mage.constants.Outcome;
-import mage.constants.Zone;
 import mage.game.Game;
 import mage.players.Player;
 import mage.util.CardUtil;
@@ -36,7 +35,7 @@ public class PutTopCardOfLibraryIntoGraveControllerEffect extends OneShotEffect
     public boolean apply(Game game, Ability source) {
         Player controller = game.getPlayer(source.getControllerId());
         if (controller != null) {
-            return controller.moveCards(controller.getLibrary().getTopCards(game, numberCards), Zone.GRAVEYARD, source, game);
+            return !controller.millCards(numberCards, source, game).isEmpty();
         }
         return false;
     }
diff --git a/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveEachPlayerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveEachPlayerEffect.java
index 0809aedfae..fb29279aa9 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveEachPlayerEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveEachPlayerEffect.java
@@ -6,7 +6,6 @@ import mage.abilities.dynamicvalue.common.StaticValue;
 import mage.abilities.effects.OneShotEffect;
 import mage.constants.Outcome;
 import mage.constants.TargetController;
-import mage.constants.Zone;
 import mage.game.Game;
 import mage.players.Player;
 import mage.util.CardUtil;
@@ -76,7 +75,7 @@ public class PutTopCardOfLibraryIntoGraveEachPlayerEffect extends OneShotEffect
     private void putCardsToGravecard(UUID playerId, Ability source, Game game) {
         Player player = game.getPlayer(playerId);
         if (player != null) {
-            player.moveCards(player.getLibrary().getTopCards(game, numberCards.calculate(game, source, this)), Zone.GRAVEYARD, source, game);
+            player.millCards(numberCards.calculate(game, source, this), source, game);
         }
     }
 
diff --git a/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveTargetEffect.java
index 9af46672ac..5c33b2f936 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveTargetEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/PutTopCardOfLibraryIntoGraveTargetEffect.java
@@ -5,7 +5,6 @@ import mage.abilities.dynamicvalue.DynamicValue;
 import mage.abilities.dynamicvalue.common.StaticValue;
 import mage.abilities.effects.OneShotEffect;
 import mage.constants.Outcome;
-import mage.constants.Zone;
 import mage.game.Game;
 import mage.players.Player;
 import mage.util.CardUtil;
@@ -41,7 +40,7 @@ public class PutTopCardOfLibraryIntoGraveTargetEffect extends OneShotEffect {
     public boolean apply(Game game, Ability source) {
         Player player = game.getPlayer(targetPointer.getFirst(game, source));
         if (player != null) {
-            player.moveCards(player.getLibrary().getTopCards(game, numberCards.calculate(game, source, this)), Zone.GRAVEYARD, source, game);
+            player.millCards(numberCards.calculate(game, source, this), source, game);
             return true;
         }
         return false;
diff --git a/Mage/src/main/java/mage/abilities/keyword/DredgeAbility.java b/Mage/src/main/java/mage/abilities/keyword/DredgeAbility.java
index c2f2b79f2d..ff67e00c50 100644
--- a/Mage/src/main/java/mage/abilities/keyword/DredgeAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/DredgeAbility.java
@@ -4,8 +4,6 @@ import mage.abilities.Ability;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.ReplacementEffectImpl;
 import mage.cards.Card;
-import mage.cards.Cards;
-import mage.cards.CardsImpl;
 import mage.constants.Duration;
 import mage.constants.Outcome;
 import mage.constants.Zone;
@@ -13,6 +11,7 @@ import mage.game.Game;
 import mage.game.events.GameEvent;
 import mage.game.events.GameEvent.EventType;
 import mage.players.Player;
+import mage.util.CardUtil;
 
 /**
  * If you would draw a card, instead you may put exactly X cards from the top of
@@ -44,9 +43,9 @@ class DredgeEffect extends ReplacementEffectImpl {
     public DredgeEffect(int value) {
         super(Duration.WhileInGraveyard, Outcome.AIDontUseIt);
         this.amount = value;
-        this.staticText = ("Dredge ") + Integer.toString(value) + " <i>(If you would draw a card, instead you may put exactly "
-                + value + " card(s) from the top of your library into your graveyard. If you do, return this card from "
-                + "your graveyard to your hand. Otherwise, draw a card.)</i>";
+        this.staticText = "Dredge " + value + " <i>(If you would draw a card, you may mill "
+                + CardUtil.numberToText(value, "a") + (value > 1 ? " cards" : " card")
+                + " instead. If you do, return this card from your graveyard to your hand.)</i>";
     }
 
     public DredgeEffect(final DredgeEffect effect) {
@@ -74,13 +73,11 @@ class DredgeEffect extends ReplacementEffectImpl {
         if (owner != null
                 && owner.getLibrary().size() >= amount
                 && owner.chooseUse(outcome, new StringBuilder("Dredge ").append(sourceCard.getLogName()).
-                        append("? (").append(amount).append(" cards go from top of library to graveyard)").toString(), source, game)) {
+                append("? (").append(amount).append(" cards go from top of library to graveyard)").toString(), source, game)) {
             if (!game.isSimulation()) {
                 game.informPlayers(new StringBuilder(owner.getLogName()).append(" dredges ").append(sourceCard.getLogName()).toString());
             }
-            Cards cardsToGrave = new CardsImpl();
-            cardsToGrave.addAll(owner.getLibrary().getTopCards(game, amount));
-            owner.moveCards(cardsToGrave, Zone.GRAVEYARD, source, game);
+            owner.millCards(amount, source, game);
             owner.moveCards(sourceCard, Zone.HAND, source, game);
             return true;
         }
diff --git a/Mage/src/main/java/mage/players/Player.java b/Mage/src/main/java/mage/players/Player.java
index cbf313bc28..e01ab4f6ce 100644
--- a/Mage/src/main/java/mage/players/Player.java
+++ b/Mage/src/main/java/mage/players/Player.java
@@ -531,10 +531,10 @@ public interface Player extends MageItem, Copyable<Player> {
      * @param cards    - list of cards that have to be moved
      * @param game     - game
      * @param anyOrder - true = if player can determine the order of the cards
-     *                     else false = random order
-     *                  401.4. If an effect puts two or more cards in a specific position in a library
-     *                  at the same time, the owner of those cards may arrange them in any order. 
-     *                  That library’s owner doesn’t reveal the order in which the cards go into the library.
+     *                 else false = random order
+     *                 401.4. If an effect puts two or more cards in a specific position in a library
+     *                 at the same time, the owner of those cards may arrange them in any order.
+     *                 That library’s owner doesn’t reveal the order in which the cards go into the library.
      * @param source   - source ability
      * @return
      */
@@ -563,13 +563,13 @@ public interface Player extends MageItem, Copyable<Player> {
      * @return
      */
     boolean putCardsOnTopOfLibrary(Cards cards, Game game, Ability source, boolean anyOrder);
-    
+
     boolean putCardsOnTopOfLibrary(Card card, Game game, Ability source, boolean anyOrder);
 
     boolean shuffleCardsToLibrary(Cards cards, Game game, Ability source);
-    
+
     boolean shuffleCardsToLibrary(Card card, Game game, Ability source);
-    
+
     // set the value for X mana spells and abilities
     default int announceXMana(int min, int max, String message, Game game, Ability ability) {
         return announceXMana(min, max, 1, message, game, ability);
@@ -840,6 +840,8 @@ public interface Player extends MageItem, Copyable<Player> {
      */
     boolean moveCardToCommandWithInfo(Card card, UUID sourceId, Game game, Zone fromZone);
 
+    Cards millCards(int toMill, Ability source, Game game);
+
     /**
      * Checks if the playerToCheckId is from an opponent in range
      *
diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java
index 55125a4602..696e98884e 100644
--- a/Mage/src/main/java/mage/players/PlayerImpl.java
+++ b/Mage/src/main/java/mage/players/PlayerImpl.java
@@ -4193,6 +4193,17 @@ public abstract class PlayerImpl implements Player, Serializable {
         return result;
     }
 
+    @Override
+    public Cards millCards(int toMill, Ability source, Game game) {
+        GameEvent event = GameEvent.getEvent(EventType.MILL_CARDS, getId(), source.getSourceId(), getId(), toMill);
+        if (game.replaceEvent(event)) {
+            return new CardsImpl();
+        }
+        Cards cards = new CardsImpl(this.getLibrary().getTopCards(game, event.getAmount()));
+        this.moveCards(cards, Zone.GRAVEYARD, source, game);
+        return cards;
+    }
+
     @Override
     public boolean hasOpponent(UUID playerToCheckId, Game game) {
         return !this.getId().equals(playerToCheckId)

From 40036271da302b4ae5b94fc2e4a281edf00f14df Mon Sep 17 00:00:00 2001
From: 18ths <13023067+18ths@users.noreply.github.com>
Date: Wed, 24 Jun 2020 17:17:32 +0200
Subject: [PATCH 492/586] Implemented Allosaurus Shepherd and Blessed Sanctuary
 (#6711)

* added allosaurus shepherd and blessed sanctuary

* fixed nonascii apostrophes

* added continuous effect dependency
---
 .../src/main/resources/card-pictures-tok.txt  |  5 +-
 .../src/mage/cards/a/AllosaurusShepherd.java  | 68 +++++++++++++++++++
 .../src/mage/cards/b/BlessedSanctuary.java    | 49 +++++++++++++
 .../src/mage/cards/d/DismissIntoDream.java    | 47 +++++++------
 Mage.Sets/src/mage/sets/Jumpstart.java        |  2 +
 .../PreventAllNonCombatDamageToAllEffect.java |  6 +-
 .../CreaturesBecomeOtherTypeEffect.java       | 67 ++++++++++++++++++
 .../SetPowerToughnessAllEffect.java           |  4 ++
 .../src/main/java/mage/constants/SubType.java | 13 ++++
 .../game/permanent/token/UnicornToken.java    | 27 ++++++++
 10 files changed, 264 insertions(+), 24 deletions(-)
 create mode 100644 Mage.Sets/src/mage/cards/a/AllosaurusShepherd.java
 create mode 100644 Mage.Sets/src/mage/cards/b/BlessedSanctuary.java
 create mode 100644 Mage/src/main/java/mage/abilities/effects/common/continuous/CreaturesBecomeOtherTypeEffect.java
 create mode 100644 Mage/src/main/java/mage/game/permanent/token/UnicornToken.java

diff --git a/Mage.Client/src/main/resources/card-pictures-tok.txt b/Mage.Client/src/main/resources/card-pictures-tok.txt
index d259a41e0a..0609d4e9ea 100644
--- a/Mage.Client/src/main/resources/card-pictures-tok.txt
+++ b/Mage.Client/src/main/resources/card-pictures-tok.txt
@@ -1401,4 +1401,7 @@
 |Generate|TOK:C20|Soldier|||SoldierToken|
 |Generate|TOK:C20|Spirit|||SpiritWhiteToken|
 |Generate|TOK:C20|Treasure|||TreasureToken|
-|Generate|TOK:C20|Zombie|||ZombieToken|
\ No newline at end of file
+|Generate|TOK:C20|Zombie|||ZombieToken|
+
+# JMP
+|Generate|TOK:JMP|Unicorn|||UnicornToken|
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/a/AllosaurusShepherd.java b/Mage.Sets/src/mage/cards/a/AllosaurusShepherd.java
new file mode 100644
index 0000000000..a9ec318ff6
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/a/AllosaurusShepherd.java
@@ -0,0 +1,68 @@
+package mage.cards.a;
+
+import mage.MageInt;
+import mage.ObjectColor;
+import mage.abilities.Ability;
+import mage.abilities.common.CantBeCounteredAbility;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.effects.common.CantBeCounteredControlledEffect;
+import mage.abilities.effects.common.continuous.CreaturesBecomeOtherTypeEffect;
+import mage.abilities.effects.common.continuous.SetPowerToughnessAllEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.*;
+import mage.filter.FilterPermanent;
+import mage.filter.FilterSpell;
+import mage.filter.common.FilterControlledCreaturePermanent;
+import mage.filter.predicate.mageobject.ColorPredicate;
+
+import java.util.UUID;
+
+public class AllosaurusShepherd extends CardImpl {
+
+    private static final FilterSpell greenSpellsFilter = new FilterSpell("green spells");
+    private static final FilterPermanent elvesFilter = new FilterControlledCreaturePermanent("each Elf creature you control");
+
+    static {
+        greenSpellsFilter.add(new ColorPredicate(ObjectColor.GREEN));
+        elvesFilter.add(SubType.ELF.getPredicate());
+    }
+
+    public AllosaurusShepherd(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{G}");
+        this.subtype.add(SubType.ELF);
+        this.subtype.add(SubType.SHAMAN);
+
+        this.power = new MageInt(1);
+        this.toughness = new MageInt(1);
+
+        //Allosaurus Shepherd can't be countered.
+        this.addAbility(new CantBeCounteredAbility());
+
+        //Green spells you control can't be countered.
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
+                new CantBeCounteredControlledEffect(greenSpellsFilter, null, Duration.WhileOnBattlefield)));
+
+        //4GG: Until end of turn, each Elf creature you control has base power and toughness 5/5 and becomes a Dinosaur in addition to its other creature types.
+        Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD,
+                new SetPowerToughnessAllEffect(5, 5, Duration.EndOfTurn, elvesFilter, true)
+                        .setText("Until end of turn, each Elf creature you control has base power and toughness 5/5"),
+                new ManaCostsImpl("{4}{G}{G}"));
+        ability.addEffect(new CreaturesBecomeOtherTypeEffect(elvesFilter, SubType.DINOSAUR, Duration.EndOfTurn)
+                .setText("and becomes a Dinosaur in addition to its other creature types"));
+        this.addAbility(ability);
+
+    }
+
+    public AllosaurusShepherd(final AllosaurusShepherd card) {
+        super(card);
+    }
+
+    @Override
+    public AllosaurusShepherd copy() {
+        return new AllosaurusShepherd(this);
+    }
+
+}
diff --git a/Mage.Sets/src/mage/cards/b/BlessedSanctuary.java b/Mage.Sets/src/mage/cards/b/BlessedSanctuary.java
new file mode 100644
index 0000000000..8b0099dc4d
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/b/BlessedSanctuary.java
@@ -0,0 +1,49 @@
+package mage.cards.b;
+
+import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.effects.common.CreateTokenEffect;
+import mage.abilities.effects.common.PreventAllNonCombatDamageToAllEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.Zone;
+import mage.filter.FilterPermanent;
+import mage.filter.common.FilterControlledCreaturePermanent;
+import mage.filter.predicate.Predicates;
+import mage.filter.predicate.permanent.TokenPredicate;
+import mage.game.permanent.token.UnicornToken;
+
+import java.util.UUID;
+
+public class BlessedSanctuary extends CardImpl {
+
+    private static final FilterPermanent filterYourCreatures = new FilterControlledCreaturePermanent("creatures you control");
+    private static final FilterControlledCreaturePermanent filterNontoken = new FilterControlledCreaturePermanent("nontoken creature");
+
+    static {
+        filterNontoken.add(Predicates.not(TokenPredicate.instance));
+    }
+
+    public BlessedSanctuary(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{W}{W}");
+
+        //Prevent all noncombat damage that would be dealt to you and creatures you control.
+        this.addAbility(new SimpleStaticAbility(new PreventAllNonCombatDamageToAllEffect(
+                Duration.WhileOnBattlefield, filterYourCreatures, true)));
+
+        //Whenever a nontoken creature enters the battlefield under your control, create a 2/2 white Unicorn creature token.
+        this.addAbility(new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD,
+                new CreateTokenEffect(new UnicornToken()), filterNontoken, false));
+    }
+
+    public BlessedSanctuary(final BlessedSanctuary card) {
+        super(card);
+    }
+
+    @Override
+    public BlessedSanctuary copy() {
+        return new BlessedSanctuary(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/cards/d/DismissIntoDream.java b/Mage.Sets/src/mage/cards/d/DismissIntoDream.java
index 16a2addbe1..2c2a2fcd09 100644
--- a/Mage.Sets/src/mage/cards/d/DismissIntoDream.java
+++ b/Mage.Sets/src/mage/cards/d/DismissIntoDream.java
@@ -3,13 +3,15 @@ package mage.cards.d;
 
 import java.util.UUID;
 import mage.abilities.Ability;
+import mage.abilities.Mode;
 import mage.abilities.common.BecomesTargetTriggeredAbility;
 import mage.abilities.common.SimpleStaticAbility;
-import mage.abilities.effects.ContinuousEffectImpl;
 import mage.abilities.effects.common.SacrificeSourceEffect;
+import mage.abilities.effects.common.continuous.CreaturesBecomeOtherTypeEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.*;
+import mage.filter.FilterPermanent;
 import mage.filter.common.FilterCreaturePermanent;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
@@ -20,13 +22,18 @@ import mage.game.permanent.Permanent;
  */
 public final class DismissIntoDream extends CardImpl {
 
+    private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Each creature your opponents control");
+    static {
+        filter.add(TargetController.OPPONENT.getControllerPredicate());
+    }
+
     public DismissIntoDream(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{6}{U}");
 
 
         // Each creature your opponents control is an Illusion in addition to its other types 
         // and has "When this creature becomes the target of a spell or ability, sacrifice it."
-        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DismissIntoDreamEffect()));
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DismissIntoDreamEffect(filter)));
     }
 
     public DismissIntoDream(final DismissIntoDream card) {
@@ -39,16 +46,11 @@ public final class DismissIntoDream extends CardImpl {
     }
 }
 
-class DismissIntoDreamEffect extends ContinuousEffectImpl {
+class DismissIntoDreamEffect extends CreaturesBecomeOtherTypeEffect {
 
-    private static final FilterCreaturePermanent filter = new FilterCreaturePermanent();
-    static {
-        filter.add(TargetController.OPPONENT.getControllerPredicate());
-    }
-
-    DismissIntoDreamEffect() {
-        super(Duration.WhileOnBattlefield, Outcome.Detriment);
-        this.staticText = "Each creature your opponents control is an Illusion in addition to its other types and has \"When this creature becomes the target of a spell or ability, sacrifice it.\"";
+    DismissIntoDreamEffect(FilterPermanent filter) {
+        super(filter, SubType.ILLUSION, Duration.WhileOnBattlefield);
+        this.outcome = Outcome.Detriment;
     }
 
     DismissIntoDreamEffect(final DismissIntoDreamEffect effect) {
@@ -67,23 +69,24 @@ class DismissIntoDreamEffect extends ContinuousEffectImpl {
 
     @Override
     public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
-        for (Permanent object: game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game)) {
-            switch (layer) {
-                case AbilityAddingRemovingEffects_6:
-                    object.addAbility(new BecomesTargetTriggeredAbility(new SacrificeSourceEffect()), source.getSourceId(), game);
-                    break;
-                case TypeChangingEffects_4:
-                    if (!object.hasSubtype(SubType.ILLUSION, game)) {
-                        object.getSubtype(game).add(SubType.ILLUSION);
-                    }
-                    break;
+        super.apply(layer, sublayer, source, game);
+
+        if (layer == Layer.AbilityAddingRemovingEffects_6) {
+            for (Permanent object: game.getBattlefield().getActivePermanents(this.filter, source.getControllerId(), game)) {
+                object.addAbility(new BecomesTargetTriggeredAbility(new SacrificeSourceEffect()), source.getSourceId(), game);
             }
         }
+
         return true;
     }
 
     @Override
     public boolean hasLayer(Layer layer) {
-        return layer == Layer.AbilityAddingRemovingEffects_6 || layer == Layer.TypeChangingEffects_4;
+        return super.hasLayer(layer) || layer == Layer.AbilityAddingRemovingEffects_6;
+    }
+
+    @Override
+    public String getText(Mode mode) {
+        return super.getText(mode) + " and has \"When this creature becomes the target of a spell or ability, sacrifice it.\"";
     }
 }
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index 6fc7e0aa64..5b61d404cd 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -31,6 +31,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Agonizing Syphon", 199, Rarity.COMMON, mage.cards.a.AgonizingSyphon.class));
         cards.add(new SetCardInfo("Ajani's Chosen", 82, Rarity.RARE, mage.cards.a.AjanisChosen.class));
         cards.add(new SetCardInfo("Alabaster Mage", 83, Rarity.UNCOMMON, mage.cards.a.AlabasterMage.class));
+        cards.add(new SetCardInfo("Allosaurus Shepherd", 28, Rarity.MYTHIC, mage.cards.a.AllosaurusShepherd.class));
         cards.add(new SetCardInfo("Alloy Myr", 457, Rarity.COMMON, mage.cards.a.AlloyMyr.class));
         cards.add(new SetCardInfo("Ambassador Oak", 375, Rarity.COMMON, mage.cards.a.AmbassadorOak.class));
         cards.add(new SetCardInfo("Ancestral Statue", 458, Rarity.COMMON, mage.cards.a.AncestralStatue.class));
@@ -63,6 +64,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Black Cat", 203, Rarity.COMMON, mage.cards.b.BlackCat.class));
         cards.add(new SetCardInfo("Black Market", 204, Rarity.RARE, mage.cards.b.BlackMarket.class));
         cards.add(new SetCardInfo("Blessed Spirits", 92, Rarity.UNCOMMON, mage.cards.b.BlessedSpirits.class));
+        cards.add(new SetCardInfo("Blessed Sanctuary", 1, Rarity.RARE, mage.cards.b.BlessedSanctuary.class));
         cards.add(new SetCardInfo("Blighted Bat", 205, Rarity.COMMON, mage.cards.b.BlightedBat.class));
         cards.add(new SetCardInfo("Blindblast", 295, Rarity.COMMON, mage.cards.b.Blindblast.class));
         cards.add(new SetCardInfo("Blood Artist", 206, Rarity.UNCOMMON, mage.cards.b.BloodArtist.class));
diff --git a/Mage/src/main/java/mage/abilities/effects/common/PreventAllNonCombatDamageToAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PreventAllNonCombatDamageToAllEffect.java
index 51cbe53711..49aee45b7f 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/PreventAllNonCombatDamageToAllEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/PreventAllNonCombatDamageToAllEffect.java
@@ -27,7 +27,11 @@ public class PreventAllNonCombatDamageToAllEffect extends PreventionEffectImpl {
         super(duration, Integer.MAX_VALUE, false);
         this.filter = filter;
         this.andToYou = andToYou;
-        staticText = "Prevent all non combat damage that would be dealt to " + (andToYou ? "you and " : "") + filter.getMessage() + ' ' + duration.toString();
+        staticText = "Prevent all non combat damage that would be dealt to " + (andToYou ? "you and " : "") + filter.getMessage();
+
+        if (duration != Duration.WhileOnBattlefield) {
+            staticText += ' ' + duration.toString();
+        }
     }
 
     private PreventAllNonCombatDamageToAllEffect(final PreventAllNonCombatDamageToAllEffect effect) {
diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/CreaturesBecomeOtherTypeEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/CreaturesBecomeOtherTypeEffect.java
new file mode 100644
index 0000000000..e2513cac56
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/CreaturesBecomeOtherTypeEffect.java
@@ -0,0 +1,67 @@
+package mage.abilities.effects.common.continuous;
+
+import mage.abilities.Ability;
+import mage.abilities.Mode;
+import mage.abilities.effects.ContinuousEffectImpl;
+import mage.constants.*;
+import mage.filter.FilterPermanent;
+import mage.game.Game;
+import mage.game.permanent.Permanent;
+
+public class CreaturesBecomeOtherTypeEffect extends ContinuousEffectImpl {
+
+    protected final FilterPermanent filter;
+    private final SubType subType;
+
+    public CreaturesBecomeOtherTypeEffect(FilterPermanent filter, SubType subType, Duration duration) {
+        super(duration, Outcome.Neutral);
+        this.filter = filter;
+        this.subType = subType;
+
+        this.dependendToTypes.add(DependencyType.BecomeCreature);  // Opalescence and Starfield of Nyx
+    }
+
+    protected CreaturesBecomeOtherTypeEffect(final CreaturesBecomeOtherTypeEffect effect) {
+        super(effect);
+        this.filter = effect.filter;
+        this.subType = effect.subType;
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        return false;
+    }
+
+    @Override
+    public CreaturesBecomeOtherTypeEffect copy() {
+        return new CreaturesBecomeOtherTypeEffect(this);
+    }
+
+    @Override
+    public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
+        if (layer == Layer.TypeChangingEffects_4) {
+            for (Permanent object: game.getBattlefield().getActivePermanents(this.filter, source.getControllerId(), game)) {
+                if (!object.hasSubtype(this.subType, game)) {
+                    object.getSubtype(game).add(this.subType);
+                }
+            }
+        }
+
+        return true;
+    }
+
+    @Override
+    public boolean hasLayer(Layer layer) {
+        return layer == Layer.TypeChangingEffects_4;
+    }
+
+    @Override
+    public String getText(Mode mode) {
+        if (staticText != null && !staticText.isEmpty()) {
+            return staticText;
+        }
+
+        return this.filter.getMessage() + " is " + this.subType.getIndefiniteArticle()
+                + " " + this.subType.toString() + " in addition to its other types";
+    }
+}
\ No newline at end of file
diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/SetPowerToughnessAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/SetPowerToughnessAllEffect.java
index edb7b8e459..ac2b5127a9 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/continuous/SetPowerToughnessAllEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/SetPowerToughnessAllEffect.java
@@ -98,6 +98,10 @@ public class SetPowerToughnessAllEffect extends ContinuousEffectImpl {
 
     @Override
     public String getText(Mode mode) {
+        if (staticText != null && !staticText.isEmpty()) {
+            return staticText;
+        }
+
         StringBuilder sb = new StringBuilder();
         sb.append(filter.getMessage());
         if (filter.getMessage().toLowerCase(Locale.ENGLISH).startsWith("Each ")) {
diff --git a/Mage/src/main/java/mage/constants/SubType.java b/Mage/src/main/java/mage/constants/SubType.java
index 7724b09214..8fe70263a2 100644
--- a/Mage/src/main/java/mage/constants/SubType.java
+++ b/Mage/src/main/java/mage/constants/SubType.java
@@ -496,6 +496,19 @@ public enum SubType {
         return predicate;
     }
 
+
+    public String getIndefiniteArticle() {
+        if (isVowel(description.charAt(0))) {
+            return "an";
+        } else {
+            return "a";
+        }
+    }
+
+    private boolean isVowel(char c) {
+        return "AEIOUaeiou".indexOf(c) != -1;
+    }
+
     public static SubType fromString(String value) {
         for (SubType st : SubType.values()) {
             if (st.toString().equals(value)) {
diff --git a/Mage/src/main/java/mage/game/permanent/token/UnicornToken.java b/Mage/src/main/java/mage/game/permanent/token/UnicornToken.java
new file mode 100644
index 0000000000..4d118d3df8
--- /dev/null
+++ b/Mage/src/main/java/mage/game/permanent/token/UnicornToken.java
@@ -0,0 +1,27 @@
+package mage.game.permanent.token;
+
+import mage.MageInt;
+import mage.constants.CardType;
+import mage.constants.SubType;
+
+public final class UnicornToken extends TokenImpl {
+
+    public UnicornToken() {
+        super("Unicorn", "2/2 white Unicorn creature token");
+        setExpansionSetCodeForImage("JMP");
+        cardType.add(CardType.CREATURE);
+        subtype.add(SubType.UNICORN);
+        color.setWhite(true);
+        power = new MageInt(2);
+        toughness = new MageInt(2);
+    }
+
+    private UnicornToken(final UnicornToken token) {
+        super(token);
+    }
+
+    @Override
+    public UnicornToken copy() {
+        return new UnicornToken(this);
+    }
+}
\ No newline at end of file

From 72891a5badad74f614c9968efdbb842b589474ed Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Wed, 24 Jun 2020 21:11:49 +0400
Subject: [PATCH 493/586] Refactor: removed redundant temporary effects list
 (#6693, #6618)

---
 .../abilities/effects/ContinuousEffects.java  | 107 ++++--------------
 .../effects/ContinuousEffectsList.java        |  43 ++++---
 2 files changed, 49 insertions(+), 101 deletions(-)

diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java b/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java
index 516cee562b..6e85bc4e22 100644
--- a/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java
+++ b/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java
@@ -1,9 +1,5 @@
 package mage.abilities.effects;
 
-import java.io.Serializable;
-import java.util.*;
-import java.util.Map.Entry;
-import java.util.stream.Collectors;
 import mage.MageObject;
 import mage.MageObjectReference;
 import mage.abilities.Ability;
@@ -30,6 +26,11 @@ import mage.target.common.TargetCardInHand;
 import mage.util.CardUtil;
 import org.apache.log4j.Logger;
 
+import java.io.Serializable;
+import java.util.*;
+import java.util.Map.Entry;
+import java.util.stream.Collectors;
+
 /**
  * @author BetaSteward_at_googlemail.com
  */
@@ -51,27 +52,21 @@ public class ContinuousEffects implements Serializable {
     private ContinuousEffectsList<SpliceCardEffect> spliceCardEffects = new ContinuousEffectsList<>();
 
     private final Map<AsThoughEffectType, ContinuousEffectsList<AsThoughEffect>> asThoughEffectsMap = new EnumMap<>(AsThoughEffectType.class);
-    public final List<ContinuousEffectsList<?>> allEffectsLists = new ArrayList<>();
+    public final List<ContinuousEffectsList<?>> allEffectsLists = new ArrayList<>(); // contains refs to real effect's list
     private final ApplyCountersEffect applyCounters;
-    //    private final PlaneswalkerRedirectionEffect planeswalkerRedirectionEffect;
     private final AuraReplacementEffect auraReplacementEffect;
 
-    private final Map<String, List<ContinuousEffect>> lastEffectsListOnLayer = new HashMap<>(); // helps to find out new effect timestamps
-
-    // note all effect/abilities that were only added temporary
-    private final Map<ContinuousEffect, Set<Ability>> temporaryEffects = new HashMap<>();
+    private final Map<String, ContinuousEffectsList<ContinuousEffect>> lastEffectsListOnLayer = new HashMap<>(); // helps to find out new effect timestamps on layers
 
     public ContinuousEffects() {
         applyCounters = new ApplyCountersEffect();
-//        planeswalkerRedirectionEffect = new PlaneswalkerRedirectionEffect();
         auraReplacementEffect = new AuraReplacementEffect();
         collectAllEffects();
     }
 
     public ContinuousEffects(final ContinuousEffects effect) {
-        this.applyCounters = effect.applyCounters.copy();
-//        this.planeswalkerRedirectionEffect = effect.planeswalkerRedirectionEffect.copy();
-        this.auraReplacementEffect = effect.auraReplacementEffect.copy();
+        applyCounters = effect.applyCounters.copy();
+        auraReplacementEffect = effect.auraReplacementEffect.copy();
         layeredEffects = effect.layeredEffects.copy();
         continuousRuleModifyingEffects = effect.continuousRuleModifyingEffects.copy();
         replacementEffects = effect.replacementEffects.copy();
@@ -85,8 +80,8 @@ public class ContinuousEffects implements Serializable {
 
         costModificationEffects = effect.costModificationEffects.copy();
         spliceCardEffects = effect.spliceCardEffects.copy();
-        for (Map.Entry<ContinuousEffect, Set<Ability>> entry : effect.temporaryEffects.entrySet()) {
-            temporaryEffects.put(entry.getKey().copy(), entry.getValue());
+        for (Map.Entry<String, ContinuousEffectsList<ContinuousEffect>> entry : effect.lastEffectsListOnLayer.entrySet()) {
+            lastEffectsListOnLayer.put(entry.getKey(), entry.getValue().copy());
         }
         collectAllEffects();
         order = effect.order;
@@ -172,7 +167,7 @@ public class ContinuousEffects implements Serializable {
      *
      * @param game
      * @param timestampGroupName workaround to fix broken timestamps on effect's
-     * add/remove between different layers
+     *                           add/remove between different layers
      * @return effects list ordered by timestamp
      */
     public synchronized List<ContinuousEffect> getLayeredEffects(Game game, String timestampGroupName) {
@@ -217,7 +212,7 @@ public class ContinuousEffects implements Serializable {
      * "actual" meaning it becomes turned on that is defined by
      * Ability.#isInUseableZone(Game, boolean) method in
      * #getLayeredEffects(Game).
-     *
+     * <p>
      * It must be called with different timestamp group name (otherwise sort
      * order will be changed for add/remove effects, see Urborg and Bloodmoon
      * test)
@@ -226,9 +221,9 @@ public class ContinuousEffects implements Serializable {
      */
     private synchronized void updateTimestamps(String timestampGroupName, List<ContinuousEffect> layerEffects) {
         if (!lastEffectsListOnLayer.containsKey(timestampGroupName)) {
-            lastEffectsListOnLayer.put(timestampGroupName, new ArrayList<>());
+            lastEffectsListOnLayer.put(timestampGroupName, new ContinuousEffectsList<>());
         }
-        List<ContinuousEffect> prevs = lastEffectsListOnLayer.get(timestampGroupName);
+        ContinuousEffectsList<ContinuousEffect> prevs = lastEffectsListOnLayer.get(timestampGroupName);
         for (ContinuousEffect continuousEffect : layerEffects) {
             // check if it's new, then set order
             if (!prevs.contains(continuousEffect)) {
@@ -341,15 +336,12 @@ public class ContinuousEffects implements Serializable {
      */
     private Map<ReplacementEffect, Set<Ability>> getApplicableReplacementEffects(GameEvent event, Game game) {
         Map<ReplacementEffect, Set<Ability>> replaceEffects = new HashMap<>();
-//        if (planeswalkerRedirectionEffect.checksEventType(event, game) && planeswalkerRedirectionEffect.applies(event, null, game)) {
-//            replaceEffects.put(planeswalkerRedirectionEffect, null);
-//        }
         if (auraReplacementEffect.checksEventType(event, game) && auraReplacementEffect.applies(event, null, game)) {
             replaceEffects.put(auraReplacementEffect, null);
         }
         // boolean checkLKI = event.getType().equals(EventType.ZONE_CHANGE) || event.getType().equals(EventType.DESTROYED_PERMANENT);
         //get all applicable transient Replacement effects
-        for (Iterator<ReplacementEffect> iterator = replacementEffects.iterator(); iterator.hasNext();) {
+        for (Iterator<ReplacementEffect> iterator = replacementEffects.iterator(); iterator.hasNext(); ) {
             ReplacementEffect effect = iterator.next();
             if (!effect.checksEventType(event, game)) {
                 continue;
@@ -382,7 +374,7 @@ public class ContinuousEffects implements Serializable {
             }
         }
 
-        for (Iterator<PreventionEffect> iterator = preventionEffects.iterator(); iterator.hasNext();) {
+        for (Iterator<PreventionEffect> iterator = preventionEffects.iterator(); iterator.hasNext(); ) {
             PreventionEffect effect = iterator.next();
             if (!effect.checksEventType(event, game)) {
                 continue;
@@ -758,8 +750,8 @@ public class ContinuousEffects implements Serializable {
      * @param event
      * @param targetAbility ability the event is attached to. can be null.
      * @param game
-     * @param silentMode true if the event does not really happen but it's
-     * checked if the event would be replaced
+     * @param silentMode    true if the event does not really happen but it's
+     *                      checked if the event would be replaced
      * @return
      */
     public boolean preventedByRuleModification(GameEvent event, Ability targetAbility, Game game, boolean silentMode) {
@@ -807,7 +799,7 @@ public class ContinuousEffects implements Serializable {
         do {
             Map<ReplacementEffect, Set<Ability>> rEffects = getApplicableReplacementEffects(event, game);
             // Remove all consumed effects (ability dependant)
-            for (Iterator<ReplacementEffect> it1 = rEffects.keySet().iterator(); it1.hasNext();) {
+            for (Iterator<ReplacementEffect> it1 = rEffects.keySet().iterator(); it1.hasNext(); ) {
                 ReplacementEffect entry = it1.next();
                 if (consumed.containsKey(entry.getId()) /*&& !(entry instanceof CommanderReplacementEffect) */) { // 903.9.
                     Set<UUID> consumedAbilitiesIds = consumed.get(entry.getId());
@@ -992,7 +984,7 @@ public class ContinuousEffects implements Serializable {
                             .entrySet()
                             .stream()
                             .filter(entry -> dependentTo.contains(entry.getKey().getId())
-                            && entry.getValue().contains(effect.getId()))
+                                    && entry.getValue().contains(effect.getId()))
                             .forEach(entry -> {
                                 entry.getValue().remove(effect.getId());
                                 dependentTo.remove(entry.getKey().getId());
@@ -1026,7 +1018,7 @@ public class ContinuousEffects implements Serializable {
                     continue;
                 }
                 // check if waiting effects can be applied now
-                for (Iterator<Map.Entry<ContinuousEffect, Set<UUID>>> iterator = waitingEffects.entrySet().iterator(); iterator.hasNext();) {
+                for (Iterator<Map.Entry<ContinuousEffect, Set<UUID>>> iterator = waitingEffects.entrySet().iterator(); iterator.hasNext(); ) {
                     Map.Entry<ContinuousEffect, Set<UUID>> entry = iterator.next();
                     if (!appliedEffects.containsAll(entry.getValue())) { // all dependent to effects are applied now so apply the effect itself
                         continue;
@@ -1195,17 +1187,6 @@ public class ContinuousEffects implements Serializable {
     public synchronized void addEffect(ContinuousEffect effect, UUID sourceId, Ability source) {
         if (!(source instanceof MageSingleton)) { // because MageSingletons may never be removed by removing the temporary effecs they are not added to the temporaryEffects to prevent this
             effect.setTemporary(true);
-            Set<Ability> abilities = temporaryEffects.get(effect);
-            if (abilities == null) {
-                abilities = new HashSet<>();
-                temporaryEffects.put(effect, abilities);
-            } else if (abilities.contains(source)) {
-                // this ability (for the continuous effect) is already added
-                return;
-            }
-            abilities.add(source);
-
-            // add the effect itself
         }
         addEffect(effect, source);
     }
@@ -1293,51 +1274,12 @@ public class ContinuousEffects implements Serializable {
         for (ContinuousEffectsList effectsList : allEffectsLists) {
             effectsList.clear();
         }
-        temporaryEffects.clear();
     }
 
     public synchronized void removeAllTemporaryEffects() {
-        for (Map.Entry<ContinuousEffect, Set<Ability>> entry : temporaryEffects.entrySet()) {
-            switch (entry.getKey().getEffectType()) {
-                case REPLACEMENT:
-                case REDIRECTION:
-                    replacementEffects.removeEffects(entry.getKey().getId(), entry.getValue());
-                    break;
-                case PREVENTION:
-                    preventionEffects.removeEffects(entry.getKey().getId(), entry.getValue());
-                    break;
-                case RESTRICTION:
-                    restrictionEffects.removeEffects(entry.getKey().getId(), entry.getValue());
-                    break;
-                case RESTRICTION_UNTAP_NOT_MORE_THAN:
-                    restrictionUntapNotMoreThanEffects.removeEffects(entry.getKey().getId(), entry.getValue());
-                    break;
-                case REQUIREMENT:
-                    requirementEffects.removeEffects(entry.getKey().getId(), entry.getValue());
-                    break;
-                case ASTHOUGH:
-                    AsThoughEffect newAsThoughEffect = (AsThoughEffect) entry.getKey();
-                    if (!asThoughEffectsMap.containsKey(newAsThoughEffect.getAsThoughEffectType())) {
-                        break;
-                    }
-                    asThoughEffectsMap.get(newAsThoughEffect.getAsThoughEffectType()).removeEffects(entry.getKey().getId(), entry.getValue());
-                    break;
-                case COSTMODIFICATION:
-                    costModificationEffects.removeEffects(entry.getKey().getId(), entry.getValue());
-                    break;
-                case SPLICE:
-                    spliceCardEffects.removeEffects(entry.getKey().getId(), entry.getValue());
-                    break;
-                case CONTINUOUS_RULE_MODIFICATION:
-                    continuousRuleModifyingEffects.removeEffects(entry.getKey().getId(), entry.getValue());
-                    break;
-                default:
-                    layeredEffects.removeEffects(entry.getKey().getId(), entry.getValue());
-                    break;
-            }
-
+        for (ContinuousEffectsList effectsList : allEffectsLists) {
+            effectsList.removeTemporaryEffects();
         }
-        temporaryEffects.clear();
     }
 
     public Map<String, String> getReplacementEffectsTexts(Map<ReplacementEffect, Set<Ability>> rEffects, Game game) {
@@ -1354,7 +1296,6 @@ public class ContinuousEffects implements Serializable {
                 }
             } else {
                 if (!(entry.getKey() instanceof AuraReplacementEffect)) {
-//                        && !(entry.getKey() instanceof PlaneswalkerRedirectionEffect)) {
                     logger.error("Replacement effect without ability: " + entry.getKey().toString());
                 }
             }
diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousEffectsList.java b/Mage/src/main/java/mage/abilities/effects/ContinuousEffectsList.java
index 6dd37673e0..6353df838a 100644
--- a/Mage/src/main/java/mage/abilities/effects/ContinuousEffectsList.java
+++ b/Mage/src/main/java/mage/abilities/effects/ContinuousEffectsList.java
@@ -1,6 +1,5 @@
 package mage.abilities.effects;
 
-import java.util.*;
 import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.MageSingleton;
@@ -11,6 +10,8 @@ import mage.game.Game;
 import mage.players.Player;
 import org.apache.log4j.Logger;
 
+import java.util.*;
+
 /**
  * @param <T>
  * @author BetaSteward_at_googlemail.com
@@ -46,7 +47,7 @@ public class ContinuousEffectsList<T extends ContinuousEffect> extends ArrayList
     public void removeEndOfTurnEffects(Game game) {
         // calls every turn on cleanup step (only end of turn duration)
         // rules 514.2
-        for (Iterator<T> i = this.iterator(); i.hasNext();) {
+        for (Iterator<T> i = this.iterator(); i.hasNext(); ) {
             T entry = i.next();
             boolean canRemove = false;
             switch (entry.getDuration()) {
@@ -65,8 +66,7 @@ public class ContinuousEffectsList<T extends ContinuousEffect> extends ArrayList
     }
 
     public void removeEndOfCombatEffects() {
-
-        for (Iterator<T> i = this.iterator(); i.hasNext();) {
+        for (Iterator<T> i = this.iterator(); i.hasNext(); ) {
             T entry = i.next();
             if (entry.getDuration() == Duration.EndOfCombat) {
                 i.remove();
@@ -76,7 +76,7 @@ public class ContinuousEffectsList<T extends ContinuousEffect> extends ArrayList
     }
 
     public void removeInactiveEffects(Game game) {
-        for (Iterator<T> i = this.iterator(); i.hasNext();) {
+        for (Iterator<T> i = this.iterator(); i.hasNext(); ) {
             T entry = i.next();
             if (isInactive(entry, game)) {
                 i.remove();
@@ -207,20 +207,13 @@ public class ContinuousEffectsList<T extends ContinuousEffect> extends ArrayList
         return effectAbilityMap.getOrDefault(effectId, new HashSet<>());
     }
 
-    public void removeEffects(UUID effectIdToRemove, Set<Ability> abilitiesToRemove) {
-        Set<Ability> abilities = effectAbilityMap.get(effectIdToRemove);
-        if (abilitiesToRemove != null && abilities != null) {
-            abilities.removeIf(ability -> abilitiesToRemove.stream().anyMatch(a -> a.isSameInstance(ability)));
-        }
-        if (abilities == null || abilities.isEmpty()) {
-            for (Iterator<T> iterator = this.iterator(); iterator.hasNext();) {
-                ContinuousEffect effect = iterator.next();
-                if (effect.getId().equals(effectIdToRemove)) {
-                    iterator.remove();
-                    break;
-                }
+    public void removeTemporaryEffects() {
+        for (Iterator<T> i = this.iterator(); i.hasNext(); ) {
+            T entry = i.next();
+            if (entry.isTemporary()) {
+                i.remove();
+                effectAbilityMap.remove(entry.getId());
             }
-            effectAbilityMap.remove(effectIdToRemove);
         }
     }
 
@@ -229,4 +222,18 @@ public class ContinuousEffectsList<T extends ContinuousEffect> extends ArrayList
         super.clear();
         effectAbilityMap.clear();
     }
+
+    public boolean contains(Effect effect) {
+        // search by id
+        for (Iterator<T> iterator = this.iterator(); iterator.hasNext(); ) {
+            T test = iterator.next();
+            if (effect.getId().equals(test.getId())) {
+                return true;
+            }
+            if (effect.equals(test)) {
+                return true;
+            }
+        }
+        return false;
+    }
 }

From d664aaa7e11b0dfd80f24f7b90ded8f4a583c338 Mon Sep 17 00:00:00 2001
From: johnm <johnm@WINDOWS-QR5QIIL.lan>
Date: Wed, 24 Jun 2020 19:46:02 +0100
Subject: [PATCH 494/586] Alter UI for choosing a number for Void.

---
 Mage.Sets/src/mage/cards/v/Void.java | 21 +++++----------------
 1 file changed, 5 insertions(+), 16 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/v/Void.java b/Mage.Sets/src/mage/cards/v/Void.java
index 902f2a2b6f..71c053f17c 100644
--- a/Mage.Sets/src/mage/cards/v/Void.java
+++ b/Mage.Sets/src/mage/cards/v/Void.java
@@ -5,8 +5,6 @@ import mage.abilities.effects.OneShotEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.cards.CardsImpl;
-import mage.choices.Choice;
-import mage.choices.ChoiceImpl;
 import mage.constants.CardType;
 import mage.constants.ComparisonType;
 import mage.constants.Outcome;
@@ -18,8 +16,6 @@ import mage.game.permanent.Permanent;
 import mage.players.Player;
 import mage.target.TargetPlayer;
 
-import java.util.HashSet;
-import java.util.Set;
 import java.util.UUID;
 
 /**
@@ -46,7 +42,7 @@ public final class Void extends CardImpl {
 }
 
 class VoidEffect extends OneShotEffect {
-
+    
     VoidEffect() {
         super(Outcome.DestroyPermanent);
         this.staticText = "Choose a number. Destroy all artifacts and creatures with converted mana cost equal to that number. Then target player reveals their hand and discards all nonland cards with converted mana cost equal to the number";
@@ -67,17 +63,10 @@ class VoidEffect extends OneShotEffect {
         if (controller == null) {
             return false;
         }
-        Choice numberChoice = new ChoiceImpl();
-        Set<String> numbers = new HashSet<>(16);
-        for (int i = 0; i <= 15; i++) {
-            numbers.add(Integer.toString(i));
-        }
-        numberChoice.setChoices(numbers);
-        numberChoice.setMessage("Choose a number");
-        if (!controller.choose(Outcome.DestroyPermanent, numberChoice, game)) {
-            return false;
-        }
-        int number = Integer.parseInt(numberChoice.getChoice());
+        
+        int number = controller.announceXMana(0, Integer.MAX_VALUE, this.staticText, game, source);
+        game.informPlayers(controller.getLogName() + " chooses " + number + '.');
+     
         for (Permanent permanent : game.getBattlefield().getActivePermanents(source.getControllerId(), game)) {
             if ((permanent.isArtifact() || permanent.isCreature())
                     && permanent.getConvertedManaCost() == number) {

From e33505ff7d1eb626211281718d9d9d6d874880d9 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Wed, 24 Jun 2020 22:20:55 +0200
Subject: [PATCH 495/586] * Agyrem Plane - Fixed that the effect was not
 applied to creatures of all players (fixes #6486).

---
 .../java/mage/game/command/planes/AgyremPlane.java    | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/Mage/src/main/java/mage/game/command/planes/AgyremPlane.java b/Mage/src/main/java/mage/game/command/planes/AgyremPlane.java
index 049c933134..319842dae7 100644
--- a/Mage/src/main/java/mage/game/command/planes/AgyremPlane.java
+++ b/Mage/src/main/java/mage/game/command/planes/AgyremPlane.java
@@ -16,7 +16,6 @@ import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetE
 import mage.abilities.effects.common.RollPlanarDieEffect;
 import mage.cards.Card;
 import mage.constants.*;
-import mage.filter.common.FilterControlledCreaturePermanent;
 import mage.filter.predicate.Predicates;
 import mage.filter.predicate.mageobject.ColorPredicate;
 import mage.game.Game;
@@ -29,14 +28,15 @@ import mage.watchers.common.PlanarRollWatcher;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
+import mage.filter.common.FilterCreaturePermanent;
 
 /**
  * @author spjspj
  */
 public class AgyremPlane extends Plane {
 
-    private static final FilterControlledCreaturePermanent filterWhite = new FilterControlledCreaturePermanent("a white creature");
-    private static final FilterControlledCreaturePermanent filterNonWhite = new FilterControlledCreaturePermanent("a nonwhite creature");
+    private static final FilterCreaturePermanent filterWhite = new FilterCreaturePermanent("a white creature");
+    private static final FilterCreaturePermanent filterNonWhite = new FilterCreaturePermanent("a nonwhite creature");
 
     static {
         filterWhite.add(new ColorPredicate(ObjectColor.WHITE));
@@ -50,6 +50,7 @@ public class AgyremPlane extends Plane {
         // Whenever a white creature dies, return it to the battlefield under its owner's control at the beginning of the next end step
         DiesCreatureTriggeredAbility ability = new DiesCreatureTriggeredAbility(Zone.COMMAND, new AgyremEffect(), false, filterWhite, true);
         this.getAbilities().add(ability);
+        // Whenever a nonwhite creature dies, return it to its owner's hand at the beginning of the next end step.
         DiesCreatureTriggeredAbility ability2 = new DiesCreatureTriggeredAbility(Zone.COMMAND, new AgyremEffect2(), false, filterNonWhite, true);
         this.getAbilities().add(ability2);
 
@@ -57,9 +58,9 @@ public class AgyremPlane extends Plane {
         Effect chaosEffect = new AgyremRestrictionEffect();
         Target chaosTarget = null;
 
-        List<Effect> chaosEffects = new ArrayList<Effect>();
+        List<Effect> chaosEffects = new ArrayList<>();
         chaosEffects.add(chaosEffect);
-        List<Target> chaosTargets = new ArrayList<Target>();
+        List<Target> chaosTargets = new ArrayList<>();
         chaosTargets.add(chaosTarget);
 
         ActivateIfConditionActivatedAbility chaosAbility = new ActivateIfConditionActivatedAbility(Zone.COMMAND, new RollPlanarDieEffect(chaosEffects, chaosTargets), new GenericManaCost(0), MainPhaseStackEmptyCondition.instance);

From 6dfacfbca5eff906072cfee7a0739c52b0b498ed Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Thu, 25 Jun 2020 00:37:13 +0400
Subject: [PATCH 496/586] * Elspeth, Undaunted Hero - fixed wrong text;

---
 .../src/mage/cards/e/ElspethUndauntedHero.java   |  2 +-
 .../SearchLibraryGraveyardPutInHandEffect.java   |  3 ++-
 ...LibraryGraveyardPutOntoBattlefieldEffect.java | 16 ++++++----------
 3 files changed, 9 insertions(+), 12 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/e/ElspethUndauntedHero.java b/Mage.Sets/src/mage/cards/e/ElspethUndauntedHero.java
index 445548bb21..d04129b353 100644
--- a/Mage.Sets/src/mage/cards/e/ElspethUndauntedHero.java
+++ b/Mage.Sets/src/mage/cards/e/ElspethUndauntedHero.java
@@ -28,7 +28,7 @@ import java.util.UUID;
  */
 public final class ElspethUndauntedHero extends CardImpl {
 
-    private static final FilterCard filter = new FilterCard("Sunlit Hoplite");
+    private static final FilterCard filter = new FilterCard("card named Sunlit Hoplite");
 
     static {
         filter.add(new NamePredicate("Sunlit Hoplite"));
diff --git a/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardPutInHandEffect.java b/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardPutInHandEffect.java
index 7af7131b28..15042461cb 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardPutInHandEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardPutInHandEffect.java
@@ -12,6 +12,7 @@ import mage.game.Game;
 import mage.players.Player;
 import mage.target.TargetCard;
 import mage.target.common.TargetCardInLibrary;
+import mage.target.common.TargetCardInYourGraveyard;
 
 /**
  * @author Styxo
@@ -67,7 +68,7 @@ public class SearchLibraryGraveyardPutInHandEffect extends OneShotEffect {
             }
 
             if (cardFound == null && controller.chooseUse(outcome, "Search your graveyard for a card named " + filter.getMessage() + '?', source, game)) {
-                TargetCard target = new TargetCard(0, 1, Zone.GRAVEYARD, filter);
+                TargetCard target = new TargetCardInYourGraveyard(0, 1, filter, true);
                 target.clearChosen();
                 if (controller.choose(outcome, controller.getGraveyard(), target, game)) {
                     if (!target.getTargets().isEmpty()) {
diff --git a/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardPutOntoBattlefieldEffect.java b/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardPutOntoBattlefieldEffect.java
index 627244f47b..726eec7a1e 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardPutOntoBattlefieldEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardPutOntoBattlefieldEffect.java
@@ -4,7 +4,6 @@ import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
 import mage.cards.Card;
-import mage.cards.CardsImpl;
 import mage.constants.Outcome;
 import mage.constants.Zone;
 import mage.filter.FilterCard;
@@ -12,9 +11,9 @@ import mage.game.Game;
 import mage.players.Player;
 import mage.target.TargetCard;
 import mage.target.common.TargetCardInLibrary;
+import mage.target.common.TargetCardInYourGraveyard;
 
 /**
- *
  * @author Styxo
  */
 public class SearchLibraryGraveyardPutOntoBattlefieldEffect extends OneShotEffect {
@@ -34,15 +33,14 @@ public class SearchLibraryGraveyardPutOntoBattlefieldEffect extends OneShotEffec
         super(Outcome.Benefit);
         this.filter = filter;
         this.forceToSearchBoth = forceToSearchBoth;
-        staticText = (youMay ? "You may" : "") + " search your library and" + (forceToSearchBoth ? "" : "/or") + " graveyard for a card named " + filter.getMessage()
-                + ", reveal it, and put it into your hand. " + (forceToSearchBoth ? "Then shuffle your library" : "If you search your library this way, shuffle it");
+        staticText = (youMay ? "You may " : "") + "search your library and" + (forceToSearchBoth ? "" : "/or") + " graveyard for a " + filter.getMessage()
+                + " and put it onto the battlefield. " + (forceToSearchBoth ? "Then shuffle your library" : "If you search your library this way, shuffle it");
     }
 
     public SearchLibraryGraveyardPutOntoBattlefieldEffect(final SearchLibraryGraveyardPutOntoBattlefieldEffect effect) {
         super(effect);
         this.filter = effect.filter;
         this.forceToSearchBoth = effect.forceToSearchBoth;
-
     }
 
     @Override
@@ -56,7 +54,7 @@ public class SearchLibraryGraveyardPutOntoBattlefieldEffect extends OneShotEffec
         MageObject sourceObject = source.getSourceObject(game);
         Card cardFound = null;
         if (controller != null && sourceObject != null) {
-            if (forceToSearchBoth || controller.chooseUse(outcome, "Search your library for a card named " + filter.getMessage() + '?', source, game)) {
+            if (forceToSearchBoth || controller.chooseUse(outcome, "Search your library for a " + filter.getMessage() + '?', source, game)) {
                 TargetCardInLibrary target = new TargetCardInLibrary(0, 1, filter);
                 target.clearChosen();
                 if (controller.searchLibrary(target, source, game)) {
@@ -67,8 +65,8 @@ public class SearchLibraryGraveyardPutOntoBattlefieldEffect extends OneShotEffec
                 controller.shuffleLibrary(source, game);
             }
 
-            if (cardFound == null && controller.chooseUse(outcome, "Search your graveyard for a card named " + filter.getMessage() + '?', source, game)) {
-                TargetCard target = new TargetCard(0, 1, Zone.GRAVEYARD, filter);
+            if (cardFound == null && controller.chooseUse(outcome, "Search your graveyard for a " + filter.getMessage() + '?', source, game)) {
+                TargetCard target = new TargetCardInYourGraveyard(0, 1, filter, true);
                 target.clearChosen();
                 if (controller.choose(outcome, controller.getGraveyard(), target, game)) {
                     if (!target.getTargets().isEmpty()) {
@@ -78,7 +76,6 @@ public class SearchLibraryGraveyardPutOntoBattlefieldEffect extends OneShotEffec
             }
 
             if (cardFound != null) {
-                controller.revealCards(sourceObject.getIdName(), new CardsImpl(cardFound), game);
                 controller.moveCards(cardFound, Zone.BATTLEFIELD, source, game);
             }
 
@@ -87,5 +84,4 @@ public class SearchLibraryGraveyardPutOntoBattlefieldEffect extends OneShotEffec
 
         return false;
     }
-
 }

From d8bbfefde6cfaaeef612ca6076fe2c8c834fc353 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Thu, 25 Jun 2020 00:43:10 +0400
Subject: [PATCH 497/586] * Search library and/or graveyard and put in hand -
 fixed that it shuffle library before card move;

---
 .../search/SearchLibraryGraveyardPutInHandEffect.java      | 7 ++++++-
 .../SearchLibraryGraveyardPutOntoBattlefieldEffect.java    | 7 ++++++-
 2 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardPutInHandEffect.java b/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardPutInHandEffect.java
index 15042461cb..ce9a090df1 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardPutInHandEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardPutInHandEffect.java
@@ -55,6 +55,7 @@ public class SearchLibraryGraveyardPutInHandEffect extends OneShotEffect {
         Player controller = game.getPlayer(source.getControllerId());
         MageObject sourceObject = source.getSourceObject(game);
         Card cardFound = null;
+        boolean needShuffle = false;
         if (controller != null && sourceObject != null) {
             if (forceToSearchBoth || controller.chooseUse(outcome, "Search your library for a card named " + filter.getMessage() + '?', source, game)) {
                 TargetCardInLibrary target = new TargetCardInLibrary(0, 1, filter);
@@ -64,7 +65,7 @@ public class SearchLibraryGraveyardPutInHandEffect extends OneShotEffect {
                         cardFound = game.getCard(target.getFirstTarget());
                     }
                 }
-                controller.shuffleLibrary(source, game);
+                needShuffle = true;
             }
 
             if (cardFound == null && controller.chooseUse(outcome, "Search your graveyard for a card named " + filter.getMessage() + '?', source, game)) {
@@ -82,6 +83,10 @@ public class SearchLibraryGraveyardPutInHandEffect extends OneShotEffect {
                 controller.moveCards(cardFound, Zone.HAND, source, game);
             }
 
+            if (needShuffle) {
+                controller.shuffleLibrary(source, game);
+            }
+
             return true;
         }
 
diff --git a/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardPutOntoBattlefieldEffect.java b/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardPutOntoBattlefieldEffect.java
index 726eec7a1e..90fefbc79f 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardPutOntoBattlefieldEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardPutOntoBattlefieldEffect.java
@@ -53,6 +53,7 @@ public class SearchLibraryGraveyardPutOntoBattlefieldEffect extends OneShotEffec
         Player controller = game.getPlayer(source.getControllerId());
         MageObject sourceObject = source.getSourceObject(game);
         Card cardFound = null;
+        boolean needShuffle = false;
         if (controller != null && sourceObject != null) {
             if (forceToSearchBoth || controller.chooseUse(outcome, "Search your library for a " + filter.getMessage() + '?', source, game)) {
                 TargetCardInLibrary target = new TargetCardInLibrary(0, 1, filter);
@@ -62,7 +63,7 @@ public class SearchLibraryGraveyardPutOntoBattlefieldEffect extends OneShotEffec
                         cardFound = game.getCard(target.getFirstTarget());
                     }
                 }
-                controller.shuffleLibrary(source, game);
+                needShuffle = true;
             }
 
             if (cardFound == null && controller.chooseUse(outcome, "Search your graveyard for a " + filter.getMessage() + '?', source, game)) {
@@ -79,6 +80,10 @@ public class SearchLibraryGraveyardPutOntoBattlefieldEffect extends OneShotEffec
                 controller.moveCards(cardFound, Zone.BATTLEFIELD, source, game);
             }
 
+            if (needShuffle) {
+                controller.shuffleLibrary(source, game);
+            }
+
             return true;
         }
 

From d5616762cbe94001e7d652ffc8228e6b347fff42 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Wed, 24 Jun 2020 18:42:58 -0400
Subject: [PATCH 498/586] Implemented Lightning Phoenix

---
 .../src/mage/cards/l/LightningPhoenix.java    | 106 ++++++++++++++++++
 Mage.Sets/src/mage/sets/Jumpstart.java        |   1 +
 2 files changed, 107 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/l/LightningPhoenix.java

diff --git a/Mage.Sets/src/mage/cards/l/LightningPhoenix.java b/Mage.Sets/src/mage/cards/l/LightningPhoenix.java
new file mode 100644
index 0000000000..82649cc8c4
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/l/LightningPhoenix.java
@@ -0,0 +1,106 @@
+package mage.cards.l;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.BeginningOfEndStepTriggeredAbility;
+import mage.abilities.common.CantBlockAbility;
+import mage.abilities.condition.Condition;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
+import mage.abilities.effects.common.DoIfCostPaid;
+import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect;
+import mage.abilities.keyword.FlyingAbility;
+import mage.abilities.keyword.HasteAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.*;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+import mage.watchers.Watcher;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class LightningPhoenix extends CardImpl {
+
+    public LightningPhoenix(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}");
+
+        this.subtype.add(SubType.PHOENIX);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(2);
+
+        // Flying
+        this.addAbility(FlyingAbility.getInstance());
+
+        // Haste
+        this.addAbility(HasteAbility.getInstance());
+
+        // Lightning Phoenix can't block.
+        this.addAbility(new CantBlockAbility());
+
+        // At the beginning of your end step, if an opponent was dealt 3 or more damage this turn, you may pay {R}. If you do, return Lightning Phoenix from your graveyard to the battlefield.
+        this.addAbility(new ConditionalInterveningIfTriggeredAbility(
+                new BeginningOfEndStepTriggeredAbility(
+                        Zone.GRAVEYARD,
+                        new DoIfCostPaid(
+                                new ReturnSourceFromGraveyardToBattlefieldEffect(), new ManaCostsImpl<>("{R}")
+                        ), TargetController.YOU, null, false
+                ), LightningPhoenixCondition.instance, "At the beginning of your end step, " +
+                "if an opponent was dealt 3 or more damage this turn, you may pay {R}. " +
+                "If you do, return {this} from your graveyard to the battlefield."
+        ), new LightningPhoenixWatcher());
+    }
+
+    private LightningPhoenix(final LightningPhoenix card) {
+        super(card);
+    }
+
+    @Override
+    public LightningPhoenix copy() {
+        return new LightningPhoenix(this);
+    }
+}
+
+enum LightningPhoenixCondition implements Condition {
+    instance;
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        LightningPhoenixWatcher watcher = game.getState().getWatcher(LightningPhoenixWatcher.class);
+        return watcher != null && watcher.checkDamage(source.getControllerId());
+    }
+}
+
+class LightningPhoenixWatcher extends Watcher {
+
+    private final Map<UUID, Integer> damageMap = new HashMap<>();
+
+    LightningPhoenixWatcher() {
+        super(WatcherScope.GAME);
+    }
+
+    @Override
+    public void watch(GameEvent event, Game game) {
+        if (event.getType() != GameEvent.EventType.DAMAGED_PLAYER) {
+            return;
+        }
+        for (UUID playerId : game.getOpponents(event.getTargetId())) {
+            damageMap.compute(playerId, ((u, i) -> i == null ? event.getAmount() : Integer.sum(i, event.getAmount())));
+        }
+    }
+
+    @Override
+    public void reset() {
+        super.reset();
+        damageMap.clear();
+    }
+
+    boolean checkDamage(UUID playerId) {
+        return damageMap.getOrDefault(playerId, 0) >= 3;
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index 5b61d404cd..b45f4c159e 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -256,6 +256,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Lightning Bolt", 342, Rarity.UNCOMMON, mage.cards.l.LightningBolt.class));
         cards.add(new SetCardInfo("Lightning Diadem", 343, Rarity.COMMON, mage.cards.l.LightningDiadem.class));
         cards.add(new SetCardInfo("Lightning Elemental", 344, Rarity.COMMON, mage.cards.l.LightningElemental.class));
+        cards.add(new SetCardInfo("Lightning Phoenix", 21, Rarity.RARE, mage.cards.l.LightningPhoenix.class));
         cards.add(new SetCardInfo("Lightning Shrieker", 345, Rarity.COMMON, mage.cards.l.LightningShrieker.class));
         cards.add(new SetCardInfo("Lightning Visionary", 22, Rarity.COMMON, mage.cards.l.LightningVisionary.class));
         cards.add(new SetCardInfo("Lightning-Core Excavator", 32, Rarity.COMMON, mage.cards.l.LightningCoreExcavator.class));

From dcc1430b60a2cd11417f86c0545f556e0c14fcf9 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Wed, 24 Jun 2020 18:44:53 -0400
Subject: [PATCH 499/586] Implemented Muxus, Goblin Grandee

---
 .../src/mage/cards/m/MuxusGoblinGrandee.java  | 115 ++++++++++++++++++
 Mage.Sets/src/mage/sets/Jumpstart.java        |   1 +
 2 files changed, 116 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/m/MuxusGoblinGrandee.java

diff --git a/Mage.Sets/src/mage/cards/m/MuxusGoblinGrandee.java b/Mage.Sets/src/mage/cards/m/MuxusGoblinGrandee.java
new file mode 100644
index 0000000000..ca54830561
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/m/MuxusGoblinGrandee.java
@@ -0,0 +1,115 @@
+package mage.cards.m;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.AttacksTriggeredAbility;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.dynamicvalue.DynamicValue;
+import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.continuous.BoostSourceEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.cards.Cards;
+import mage.cards.CardsImpl;
+import mage.constants.*;
+import mage.filter.FilterPermanent;
+import mage.filter.common.FilterControlledPermanent;
+import mage.filter.predicate.permanent.AnotherPredicate;
+import mage.game.Game;
+import mage.players.Player;
+
+import java.util.Objects;
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class MuxusGoblinGrandee extends CardImpl {
+
+    private static final FilterPermanent filter = new FilterControlledPermanent(SubType.GOBLIN);
+
+    static {
+        filter.add(AnotherPredicate.instance);
+    }
+
+    private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter);
+
+    public MuxusGoblinGrandee(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}{R}");
+
+        this.addSuperType(SuperType.LEGENDARY);
+        this.subtype.add(SubType.GOBLIN);
+        this.subtype.add(SubType.NOBLE);
+        this.power = new MageInt(4);
+        this.toughness = new MageInt(4);
+
+        // When Muxus, Goblin Grandee enters the battlefield, reveal the top six cards of your library. Put all Goblin creature cards with converted mana cost 5 or less from among them onto the battlefield and the rest on the bottom of your library in a random order.
+        this.addAbility(new EntersBattlefieldTriggeredAbility(new MuxusGoblinGrandeeEffect()));
+
+        // Whenever Muxus attacks, it gets +1/+1 until end of turn for each other Goblin you control.
+        this.addAbility(new AttacksTriggeredAbility(new BoostSourceEffect(
+                xValue, xValue, Duration.EndOfTurn
+        ).setText("it gets +1/+1 until end of turn for each other Goblin you control"), false));
+    }
+
+    private MuxusGoblinGrandee(final MuxusGoblinGrandee card) {
+        super(card);
+    }
+
+    @Override
+    public MuxusGoblinGrandee copy() {
+        return new MuxusGoblinGrandee(this);
+    }
+}
+
+class MuxusGoblinGrandeeEffect extends OneShotEffect {
+
+    MuxusGoblinGrandeeEffect() {
+        super(Outcome.Benefit);
+        staticText = "reveal the top six cards of your library. " +
+                "Put all Goblin creature cards with converted mana cost 5 or less " +
+                "from among them onto the battlefield and the rest on the bottom of your library in a random order.";
+    }
+
+    private MuxusGoblinGrandeeEffect(final MuxusGoblinGrandeeEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public MuxusGoblinGrandeeEffect copy() {
+        return new MuxusGoblinGrandeeEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Player player = game.getPlayer(source.getControllerId());
+        if (player == null) {
+            return false;
+        }
+        Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, 6));
+        player.revealCards(source, cards, game);
+        Cards toBattlfield = new CardsImpl();
+        Cards toBottom = new CardsImpl();
+        cards.getCards(game)
+                .stream()
+                .filter(Objects::nonNull)
+                .forEach(card -> {
+                    if (card.isCreature()
+                            && card.hasSubtype(SubType.GOBLIN, game)
+                            && card.getConvertedManaCost() <= 5) {
+                        toBattlfield.add(card);
+                    } else {
+                        toBottom.add(card);
+                    }
+                });
+        player.moveCards(toBattlfield, Zone.BATTLEFIELD, source, game);
+        // need to account for effects like Grafdigger's Cage
+        toBattlfield
+                .stream()
+                .filter(uuid -> game.getState().getZone(uuid) == Zone.LIBRARY)
+                .forEach(toBottom::add);
+        player.putCardsOnBottomOfLibrary(toBottom, game, source, false);
+        return true;
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index b45f4c159e..b028b78056 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -292,6 +292,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Mountain", 62, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Mugging", 352, Rarity.COMMON, mage.cards.m.Mugging.class));
         cards.add(new SetCardInfo("Murmuring Phantasm", 157, Rarity.COMMON, mage.cards.m.MurmuringPhantasm.class));
+        cards.add(new SetCardInfo("Muxus, Goblin Grandee", 24, Rarity.RARE, mage.cards.m.MuxusGoblinGrandee.class));
         cards.add(new SetCardInfo("Myr Sire", 475, Rarity.COMMON, mage.cards.m.MyrSire.class));
         cards.add(new SetCardInfo("Mystic Archaeologist", 158, Rarity.RARE, mage.cards.m.MysticArchaeologist.class));
         cards.add(new SetCardInfo("Narcolepsy", 159, Rarity.COMMON, mage.cards.n.Narcolepsy.class));

From 9b2c8c5d874ca28decc11a8d1d8e27e3af560fa8 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Wed, 24 Jun 2020 18:46:24 -0400
Subject: [PATCH 500/586] updated JMP spoiler and reprints

---
 .../src/mage/cards/m/MaelstromArchangel.java  |  6 +--
 Mage.Sets/src/mage/cards/q/QuirionDryad.java  |  2 +-
 Mage.Sets/src/mage/cards/r/RunedHalo.java     |  2 +-
 Mage.Sets/src/mage/sets/Jumpstart.java        |  8 +--
 Utils/mtg-cards-data.txt                      | 50 +++++++++----------
 5 files changed, 34 insertions(+), 34 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/m/MaelstromArchangel.java b/Mage.Sets/src/mage/cards/m/MaelstromArchangel.java
index fe3106b78c..a02f430b5d 100644
--- a/Mage.Sets/src/mage/cards/m/MaelstromArchangel.java
+++ b/Mage.Sets/src/mage/cards/m/MaelstromArchangel.java
@@ -53,11 +53,11 @@ public final class MaelstromArchangel extends CardImpl {
 
 class MaelstromArchangelCastEffect extends OneShotEffect {
 
-    private static final FilterCard filter = new FilterNonlandCard("nonland card from your hand");
+    private static final FilterCard filter = new FilterNonlandCard("spell from your hand");
 
     public MaelstromArchangelCastEffect() {
         super(Outcome.PlayForFree);
-        this.staticText = "you may cast a nonland card from your hand without paying its mana cost";
+        this.staticText = "you may cast a spell from your hand without paying its mana cost";
     }
 
     public MaelstromArchangelCastEffect(final MaelstromArchangelCastEffect effect) {
@@ -75,7 +75,7 @@ class MaelstromArchangelCastEffect extends OneShotEffect {
         if (controller != null) {
             Target target = new TargetCardInHand(filter);
             if (target.canChoose(source.getSourceId(), controller.getId(), game)
-                    && controller.chooseUse(outcome, "Cast a nonland card from your hand without paying its mana cost?", source, game)) {
+                    && controller.chooseUse(outcome, "Cast a spell from your hand without paying its mana cost?", source, game)) {
                 Card cardToCast = null;
                 boolean cancel = false;
                 while (controller.canRespond() && !cancel) {
diff --git a/Mage.Sets/src/mage/cards/q/QuirionDryad.java b/Mage.Sets/src/mage/cards/q/QuirionDryad.java
index 4e2afae86e..e15c701108 100644
--- a/Mage.Sets/src/mage/cards/q/QuirionDryad.java
+++ b/Mage.Sets/src/mage/cards/q/QuirionDryad.java
@@ -21,7 +21,7 @@ import mage.filter.predicate.mageobject.ColorPredicate;
  */
 public final class QuirionDryad extends CardImpl {
 
-    private static final FilterSpell filter = new FilterSpell("white, blue, black, or red spell");
+    private static final FilterSpell filter = new FilterSpell("a spell that's white, blue, black, or red");
 
     static {
         filter.add(Predicates.or(
diff --git a/Mage.Sets/src/mage/cards/r/RunedHalo.java b/Mage.Sets/src/mage/cards/r/RunedHalo.java
index 3800320f37..50861691d1 100644
--- a/Mage.Sets/src/mage/cards/r/RunedHalo.java
+++ b/Mage.Sets/src/mage/cards/r/RunedHalo.java
@@ -57,7 +57,7 @@ class RunedHaloSetProtectionEffect extends OneShotEffect {
 
     public RunedHaloSetProtectionEffect() {
         super(Outcome.Protect);
-        staticText = "<br/><br/>You have protection from the chosen name  <i>(You can't be targeted, dealt damage, or enchanted by anything with that name.)</i>";
+        staticText = "<br/><br/>You have protection from the chosen card name  <i>(You can't be targeted, dealt damage, or enchanted by anything with that name.)</i>";
     }
 
     public RunedHaloSetProtectionEffect(final RunedHaloSetProtectionEffect effect) {
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index b028b78056..87de816a49 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -63,8 +63,8 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Belltower Sphinx", 141, Rarity.UNCOMMON, mage.cards.b.BelltowerSphinx.class));
         cards.add(new SetCardInfo("Black Cat", 203, Rarity.COMMON, mage.cards.b.BlackCat.class));
         cards.add(new SetCardInfo("Black Market", 204, Rarity.RARE, mage.cards.b.BlackMarket.class));
-        cards.add(new SetCardInfo("Blessed Spirits", 92, Rarity.UNCOMMON, mage.cards.b.BlessedSpirits.class));
         cards.add(new SetCardInfo("Blessed Sanctuary", 1, Rarity.RARE, mage.cards.b.BlessedSanctuary.class));
+        cards.add(new SetCardInfo("Blessed Spirits", 92, Rarity.UNCOMMON, mage.cards.b.BlessedSpirits.class));
         cards.add(new SetCardInfo("Blighted Bat", 205, Rarity.COMMON, mage.cards.b.BlightedBat.class));
         cards.add(new SetCardInfo("Blindblast", 295, Rarity.COMMON, mage.cards.b.Blindblast.class));
         cards.add(new SetCardInfo("Blood Artist", 206, Rarity.UNCOMMON, mage.cards.b.BloodArtist.class));
@@ -131,7 +131,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Divine Arrow", 100, Rarity.COMMON, mage.cards.d.DivineArrow.class));
         cards.add(new SetCardInfo("Doublecast", 307, Rarity.UNCOMMON, mage.cards.d.Doublecast.class));
         cards.add(new SetCardInfo("Douse in Gloom", 223, Rarity.COMMON, mage.cards.d.DouseInGloom.class));
-        cards.add(new SetCardInfo("Draconic Roar", 308, Rarity.COMMON, mage.cards.d.DraconicRoar.class));
+        cards.add(new SetCardInfo("Draconic Roar", 308, Rarity.UNCOMMON, mage.cards.d.DraconicRoar.class));
         cards.add(new SetCardInfo("Dragon Fodder", 309, Rarity.COMMON, mage.cards.d.DragonFodder.class));
         cards.add(new SetCardInfo("Dragon Hatchling", 310, Rarity.COMMON, mage.cards.d.DragonHatchling.class));
         cards.add(new SetCardInfo("Dragonloft Idol", 463, Rarity.UNCOMMON, mage.cards.d.DragonloftIdol.class));
@@ -157,7 +157,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Eternal Thirst", 229, Rarity.COMMON, mage.cards.e.EternalThirst.class));
         cards.add(new SetCardInfo("Exclude", 152, Rarity.UNCOMMON, mage.cards.e.Exclude.class));
         cards.add(new SetCardInfo("Exclusion Mage", 153, Rarity.UNCOMMON, mage.cards.e.ExclusionMage.class));
-        cards.add(new SetCardInfo("Exhume", 230, Rarity.COMMON, mage.cards.e.Exhume.class));
+        cards.add(new SetCardInfo("Exhume", 230, Rarity.UNCOMMON, mage.cards.e.Exhume.class));
         cards.add(new SetCardInfo("Explore", 393, Rarity.COMMON, mage.cards.e.Explore.class));
         cards.add(new SetCardInfo("Exquisite Blood", 231, Rarity.RARE, mage.cards.e.ExquisiteBlood.class));
         cards.add(new SetCardInfo("Fa'adiyah Seer", 394, Rarity.COMMON, mage.cards.f.FaadiyahSeer.class));
@@ -181,7 +181,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Forge Devil", 322, Rarity.COMMON, mage.cards.f.ForgeDevil.class));
         cards.add(new SetCardInfo("Fortify", 105, Rarity.COMMON, mage.cards.f.Fortify.class));
         cards.add(new SetCardInfo("Funeral Rites", 235, Rarity.COMMON, mage.cards.f.FuneralRites.class));
-        cards.add(new SetCardInfo("Furnace Whelp", 323, Rarity.COMMON, mage.cards.f.FurnaceWhelp.class));
+        cards.add(new SetCardInfo("Furnace Whelp", 323, Rarity.UNCOMMON, mage.cards.f.FurnaceWhelp.class));
         cards.add(new SetCardInfo("Fusion Elemental", 451, Rarity.UNCOMMON, mage.cards.f.FusionElemental.class));
         cards.add(new SetCardInfo("Gargoyle Sentinel", 465, Rarity.UNCOMMON, mage.cards.g.GargoyleSentinel.class));
         cards.add(new SetCardInfo("Ghalta, Primal Hunger", 399, Rarity.RARE, mage.cards.g.GhaltaPrimalHunger.class));
diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt
index d824061739..0e99bd2dc9 100644
--- a/Utils/mtg-cards-data.txt
+++ b/Utils/mtg-cards-data.txt
@@ -37469,7 +37469,7 @@ Nine Lives|Core Set 2021|28|R|{1}{W}{W}|Enchantment|||Hexproof$If a source would
 Pack Leader|Core Set 2021|29|R|{1}{W}|Creature - Dog|2|2|Other Dogs you control get +1/+1.$Whenever Pack Leader attacks, prevent all combat damage that would be dealt this turn to Dogs you control.|
 Rambunctious Mutt|Core Set 2021|30|C|{3}{W}{W}|Creature - Dog|3|4|When Rambunctious Mutt enters the battlefield, destroy target artifact or enchantment an opponent controls.|
 Revitalize|Core Set 2021|31|C|{1}{W}|Instant|||You gain 3 life.$Draw a card.|
-Runed Halo|Core Set 2021|32|R|{W}{W}|Enchantment|||As Runed Halo enters the battlefield, choose a card name.$You have protection from the chosen name.|
+Runed Halo|Core Set 2021|32|R|{W}{W}|Enchantment|||As Runed Halo enters the battlefield, choose a card name.$You have protection from the chosen card name.|
 Sanctum of Tranquil Light|Core Set 2021|33|U|{W}|Legendary Enchantment - Shrine|||{5}{W}: Tap target creature. This ability costs {1} less to activate for each Shrine you control.|
 Seasoned Hallowblade|Core Set 2021|34|U|{1}{W}|Creature - Human Warrior|3|1|Discard a card: Tap Seasoned Hallowblade. It gains indestructible until end of turn.|
 Secure the Scene|Core Set 2021|35|C|{4}{W}|Sorcery|||Exile target nonland permanent. Its controller creates a 1/1 white Soldier creature token.|
@@ -37540,7 +37540,7 @@ Finishing Blow|Core Set 2021|99|C|{4}{B}|Instant|||Destroy target creature or pl
 Gloom Sower|Core Set 2021|100|C|{5}{B}{B}|Creature - Horror|8|6|Whenever Gloom Sower becomes blocked by a creature, that creature's controller loses 2 life and you gain 2 life.|
 Goremand|Core Set 2021|101|U|{4}{B}{B}|Creature - Demon|5|5|As an additional cost to cast this spell, sacrifice a creature.$Flying$Trample$When Goremand enters the battlefield, each opponent sacrifices a creature.|
 Grasp of Darkness|Core Set 2021|102|C|{B}{B}|Instant|||Target creature gets -4/-4 until end of turn.|
-Grim Tutor|Core Set 2021|103|M|{1}{B}{B}|Sorcery|||Search your library for a card and put that card into your hand, then shuffle your library. You lose 3 life.|
+Grim Tutor|Core Set 2021|103|M|{1}{B}{B}|Sorcery|||Search your library for a card, put that card into your hand, then shuffle your library. You lose 3 life.|
 Hooded Blightfang|Core Set 2021|104|R|{2}{B}|Creature - Snake|1|4|Deathtouch$Whenever a creature you control with deathtouch attacks, each opponent loses 1 life and you gain 1 life.$Whenever a creature you control with deathtouch deals damage to a planeswalker, destroy that planeswalker.|
 Infernal Scarring|Core Set 2021|105|C|{1}{B}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +2/+0 and has "When this creature dies, draw a card."|
 Kaervek, the Spiteful|Core Set 2021|106|R|{2}{B}{B}|Legendary Creature - Human Warlock|3|2|Other creatures get -1/-1.|
@@ -37635,7 +37635,7 @@ Ornery Dilophosaur|Core Set 2021|194|C|{3}{G}|Creature - Dinosaur|2|2|Deathtouch
 Portcullis Vine|Core Set 2021|195|C|{G}|Creature - Plant Wall|0|3|Defender${2}, {T}, Sacrifice a creature with defender: Draw a card.|
 Pridemalkin|Core Set 2021|196|C|{2}{G}|Creature - Cat|2|1|When Pridemalkin enters the battlefield, put a +1/+1 counter on target creature you control.$Each creature you control with a +1/+1 counter on it has trample.|
 Primal Might|Core Set 2021|197|R|{X}{G}|Sorcery|||Target creature you control gets +X/+X until end of turn. Then it fights up to one target creature you don't control.|
-Quirion Dryad|Core Set 2021|198|U|{1}{G}|Creature - Dryad|1|1|Whenever you cast a white, blue, black, or red spell, put a +1/+1 counter on Quirion Dryad.|
+Quirion Dryad|Core Set 2021|198|U|{1}{G}|Creature - Dryad|1|1|Whenever you cast a spell that's white, blue, black, or red, put a +1/+1 counter on Quirion Dryad.|
 Ranger's Guile|Core Set 2021|199|C|{G}|Instant|||Target creature you control gets +1/+1 and gains hexproof until end of turn.|
 Return to Nature|Core Set 2021|200|C|{1}{G}|Instant|||Choose one —$• Destroy target artifact.$• Destroy target enchantment.$• Exile target card from a graveyard.|
 Run Afoul|Core Set 2021|201|C|{G}|Instant|||Target opponent sacrifices a creature with flying.|
@@ -37658,7 +37658,7 @@ Dire Fleet Warmonger|Core Set 2021|217|U|{1}{B}{R}|Creature - Orc Pirate|3|3|At
 Experimental Overload|Core Set 2021|218|U|{2}{U}{R}|Sorcery|||Create an X/X blue and red Weird creature token, where X is the number of instant and sorcery cards in your graveyard. Then you may return an instant or sorcery card from your graveyard to your hand. Exile Experimental Overload.|
 Indulging Patrician|Core Set 2021|219|U|{1}{W}{B}|Creature - Vampire Noble|1|4|Flying$Lifelink$At the beginning of your end step, if you gained 3 or more life this turn, each opponent loses 3 life.|
 Leafkin Avenger|Core Set 2021|220|U|{2}{R}{G}|Creature - Elemental Druid|4|3|{T}: Add {G} for each creature you control with power 4 or greater.${7}{R}: Leafkin Avenger deals damage equal to its power to target player or planeswalker.|
-Lorescale Coatl|Core Set 2021|221|U|{1}{G}{U}|Creature - Snake|2|2|Whenever you draw a card, you may put a +1/+1 counter on Lorescale Coatl.|
+Lorescale Coatl|Core Set 2021|221|U|{1}{G}{U}|Creature - Snake|2|2|Whenever you draw a card, put a +1/+1 counter on Lorescale Coatl.|
 Niambi, Esteemed Speaker|Core Set 2021|222|R|{W}{U}|Legendary Creature - Human Cleric|2|1|Flash$When Niambi, Esteemed Speaker enters the battlefield, you may return another target creature you control to its owner's hand. If you do, you gain life equal to that creature's converted mana cost.${1}{W}{U}, {T}, Discard a legendary card: Draw two cards.|
 Obsessive Stitcher|Core Set 2021|223|U|{1}{U}{B}|Creature - Human Wizard|0|3|{T}: Draw a card, then discard a card.${2}{U}{B}, {T}, Sacrifice Obsessive Stitcher: Return target creature card from your graveyard to the battlefield.|
 Radha, Heart of Keld|Core Set 2021|224|R|{1}{R}{G}|Legendary Creature - Elf Warrior|3|3|As long as it's your turn, Radha, Heart of Keld has first strike.$You may look at the top card of your library any time, and you may play lands from the top of your library.${4}{R}{G}: Radha gets +X/+X until end of turn, where X is the number of lands you control.|
@@ -37732,7 +37732,7 @@ Stone Haven Pilgrim|Jumpstart|6|U|{1}{W}|Creature - Kor Cleric|2|2|Whenever Ston
 Supply Runners|Jumpstart|7|U|{4}{W}|Creature - Dog|2|2|When Supply Runners enters the battlefield, put a +1/+1 counter on each other creature you control.|
 Trusty Retriever|Jumpstart|8|C|{3}{W}|Creature - Dog|2|3|When Trusty Retriever enters the battlefield, choose one —$• Put a +1/+1 counter on Trusty Retriever.$• Return target artifact or enchantment card from your graveyard to your hand.|
 Archaeomender|Jumpstart|9|C|{2}{U}|Creature - Human Wizard|2|3|When Archaeomender enters the battlefield, return target artifact card from your graveyard to your hand.|
-Bruvac the Grandiloquent|Jumpstart|10|M|{2}{U}|Legendary Creature - Human Advisor|1|4|If an opponent would mill one or more cards, they mill twice that many cards insted.|
+Bruvac the Grandiloquent|Jumpstart|10|M|{2}{U}|Legendary Creature - Human Advisor|1|4|If an opponent would mill one or more cards, they mill twice that many cards instead.|
 Corsair Captain|Jumpstart|11|R|{2}{U}|Creature - Human Pirate|2|2|When Corsair Captain enters the battlefield, create a treasure token.$Other Pirates you control get +1/+1.|
 Inniaz, the Gale Force|Jumpstart|12|R|{3}{U}{U}|Legendary Creature - Djinn|4|4|Flying${2}{W/U}: Attacking creatures with flying get +1/+1 until end of turn.$Whenever three or more creatures you control with flying attack, each player gains control of a nonland permanent of your choice controlled by the player to their right.|
 Ormos, Archive Keeper|Jumpstart|13|R|{4}{U}{U}|Legendary Creature - Sphinx|5|5|Flying$If you would draw a card while your library has no cards in it, instead put five +1/+1 counters on Ormos, Archive Keeper.${1}{U}{U}, Discard three cards with different names: Draw five cards.|
@@ -37742,7 +37742,7 @@ Nocturnal Feeder|Jumpstart|16|C|{2}{B}|Creature - Vampire Rogue|2|1|Flying$When
 Tinybones, Trinket Thief|Jumpstart|17|M|{1}{B}|Legendary Creature - Skeleton Rogue|1|2|At the beginning of each end step, if an opponent discarded a card this turn, you draw a card and you lose 1 life.${4}{B}{B}: Each opponent with no cards in hand loses 10 life.|
 Witch of the Moors|Jumpstart|18|R|{3}{B}{B}|Creature - Human Warlock|4|4|Deathtouch$At the beginning of your end step, if you gained life this turn, each opponent sacrifices a creature and you return up to one target creature card from your graveyard to your hand.|
 Chained Brute|Jumpstart|19|U|{1}{R}|Creature - Devil|4|3|Chained Brute doesn't untap during your untap step.${1}, Sacrifice another creature: Untap Chained Brute. Activate this ability only during your turn.|
-Immolating Gyre|Jumpstart|20|M|{4}{R}{R}|Sorcery|||Immolating Gyre deals X damage to each creature and planeswalker you don't control, where X is the number of instant and sorcery cards in your graveyard,|
+Immolating Gyre|Jumpstart|20|M|{4}{R}{R}|Sorcery|||Immolating Gyre deals X damage to each creature and planeswalker you don't control, where X is the number of instant and sorcery cards in your graveyard.|
 Lightning Phoenix|Jumpstart|21|R|{2}{R}|Creature - Phoenix|2|2|Flying, haste$Lightning Phoenix can't block.$At the beginning of your end step, if an opponent was dealt 3 or more damage this turn, you may pay {R}. If you do, return Lightning Phoenix from your graveyard to the battlefield.|
 Lightning Visionary|Jumpstart|22|C|{1}{R}|Creature - Minotaur Shaman|2|1|Prowess|
 Living Lightning|Jumpstart|23|U|{3}{R}|Creature - Elemental Shaman|3|2|When Living Lightning dies, return target instant or sorcery card from your graveyard to your hand.|
@@ -37768,7 +37768,7 @@ Forest|Jumpstart|70|C||Basic Land - Forest|||({T}: Add {G}.)|
 Terramorphic Expanse|Jumpstart|78|C||Land|||{T}, Sacrifice Terramorphic Expanse: Search your library for a basic land card, put it onto the battlefield tapped, then shuffle your library.|
 Aegis of the Heavens|Jumpstart|79|U|{1}{W}|Instant|||Target creature gets +1/+7 until end of turn.|
 Aerial Assault|Jumpstart|80|C|{2}{W}|Sorcery|||Destroy target tapped creature. You gain 1 life for each creature you control with flying.|
-Affa Guard Hound|Jumpstart|81|U|{2}{W}|Creature - Hound|2|2|Flash$When Affa Guard Hound enters the battlefield, target creature gets +0/+3 until end of turn.|
+Affa Guard Hound|Jumpstart|81|U|{2}{W}|Creature - Dog|2|2|Flash$When Affa Guard Hound enters the battlefield, target creature gets +0/+3 until end of turn.|
 Ajani's Chosen|Jumpstart|82|R|{2}{W}{W}|Creature - Cat Soldier|3|3|Whenever an enchantment enters the battlefield under your control, create a 2/2 white Cat creature token. If that enchantment is an Aura, you may attach it to the token.|
 Alabaster Mage|Jumpstart|83|U|{1}{W}|Creature - Human Wizard|2|1|{1}{W}: Target creature you control gains lifelink until end of turn.|
 Angel of Mercy|Jumpstart|84|C|{4}{W}|Creature - Angel|3|3|Flying$When Angel of Mercy enters the battlefield, you gain 3 life.|
@@ -37781,7 +37781,7 @@ Archon of Redemption|Jumpstart|90|R|{3}{W}{W}|Creature - Archon|3|4|Flying$Whene
 Battlefield Promotion|Jumpstart|91|C|{1}{W}|Instant|||Put a +1/+1 counter on target creature. That creature gains first strike until end of turn. You gain 2 life.|
 Blessed Spirits|Jumpstart|92|U|{2}{W}|Creature - Spirit|2|2|Flying$Whenever you cast an enchantment spell, put a +1/+1 counter on Blessed Spirits.|
 Bulwark Giant|Jumpstart|93|C|{5}{W}|Creature - Giant Soldier|3|6|When Bulwark Giant enters the battlefield, you gain 5 life.|
-Cathar's Companion|Jumpstart|94|C|{2}{W}|Creature - Hound|3|1|Whenever you cast a noncreature spell, Cathar's Companion gains indestructible until end of turn.|
+Cathar's Companion|Jumpstart|94|C|{2}{W}|Creature - Dog|3|1|Whenever you cast a noncreature spell, Cathar's Companion gains indestructible until end of turn.|
 Cathars' Crusade|Jumpstart|95|R|{3}{W}{W}|Enchantment|||Whenever a creature enters the battlefield under your control, put a +1/+1 counter on each creature you control.|
 Celestial Mantle|Jumpstart|96|R|{3}{W}{W}{W}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +3/+3.$Whenever enchanted creature deals combat damage to a player, double its controller's life total.|
 Cloudshift|Jumpstart|97|C|{W}|Instant|||Exile target creature you control, then return that card to the battlefield under your control.|
@@ -37800,7 +37800,7 @@ Indomitable Will|Jumpstart|109|C|{1}{W}|Enchantment - Aura|||Flash$Enchant creat
 Inspired Charge|Jumpstart|110|C|{2}{W}{W}|Instant|||Creatures you control get +2/+1 until end of turn.|
 Inspiring Captain|Jumpstart|111|C|{3}{W}|Creature - Human Knight|3|3|When Inspiring Captain enters the battlefield, creatures you control get +1/+1 until end of turn.|
 Inspiring Unicorn|Jumpstart|112|U|{2}{W}{W}|Creature - Unicorn|2|2|Whenever Inspiring Unicorn attacks, creatures you control get +1/+1 until end of turn.|
-Isamaru, Hound of Konda|Jumpstart|113|R|{W}|Legendary Creature - Hound|2|2||
+Isamaru, Hound of Konda|Jumpstart|113|R|{W}|Legendary Creature - Dog|2|2||
 Knight of the Tusk|Jumpstart|114|C|{4}{W}{W}|Creature - Human Knight|3|7|Vigilance|
 Knightly Valor|Jumpstart|115|C|{4}{W}|Enchantment - Aura|||Enchant creature$When Knightly Valor enters the battlefield, create a 2/2 white Knight creature token with vigilance.$Enchanted creature gets +2/+2 and has vigilance.|
 Kor Spiritdancer|Jumpstart|116|R|{1}{W}|Creature - Kor Wizard|0|2|Kor Spiritdancer gets +2/+2 for each Aura attached to it.$Whenever you cast an Aura spell, you may draw a card.|
@@ -37828,7 +37828,7 @@ Voice of the Provinces|Jumpstart|137|C|{4}{W}{W}|Creature - Angel|3|3|Flying$Whe
 Aegis Turtle|Jumpstart|138|C|{U}|Creature - Turtle|0|5||
 Battleground Geist|Jumpstart|139|U|{4}{U}|Creature - Spirit|3|3|Flying$Other Spirit creatures you control get +1/+0.|
 Befuddle|Jumpstart|140|C|{2}{U}|Instant|||Target creature gets -4/-0 until end of turn.$Draw a card.|
-Belltower Sphinx|Jumpstart|141|U|{4}{U}|Creature - Sphinx|2|5|Flying$Whenever a source deals damage to Belltower Sphinx, that source's controller puts that many cards from the top of their library into their graveyard.|
+Belltower Sphinx|Jumpstart|141|U|{4}{U}|Creature - Sphinx|2|5|Flying$Whenever a source deals damage to Belltower Sphinx, that source's controller mills that many cards.|
 Chart a Course|Jumpstart|142|U|{1}{U}|Sorcery|||Draw two cards. Then discard a card unless you attacked this turn.|
 Cloudreader Sphinx|Jumpstart|143|C|{4}{U}|Creature - Sphinx|3|4|Flying$When Cloudreader Sphinx enters the battlefield, scry 2.|
 Coastal Piracy|Jumpstart|144|U|{2}{U}{U}|Enchantment|||Whenever a creature you control deals combat damage to an opponent, you may draw a card.|
@@ -37861,7 +37861,7 @@ Rishadan Airship|Jumpstart|170|C|{2}{U}|Creature - Human Pirate|3|1|Flying$Risha
 Sage's Row Savant|Jumpstart|171|C|{1}{U}|Creature - Vedalken Wizard|2|1|When Sage's Row Savant enters the battlefield, scry 2.|
 Sailor of Means|Jumpstart|172|C|{2}{U}|Creature - Human Pirate|1|4|When Sailor of Means enters the battlefield, create a Treasure token.|
 Sea Gate Oracle|Jumpstart|173|C|{2}{U}|Creature - Human Wizard|1|3|When Sea Gate Oracle enters the battlefield, look at the top two cards of your library. Put one of them into your hand and the other on the bottom of your library.|
-Selhoff Occultist|Jumpstart|174|C|{2}{U}|Creature - Human Rogue|2|3|Whenever Selhoff Occultist or another creature dies, target player puts the top card of their library into their graveyard.|
+Selhoff Occultist|Jumpstart|174|C|{2}{U}|Creature - Human Rogue|2|3|Whenever Selhoff Occultist or another creature dies, target player mills a card.|
 Serendib Efreet|Jumpstart|175|R|{2}{U}|Creature - Efreet|3|4|Flying$At the beginning of your upkeep, Serendib Efreet deals 1 damage to you.|
 Sharding Sphinx|Jumpstart|176|R|{4}{U}{U}|Artifact Creature - Sphinx|4|4|Flying$Whenever an artifact creature you control deals combat damage to a player, you may create a 1/1 blue Thopter artifact creature token with flying.|
 Sigiled Starfish|Jumpstart|177|U|{1}{U}|Creature - Starfish|0|3|{T}: Scry 1.|
@@ -37871,13 +37871,13 @@ Sweep Away|Jumpstart|180|C|{2}{U}|Instant|||Return target creature to its owner'
 Talrand, Sky Summoner|Jumpstart|181|R|{2}{U}{U}|Legendary Creature - Merfolk Wizard|2|2|Whenever you cast an instant or sorcery spell, create a 2/2 blue Drake creature token with flying.|
 Talrand's Invocation|Jumpstart|182|U|{2}{U}{U}|Sorcery|||Create two 2/2 blue Drake creature tokens with flying.|
 Thirst for Knowledge|Jumpstart|183|U|{2}{U}|Instant|||Draw three cards. Then discard two cards unless you discard an artifact card.|
-Thought Collapse|Jumpstart|184|C|{1}{U}{U}|Instant|||Counter target spell. Its controller puts the top three cards of their library into their graveyard.|
-Thought Scour|Jumpstart|185|C|{U}|Instant|||Target player puts the top two cards of their library into their graveyard.$Draw a card.|
-Towering-Wave Mystic|Jumpstart|186|C|{1}{U}|Creature - Merfolk Wizard|2|1|Whenever Towering-Wave Mystic deals damage, target player puts that many cards from the top of their library into their graveyard.|
+Thought Collapse|Jumpstart|184|C|{1}{U}{U}|Instant|||Counter target spell. Its controller mills three cards.|
+Thought Scour|Jumpstart|185|C|{U}|Instant|||Target player mills two cards.$Draw a card.|
+Towering-Wave Mystic|Jumpstart|186|C|{1}{U}|Creature - Merfolk Wizard|2|1|Whenever Towering-Wave Mystic deals damage, target player mills that many cards.|
 Vedalken Archmage|Jumpstart|187|R|{2}{U}{U}|Creature - Vedalken Wizard|0|2|Whenever you cast an artifact spell, draw a card.|
-Vedalken Entrancer|Jumpstart|188|C|{3}{U}|Creature - Vedalken Wizard|1|4|{U}, {T}: Target player puts the top two cards of their library into their graveyard.|
+Vedalken Entrancer|Jumpstart|188|C|{3}{U}|Creature - Vedalken Wizard|1|4|{U}, {T}: Target player mills two cards.|
 Voyage's End|Jumpstart|189|C|{1}{U}|Instant|||Return target creature to its owner's hand. Scry 1.|
-Wall of Lost Thoughts|Jumpstart|190|U|{1}{U}|Creature - Wall|0|4|Defender$When Wall of Lost Thoughts enters the battlefield, target player puts the top four cards of their library into their graveyard.|
+Wall of Lost Thoughts|Jumpstart|190|U|{1}{U}|Creature - Wall|0|4|Defender$When Wall of Lost Thoughts enters the battlefield, target player mills four cards.|
 Warden of Evos Isle|Jumpstart|191|U|{2}{U}|Creature - Bird Wizard|2|2|Flying$Creature spells with flying you cast cost {1} less to cast.|
 Waterknot|Jumpstart|192|C|{1}{U}{U}|Enchantment - Aura|||Enchant creature$When Waterknot enters the battlefield, tap enchanted creature.$Enchanted creature doesn't untap during its controller's untap step.|
 Whelming Wave|Jumpstart|193|R|{2}{U}{U}|Sorcery|||Return all creatures to their owners' hands except for Krakens, Leviathans, Octopuses, and Serpents.|
@@ -37908,7 +37908,7 @@ Cemetery Recruitment|Jumpstart|217|C|{1}{B}|Sorcery|||Return target creature car
 Child of Night|Jumpstart|218|C|{1}{B}|Creature - Vampire|2|1|Lifelink|
 Corpse Hauler|Jumpstart|219|C|{1}{B}|Creature - Human Rogue|2|1|{2}{B}, Sacrifice Corpse Hauler: Return another target creature card from your graveyard to your hand.|
 Corpse Traders|Jumpstart|220|U|{3}{B}|Creature - Human Rogue|3|3|{2}{B}, Sacrifice a creature: Target opponent reveals their hand. You choose a card from it. That player discards that card. Activate this ability only any time you could cast a sorcery.|
-Crow of Dark Tidings|Jumpstart|221|C|{2}{B}|Creature - Zombie Bird|2|1|Flying$When Crow of Dark Tidings enters the battlefield or dies, put the top two cards of your library into your graveyard.|
+Crow of Dark Tidings|Jumpstart|221|C|{2}{B}|Creature - Zombie Bird|2|1|Flying$When Crow of Dark Tidings enters the battlefield or dies, mill two cards.|
 Death's Approach|Jumpstart|222|C|{B}|Enchantment - Aura|||Enchant creature$Enchanted creature gets -X/-X, where X is the number of creature cards in its controller's graveyard.|
 Douse in Gloom|Jumpstart|223|C|{2}{B}|Instant|||Douse in Gloom deals 2 damage to target creature and you gain 2 life.|
 Drainpipe Vermin|Jumpstart|224|C|{B}|Creature - Rat|1|1|When Drainpipe Vermin dies, you may pay {B}. If you do, target player discards a card.|
@@ -37917,12 +37917,12 @@ Dutiful Attendant|Jumpstart|226|C|{2}{B}|Creature - Human Warrior|1|2|When Dutif
 Entomber Exarch|Jumpstart|227|U|{2}{B}{B}|Creature - Cleric|2|2|When Entomber Exarch enters the battlefield, choose one —$• Return target creature card from your graveyard to your hand.$• Target opponent reveals their hand. You choose a noncreature card from it. That player discards that card.|
 Eternal Taskmaster|Jumpstart|228|U|{1}{B}|Creature - Zombie|2|3|Eternal Taskmaster enters the battlefield tapped.$Whenever Eternal Taskmaster attacks, you may pay {2}{B}. If you do, return target creature card from your graveyard to your hand.|
 Eternal Thirst|Jumpstart|229|C|{1}{B}|Enchantment - Aura|||Enchant creature$Enchanted creature has lifelink and "Whenever a creature an opponent controls dies, put a +1/+1 counter on this creature."|
-Exhume|Jumpstart|230|C|{1}{B}|Sorcery|||Each player puts a creature card from their graveyard onto the battlefield.|
+Exhume|Jumpstart|230|U|{1}{B}|Sorcery|||Each player puts a creature card from their graveyard onto the battlefield.|
 Exquisite Blood|Jumpstart|231|R|{4}{B}|Enchantment|||Whenever an opponent loses life, you gain that much life.|
 Falkenrath Noble|Jumpstart|232|U|{3}{B}|Creature - Vampire Noble|2|2|Flying$Whenever Falkenrath Noble or another creature dies, target player loses 1 life and you gain 1 life.|
 Fell Specter|Jumpstart|233|U|{3}{B}|Creature - Specter|1|3|Flying$When Fell Specter enters the battlefield, target opponent discards a card.$Whenever an opponent discards a card, that player loses 2 life.|
 Festering Newt|Jumpstart|234|C|{B}|Creature - Salamander|1|1|When Festering Newt dies, target creature an opponent controls gets -1/-1 until end of turn. That creature gets -4/-4 instead if you control a creature named Bogbrew Witch.|
-Funeral Rites|Jumpstart|235|C|{2}{B}|Sorcery|||You draw two cards, lose 2 life, and put the top two cards of your library into your graveyard.|
+Funeral Rites|Jumpstart|235|C|{2}{B}|Sorcery|||You draw two cards, lose 2 life, then mill two cards.|
 Ghoulcaller Gisa|Jumpstart|236|M|{3}{B}{B}|Legendary Creature - Human Wizard|3|4|{B}, {T}, Sacrifice another creature: Create X 2/2 black Zombie creature tokens, where X is the sacrificed creature's power.|
 Ghoulcaller's Accomplice|Jumpstart|237|C|{1}{B}|Creature - Human Rogue|2|2|{3}{B}, Exile Ghoulcaller's Accomplice from your graveyard: Create a 2/2 black Zombie creature token. Activate this ability only any time you could cast a sorcery.|
 Ghoulraiser|Jumpstart|238|C|{1}{B}{B}|Creature - Zombie|2|2|When Ghoulraiser enters the battlefield, return a Zombie card at random from your graveyard to your hand.|
@@ -37944,7 +37944,7 @@ Malakir Familiar|Jumpstart|253|U|{2}{B}|Creature - Bat|2|1|Flying, deathtouch$Wh
 Mark of the Vampire|Jumpstart|254|C|{3}{B}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +2/+2 and has lifelink.|
 Mausoleum Turnkey|Jumpstart|255|U|{3}{B}|Creature - Ogre Rogue|3|2|When Mausoleum Turnkey enters the battlefield, return target creature card of an opponent's choice from your graveyard to your hand.|
 Miasmic Mummy|Jumpstart|256|C|{1}{B}|Creature - Zombie Jackal|2|2|When Miasmic Mummy enters the battlefield, each player discards a card.|
-Mire Triton|Jumpstart|257|U|{1}{B}|Creature - Zombie Merfolk|2|1|Deathtouch$When Mire Triton enters the battlefield, put the top two cards of your library into your graveyard and you gain 2 life.|
+Mire Triton|Jumpstart|257|U|{1}{B}|Creature - Zombie Merfolk|2|1|Deathtouch$When Mire Triton enters the battlefield, mill two cards and you gain 2 life.|
 Nightshade Stinger|Jumpstart|258|C|{B}|Creature - Faerie Rogue|1|1|Flying$Nightshade Stinger can't block.|
 Nyxathid|Jumpstart|259|R|{1}{B}{B}|Creature - Elemental|7|7|As Nyxathid enters the battlefield, choose an opponent.$Nyxathid gets -1/-1 for each card in the chosen player's hand.|
 Ogre Slumlord|Jumpstart|260|R|{3}{B}{B}|Creature - Ogre Rogue|3|3|Whenever another nontoken creature dies, you may create a 1/1 black Rat creature token.$Rats you control have deathtouch.|
@@ -37973,11 +37973,11 @@ Swarm of Bloodflies|Jumpstart|282|U|{4}{B}|Creature - Insect|0|0|Flying$Swarm of
 Tempting Witch|Jumpstart|283|U|{2}{B}|Creature - Human Warlock|1|3|When Tempting Witch enters the battlefield, create a Food token.${2}, {T}, Sacrifice a Food: Target player loses 3 life.|
 Tithebearer Giant|Jumpstart|284|C|{5}{B}|Creature - Giant Warrior|4|5|When Tithebearer Giant enters the battlefield, you draw a card and you lose 1 life.|
 Vampire Neonate|Jumpstart|285|C|{B}|Creature - Vampire|0|3|{2}, {T}: Each opponent loses 1 life and you gain 1 life.|
-Wailing Ghoul|Jumpstart|286|C|{1}{B}|Creature - Zombie|1|3|When Wailing Ghoul enters the battlefield, put the top two cards of your library into your graveyard.|
+Wailing Ghoul|Jumpstart|286|C|{1}{B}|Creature - Zombie|1|3|When Wailing Ghoul enters the battlefield, mill two cards.|
 Wight of Precinct Six|Jumpstart|287|C|{1}{B}|Creature - Zombie|1|1|Wight of Precinct Six gets +1/+1 for each creature card in your opponents' graveyards.|
 Zombie Infestation|Jumpstart|288|U|{1}{B}|Enchantment|||Discard two cards: Create a 2/2 black Zombie creature token.|
 Act of Treason|Jumpstart|289|C|{2}{R}|Sorcery|||Gain control of target creature until end of turn. Untap that creature. It gains haste until end of turn.|
-Ashmouth Hound|Jumpstart|290|C|{1}{R}|Creature - Elemental Hound|2|1|Whenever Ashmouth Hound blocks or becomes blocked by a creature, Ashmouth Hound deals 1 damage to that creature.|
+Ashmouth Hound|Jumpstart|290|C|{1}{R}|Creature - Elemental Dog|2|1|Whenever Ashmouth Hound blocks or becomes blocked by a creature, Ashmouth Hound deals 1 damage to that creature.|
 Ball Lightning|Jumpstart|291|R|{R}{R}{R}|Creature - Elemental|6|1|Trample$Haste$At the beginning of the end step, sacrifice Ball Lightning.|
 Barrage of Expendables|Jumpstart|292|U|{R}|Enchantment|||{R}, Sacrifice a creature: Barrage of Expendables deals 1 damage to any target.|
 Bathe in Dragonfire|Jumpstart|293|C|{2}{R}|Sorcery|||Bathe in Dragonfire deals 4 damage to target creature.|
@@ -37995,7 +37995,7 @@ Cinder Elemental|Jumpstart|304|U|{3}{R}|Creature - Elemental|2|2|{X}{R}, {T}, Sa
 Collateral Damage|Jumpstart|305|C|{R}|Instant|||As an additional cost to cast this spell, sacrifice a creature.$Collateral Damage deals 3 damage to any target.|
 Dance with Devils|Jumpstart|306|U|{3}{R}|Instant|||Create two 1/1 red Devil creature tokens. They have "When this creature dies, it deals 1 damage to any target."|
 Doublecast|Jumpstart|307|U|{R}{R}|Sorcery|||When you cast your next instant or sorcery spell this turn, copy that spell. You may choose new targets for the copy.|
-Draconic Roar|Jumpstart|308|C|{1}{R}|Instant|||As an additional cost to cast this spell, you may reveal a Dragon card from your hand.$Draconic Roar deals 3 damage to target creature. If you revealed a Dragon card or controlled a Dragon as you cast this spell, Draconic Roar deals 3 damage to that creature's controller.|
+Draconic Roar|Jumpstart|308|U|{1}{R}|Instant|||As an additional cost to cast this spell, you may reveal a Dragon card from your hand.$Draconic Roar deals 3 damage to target creature. If you revealed a Dragon card or controlled a Dragon as you cast this spell, Draconic Roar deals 3 damage to that creature's controller.|
 Dragon Fodder|Jumpstart|309|C|{1}{R}|Sorcery|||Create two 1/1 red Goblin creature tokens.|
 Dragon Hatchling|Jumpstart|310|C|{1}{R}|Creature - Dragon|0|1|Flying${R}: Dragon Hatchling gets +1/+0 until end of turn.|
 Dragonlord's Servant|Jumpstart|311|U|{1}{R}|Creature - Goblin Shaman|1|3|Dragon spells you cast cost {1} less to cast.|
@@ -38010,7 +38010,7 @@ Flametongue Kavu|Jumpstart|319|U|{3}{R}|Creature - Kavu|4|2|When Flametongue Kav
 Fling|Jumpstart|320|C|{1}{R}|Instant|||As an additional cost to cast this spell, sacrifice a creature.$Fling deals damage equal to the sacrificed creature's power to any target.|
 Flurry of Horns|Jumpstart|321|C|{4}{R}|Sorcery|||Create two 2/3 red Minotaur creature tokens with haste.|
 Forge Devil|Jumpstart|322|C|{R}|Creature - Devil|1|1|When Forge Devil enters the battlefield, it deals 1 damage to target creature and 1 damage to you.|
-Furnace Whelp|Jumpstart|323|C|{2}{R}{R}|Creature - Dragon|2|2|Flying${R}: Furnace Whelp gets +1/+0 until end of turn.|
+Furnace Whelp|Jumpstart|323|U|{2}{R}{R}|Creature - Dragon|2|2|Flying${R}: Furnace Whelp gets +1/+0 until end of turn.|
 Goblin Chieftain|Jumpstart|324|R|{1}{R}{R}|Creature - Goblin|2|2|Haste$Other Goblin creatures you control get +1/+1 and have haste.|
 Goblin Commando|Jumpstart|325|U|{4}{R}|Creature - Goblin|2|2|When Goblin Commando enters the battlefield, it deals 2 damage to target creature.|
 Goblin Goon|Jumpstart|326|R|{3}{R}|Creature - Goblin Mutant|6|6|Goblin Goon can't attack unless you control more creatures than defending player.$Goblin Goon can't block unless you control more creatures than attacking player.|
@@ -38141,7 +38141,7 @@ Dinrova Horror|Jumpstart|450|U|{4}{U}{B}|Creature - Horror|4|4|When Dinrova Horr
 Fusion Elemental|Jumpstart|451|U|{W}{U}{B}{R}{G}|Creature - Elemental|8|8||
 Ironroot Warlord|Jumpstart|452|U|{1}{G}{W}|Creature - Treefolk Soldier|*|5|Ironroot Warlord's power is equal to the number of creatures you control.${3}{G}{W}: Create a 1/1 white Soldier creature token.|
 Lawmage's Binding|Jumpstart|453|C|{1}{W}{U}|Enchantment - Aura|||Flash$Enchant creature$Enchanted creature can't attack or block, and its activated abilities can't be activated.|
-Maelstrom Archangel|Jumpstart|454|M|{W}{U}{B}{R}{G}|Creature - Angel|5|5|Flying$Whenever Maelstrom Archangel deals combat damage to a player, you may cast a nonland card from your hand without paying its mana cost.|
+Maelstrom Archangel|Jumpstart|454|M|{W}{U}{B}{R}{G}|Creature - Angel|5|5|Flying$Whenever Maelstrom Archangel deals combat damage to a player, you may cast a spell from your hand without paying its mana cost.|
 Raging Regisaur|Jumpstart|455|U|{2}{R}{G}|Creature - Dinosaur|4|4|Whenever Raging Regisaur attacks, it deals 1 damage to any target.|
 Aether Spellbomb|Jumpstart|456|C|{1}|Artifact|||{U}, Sacrifice Aether Spellbomb: Return target creature to its owner's hand.${1}, Sacrifice Aether Spellbomb: Draw a card.|
 Alloy Myr|Jumpstart|457|C|{3}|Artifact Creature - Myr|2|2|{T}: Add one mana of any color.|

From 7bb7afe28a744c174c55a6b1d7c206529970f617 Mon Sep 17 00:00:00 2001
From: htrajan <htrajan2002@yahoo.com>
Date: Wed, 24 Jun 2020 22:31:09 -0700
Subject: [PATCH 501/586] [M21] Implement sanctum cards (#6708)

---
 .../mage/cards/n/NabanDeanOfIteration.java    |  14 +--
 Mage.Sets/src/mage/cards/s/SanctumOfAll.java  | 107 ++++++++++++++++++
 .../src/mage/cards/s/SanctumOfCalmWaters.java |  16 ++-
 .../cards/s/SanctumOfFruitfulHarvest.java     |  57 ++++++++++
 .../cards/s/SanctumOfShatteredHeights.java    |  12 +-
 .../src/mage/cards/s/SanctumOfStoneFangs.java |  56 +++++++++
 .../mage/cards/s/SanctumOfTranquilLight.java  |  76 +++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |   4 +
 .../cost/CostModificationSourceEffect.java    |  64 +++++++++++
 9 files changed, 393 insertions(+), 13 deletions(-)
 create mode 100644 Mage.Sets/src/mage/cards/s/SanctumOfAll.java
 create mode 100644 Mage.Sets/src/mage/cards/s/SanctumOfFruitfulHarvest.java
 create mode 100644 Mage.Sets/src/mage/cards/s/SanctumOfStoneFangs.java
 create mode 100644 Mage.Sets/src/mage/cards/s/SanctumOfTranquilLight.java
 create mode 100644 Mage/src/main/java/mage/abilities/effects/common/cost/CostModificationSourceEffect.java

diff --git a/Mage.Sets/src/mage/cards/n/NabanDeanOfIteration.java b/Mage.Sets/src/mage/cards/n/NabanDeanOfIteration.java
index be2690ceb5..bc03ebc017 100644
--- a/Mage.Sets/src/mage/cards/n/NabanDeanOfIteration.java
+++ b/Mage.Sets/src/mage/cards/n/NabanDeanOfIteration.java
@@ -1,24 +1,24 @@
 
 package mage.cards.n;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.ReplacementEffectImpl;
-import mage.constants.SubType;
-import mage.constants.SuperType;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.Duration;
 import mage.constants.Outcome;
-import mage.constants.Zone;
+import mage.constants.SubType;
+import mage.constants.SuperType;
 import mage.game.Game;
 import mage.game.events.EntersTheBattlefieldEvent;
 import mage.game.events.GameEvent;
 import mage.game.events.NumberOfTriggersEvent;
 
+import java.util.UUID;
+
 /**
  *
  * @author TheElk801
@@ -35,7 +35,7 @@ public final class NabanDeanOfIteration extends CardImpl {
         this.toughness = new MageInt(1);
 
         // If a Wizard entering the battlefield under your control causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time.
-        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new NabanDeanOfIterationEffect()));
+        this.addAbility(new SimpleStaticAbility(new NabanDeanOfIterationEffect()));
     }
 
     public NabanDeanOfIteration(final NabanDeanOfIteration card) {
@@ -84,9 +84,7 @@ class NabanDeanOfIterationEffect extends ReplacementEffectImpl {
                     // Only for entering artifacts or creatures
                     if (entersTheBattlefieldEvent.getTarget().hasSubtype(SubType.WIZARD, game)) {
                         // Only for triggers of permanents
-                        if (game.getPermanent(numberOfTriggersEvent.getSourceId()) != null) {
-                            return true;
-                        }
+                        return game.getPermanent(numberOfTriggersEvent.getSourceId()) != null;
                     }
                 }
             }
diff --git a/Mage.Sets/src/mage/cards/s/SanctumOfAll.java b/Mage.Sets/src/mage/cards/s/SanctumOfAll.java
new file mode 100644
index 0000000000..ccb8c70cf7
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/SanctumOfAll.java
@@ -0,0 +1,107 @@
+package mage.cards.s;
+
+import mage.abilities.Ability;
+import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
+import mage.abilities.effects.ReplacementEffectImpl;
+import mage.abilities.effects.common.search.SearchLibraryGraveyardPutOntoBattlefieldEffect;
+import mage.abilities.hint.ValueHint;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.Outcome;
+import mage.constants.SubType;
+import mage.constants.SuperType;
+import mage.constants.TargetController;
+import mage.filter.FilterCard;
+import mage.filter.FilterPermanent;
+import mage.filter.common.FilterControlledPermanent;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+import mage.game.permanent.Permanent;
+
+import java.util.UUID;
+
+/**
+ *
+ * @author htrajan
+ */
+public final class SanctumOfAll extends CardImpl {
+
+    private static final FilterPermanent filter = new FilterControlledPermanent();
+    private static final FilterCard filterCard = new FilterCard("a Shrine card");
+
+    static final PermanentsOnBattlefieldCount count = new PermanentsOnBattlefieldCount(filter);
+
+    static {
+        filter.add(SubType.SHRINE.getPredicate());
+        filterCard.add(SubType.SHRINE.getPredicate());
+    }
+
+    public SanctumOfAll(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}{U}{B}{R}{G}");
+        
+        this.addSuperType(SuperType.LEGENDARY);
+        this.subtype.add(SubType.SHRINE);
+
+        // At the beginning of your upkeep, you may search your library and/or graveyard for a Shrine card and put it onto the battlefield. If you search your library this way, shuffle it.
+        this.addAbility(new BeginningOfUpkeepTriggeredAbility(new SearchLibraryGraveyardPutOntoBattlefieldEffect(filterCard), TargetController.YOU, true));
+
+        // If an ability of another Shrine you control triggers while you control six or more Shrines, that ability triggers an additional time.
+        this.addAbility(new SimpleStaticAbility(new SanctumOfAllTriggerEffect()).addHint(new ValueHint("Shrines you control", count)));
+    }
+
+    private SanctumOfAll(final SanctumOfAll card) {
+        super(card);
+    }
+
+    @Override
+    public SanctumOfAll copy() {
+        return new SanctumOfAll(this);
+    }
+}
+
+class SanctumOfAllTriggerEffect extends ReplacementEffectImpl {
+
+    SanctumOfAllTriggerEffect() {
+        super(Duration.WhileOnBattlefield, Outcome.Benefit);
+        staticText = "if an ability of another Shrine you control triggers while you control six or more Shrines, that ability triggers an additional time";
+    }
+
+    private SanctumOfAllTriggerEffect(SanctumOfAllTriggerEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public SanctumOfAllTriggerEffect copy() {
+        return new SanctumOfAllTriggerEffect(this);
+    }
+
+    @Override
+    public boolean replaceEvent(GameEvent event, Ability source, Game game) {
+        event.setAmount(event.getAmount() + 1);
+        return false;
+    }
+
+    @Override
+    public boolean checksEventType(GameEvent event, Game game) {
+        return event.getType() == GameEvent.EventType.NUMBER_OF_TRIGGERS;
+    }
+
+    @Override
+    public boolean applies(GameEvent event, Ability source, Game game) {
+        // Only triggers of the controller of Sanctum of All
+        if (source.isControlledBy(event.getPlayerId())) {
+            // Only trigger while you control six or more Shrines
+            int numShrines = SanctumOfAll.count.calculate(game, source, this);
+            if (numShrines >= 6) {
+                // Only for triggers of Shrines
+                Permanent permanent = game.getPermanent(event.getSourceId());
+                return permanent != null && permanent.hasSubtype(SubType.SHRINE, game);
+            }
+        }
+        return false;
+    }
+}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/s/SanctumOfCalmWaters.java b/Mage.Sets/src/mage/cards/s/SanctumOfCalmWaters.java
index e07cc5d225..a132be5050 100644
--- a/Mage.Sets/src/mage/cards/s/SanctumOfCalmWaters.java
+++ b/Mage.Sets/src/mage/cards/s/SanctumOfCalmWaters.java
@@ -5,12 +5,14 @@ import mage.abilities.common.BeginningOfPreCombatMainTriggeredAbility;
 import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.abilities.effects.common.discard.DiscardControllerEffect;
+import mage.abilities.hint.ValueHint;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.SubType;
 import mage.constants.SuperType;
 import mage.constants.TargetController;
+import mage.filter.FilterPermanent;
 import mage.filter.common.FilterControlledPermanent;
 
 import java.util.UUID;
@@ -21,6 +23,13 @@ import java.util.UUID;
 
 public final class SanctumOfCalmWaters extends CardImpl {
 
+    private static final FilterPermanent filter = new FilterControlledPermanent();
+    private static final PermanentsOnBattlefieldCount xValue = new PermanentsOnBattlefieldCount(filter);
+
+    static {
+        filter.add(SubType.SHRINE.getPredicate());
+    }
+
     public SanctumOfCalmWaters(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}");
 
@@ -28,9 +37,10 @@ public final class SanctumOfCalmWaters extends CardImpl {
         this.subtype.add(SubType.SHRINE);
 
         // At the beginning of your precombat main phase, you may draw X cards, where X is the number of Shrines you control. If you do, discard a card.
-        Ability ability = new BeginningOfPreCombatMainTriggeredAbility(new DrawCardSourceControllerEffect(new PermanentsOnBattlefieldCount(new FilterControlledPermanent(SubType.SHRINE)))
-                .setText("At the beginning of your precombat main phase, you may draw X cards, where X is the number of Shrines you control"),
-                TargetController.YOU, true);
+        Ability ability = new BeginningOfPreCombatMainTriggeredAbility(new DrawCardSourceControllerEffect(xValue)
+                .setText("you may draw X cards, where X is the number of Shrines you control"),
+                TargetController.YOU, true)
+                .addHint(new ValueHint("Shrines you control", xValue));
         ability.addEffect(new DiscardControllerEffect(1).setText("If you do, discard a card"));
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/s/SanctumOfFruitfulHarvest.java b/Mage.Sets/src/mage/cards/s/SanctumOfFruitfulHarvest.java
new file mode 100644
index 0000000000..62c8b50374
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/SanctumOfFruitfulHarvest.java
@@ -0,0 +1,57 @@
+package mage.cards.s;
+
+import mage.Mana;
+import mage.abilities.common.BeginningOfPreCombatMainTriggeredAbility;
+import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
+import mage.abilities.effects.mana.DynamicManaEffect;
+import mage.abilities.hint.ValueHint;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.SuperType;
+import mage.constants.TargetController;
+import mage.filter.FilterPermanent;
+import mage.filter.common.FilterControlledPermanent;
+
+import java.util.UUID;
+
+/**
+ *
+ * @author htrajan
+ */
+public final class SanctumOfFruitfulHarvest extends CardImpl {
+
+    private static final FilterPermanent filter = new FilterControlledPermanent();
+    private static final PermanentsOnBattlefieldCount xValue = new PermanentsOnBattlefieldCount(filter);
+
+    static {
+        filter.add(SubType.SHRINE.getPredicate());
+    }
+
+    public SanctumOfFruitfulHarvest(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}");
+        
+        this.addSuperType(SuperType.LEGENDARY);
+        this.subtype.add(SubType.SHRINE);
+
+        // At the beginning of your precombat main phase, add X mana of any one color, where X is the number of Shrines you control.
+        this.addAbility(new BeginningOfPreCombatMainTriggeredAbility(
+                new DynamicManaEffect(
+                        Mana.AnyMana(1),
+                        xValue,
+                        "add X mana of any one color, where X is the number of Shrines you control",
+                        true),
+                TargetController.YOU, false)
+                .addHint(new ValueHint("Shrines you control", xValue)));
+    }
+
+    private SanctumOfFruitfulHarvest(final SanctumOfFruitfulHarvest card) {
+        super(card);
+    }
+
+    @Override
+    public SanctumOfFruitfulHarvest copy() {
+        return new SanctumOfFruitfulHarvest(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/cards/s/SanctumOfShatteredHeights.java b/Mage.Sets/src/mage/cards/s/SanctumOfShatteredHeights.java
index 7bc906a3d5..d6ef9e0495 100644
--- a/Mage.Sets/src/mage/cards/s/SanctumOfShatteredHeights.java
+++ b/Mage.Sets/src/mage/cards/s/SanctumOfShatteredHeights.java
@@ -6,6 +6,7 @@ import mage.abilities.costs.common.DiscardTargetCost;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
 import mage.abilities.effects.common.DamageTargetEffect;
+import mage.abilities.hint.ValueHint;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
@@ -13,9 +14,11 @@ import mage.constants.SubType;
 import mage.constants.SuperType;
 import mage.constants.Zone;
 import mage.filter.FilterCard;
+import mage.filter.FilterPermanent;
 import mage.filter.common.FilterControlledPermanent;
 import mage.filter.predicate.Predicates;
 import mage.target.common.TargetCardInHand;
+import mage.target.common.TargetCreatureOrPlaneswalker;
 
 import java.util.UUID;
 
@@ -26,9 +29,12 @@ import java.util.UUID;
 public final class SanctumOfShatteredHeights extends CardImpl {
 
     private static final FilterCard filter = new FilterCard("a land card or Shrine card");
+    private static final FilterPermanent filterShrinesOnly = new FilterControlledPermanent("Shrine you control");
+    private static final PermanentsOnBattlefieldCount xValue = new PermanentsOnBattlefieldCount(filterShrinesOnly);
 
     static {
         filter.add(Predicates.or(CardType.LAND.getPredicate(), SubType.SHRINE.getPredicate()));
+        filterShrinesOnly.add(SubType.SHRINE.getPredicate());
     }
 
     public SanctumOfShatteredHeights(UUID ownerId, CardSetInfo setInfo) {
@@ -38,10 +44,12 @@ public final class SanctumOfShatteredHeights extends CardImpl {
         this.subtype.add(SubType.SHRINE);
 
         // {1}, Discard a land card or Shrine card: Sanctum of Shattered Heights deals X damage to target creature or planeswalker, where X is the number of Shrines you control.
-        Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(new PermanentsOnBattlefieldCount(new FilterControlledPermanent(SubType.SHRINE)))
+        Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(xValue)
                 .setText("Sanctum of Shattered Heights deals X damage to target creature or planeswalker, where X is the number of Shrines you control"),
-                new ManaCostsImpl("{1}"));
+                new ManaCostsImpl<>("{1}"))
+                .addHint(new ValueHint("Shrines you control", xValue));
         ability.addCost(new DiscardTargetCost(new TargetCardInHand(filter)));
+        ability.addTarget(new TargetCreatureOrPlaneswalker());
         this.addAbility(ability);
     }
 
diff --git a/Mage.Sets/src/mage/cards/s/SanctumOfStoneFangs.java b/Mage.Sets/src/mage/cards/s/SanctumOfStoneFangs.java
new file mode 100644
index 0000000000..2067849ca4
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/SanctumOfStoneFangs.java
@@ -0,0 +1,56 @@
+package mage.cards.s;
+
+import mage.abilities.Ability;
+import mage.abilities.common.BeginningOfPreCombatMainTriggeredAbility;
+import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
+import mage.abilities.effects.common.GainLifeEffect;
+import mage.abilities.effects.common.LoseLifeOpponentsEffect;
+import mage.abilities.hint.ValueHint;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.SuperType;
+import mage.constants.TargetController;
+import mage.filter.FilterPermanent;
+import mage.filter.common.FilterControlledPermanent;
+
+import java.util.UUID;
+
+/**
+ *
+ * @author htrajan
+ */
+public final class SanctumOfStoneFangs extends CardImpl {
+
+    private static final FilterPermanent filter = new FilterControlledPermanent("");
+    private static final PermanentsOnBattlefieldCount xValue = new PermanentsOnBattlefieldCount(filter, null);
+
+    static {
+        filter.add(SubType.SHRINE.getPredicate());
+    }
+
+    public SanctumOfStoneFangs(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}");
+        
+        this.addSuperType(SuperType.LEGENDARY);
+        this.subtype.add(SubType.SHRINE);
+
+        // At the beginning of your precombat main phase, each opponent loses X life and you gain X life, where X is the number of Shrines you control.
+        Ability ability = new BeginningOfPreCombatMainTriggeredAbility(
+                new LoseLifeOpponentsEffect(xValue).setText("each opponent loses X life"),
+                TargetController.YOU, false)
+                .addHint(new ValueHint("Shrines you control", xValue));
+        ability.addEffect(new GainLifeEffect(xValue).setText("and you gain X life, where X is the number of Shrines you control"));
+        this.addAbility(ability);
+    }
+
+    private SanctumOfStoneFangs(final SanctumOfStoneFangs card) {
+        super(card);
+    }
+
+    @Override
+    public SanctumOfStoneFangs copy() {
+        return new SanctumOfStoneFangs(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/cards/s/SanctumOfTranquilLight.java b/Mage.Sets/src/mage/cards/s/SanctumOfTranquilLight.java
new file mode 100644
index 0000000000..50ea1e8b8d
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/SanctumOfTranquilLight.java
@@ -0,0 +1,76 @@
+package mage.cards.s;
+
+import mage.abilities.Ability;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
+import mage.abilities.effects.common.TapTargetEffect;
+import mage.abilities.effects.common.cost.CostModificationSourceEffect;
+import mage.abilities.hint.ValueHint;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.Outcome;
+import mage.constants.SubType;
+import mage.constants.SuperType;
+import mage.constants.Zone;
+import mage.filter.FilterPermanent;
+import mage.filter.common.FilterControlledPermanent;
+import mage.target.common.TargetCreaturePermanent;
+
+import java.util.UUID;
+
+/**
+ *
+ * @author htrajan
+ */
+public final class SanctumOfTranquilLight extends CardImpl {
+
+    private static final FilterPermanent filter = new FilterControlledPermanent("Shrine you control");
+    private static final PermanentsOnBattlefieldCount count = new PermanentsOnBattlefieldCount(filter);
+
+    static {
+        filter.add(SubType.SHRINE.getPredicate());
+    }
+
+    public SanctumOfTranquilLight(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}");
+        
+        this.addSuperType(SuperType.LEGENDARY);
+        this.subtype.add(SubType.SHRINE);
+
+        // {5}{W}: Tap target creature. This ability costs {1} less to activate for each Shrine you control.
+        Ability ability = new SimpleActivatedAbility(new SanctumOfTranquilLightActivatedAbility());
+        ability.addTarget(new TargetCreaturePermanent());
+        this.addAbility(ability);
+        this.addAbility(new SimpleStaticAbility(Zone.ALL, new CostModificationSourceEffect(Duration.EndOfGame, Outcome.Benefit, SanctumOfTranquilLightActivatedAbility.class, count, true).setText(""))
+                .addHint(new ValueHint("Shrines you control", count)));
+    }
+
+    private SanctumOfTranquilLight(final SanctumOfTranquilLight card) {
+        super(card);
+    }
+
+    @Override
+    public SanctumOfTranquilLight copy() {
+        return new SanctumOfTranquilLight(this);
+    }
+}
+
+class SanctumOfTranquilLightActivatedAbility extends SimpleActivatedAbility {
+
+    SanctumOfTranquilLightActivatedAbility() {
+        super(new TapTargetEffect().setText("target creature. This ability costs {1} less to activate for each Shrine you control"), new ManaCostsImpl<>("{5}{W}"));
+    }
+
+    private SanctumOfTranquilLightActivatedAbility(SanctumOfTranquilLightActivatedAbility ability) {
+        super(ability);
+    }
+
+    @Override
+    public SanctumOfTranquilLightActivatedAbility copy() {
+        return new SanctumOfTranquilLightActivatedAbility(this);
+    }
+}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 6cff003ba8..8bb6310bfc 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -217,8 +217,12 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Run Afoul", 201, Rarity.COMMON, mage.cards.r.RunAfoul.class));
         cards.add(new SetCardInfo("Runed Halo", 32, Rarity.RARE, mage.cards.r.RunedHalo.class));
         cards.add(new SetCardInfo("Sabertooth Mauler", 202, Rarity.COMMON, mage.cards.s.SabertoothMauler.class));
+        cards.add(new SetCardInfo("Sanctum of All", 225, Rarity.RARE, mage.cards.s.SanctumOfAll.class));
         cards.add(new SetCardInfo("Sanctum of Calm Waters", 68, Rarity.UNCOMMON, mage.cards.s.SanctumOfCalmWaters.class));
+        cards.add(new SetCardInfo("Sanctum of Fruitful Harvest", 203, Rarity.UNCOMMON, mage.cards.s.SanctumOfFruitfulHarvest.class));
         cards.add(new SetCardInfo("Sanctum of Shattered Heights", 157, Rarity.UNCOMMON, mage.cards.s.SanctumOfShatteredHeights.class));
+        cards.add(new SetCardInfo("Sanctum of Stone Fangs", 120, Rarity.UNCOMMON, mage.cards.s.SanctumOfStoneFangs.class));
+        cards.add(new SetCardInfo("Sanctum of Tranquil Light", 33, Rarity.UNCOMMON, mage.cards.s.SanctumOfTranquilLight.class));
         cards.add(new SetCardInfo("Sanguine Indulgence", 121, Rarity.COMMON, mage.cards.s.SanguineIndulgence.class));
         cards.add(new SetCardInfo("Scavenging Ooze", 204, Rarity.RARE, mage.cards.s.ScavengingOoze.class));
         cards.add(new SetCardInfo("Scorching Dragonfire", 158, Rarity.COMMON, mage.cards.s.ScorchingDragonfire.class));
diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/CostModificationSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/CostModificationSourceEffect.java
new file mode 100644
index 0000000000..c1be451a4c
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/effects/common/cost/CostModificationSourceEffect.java
@@ -0,0 +1,64 @@
+package mage.abilities.effects.common.cost;
+
+import mage.abilities.Ability;
+import mage.abilities.Mode;
+import mage.abilities.dynamicvalue.DynamicValue;
+import mage.constants.CostModificationType;
+import mage.constants.Duration;
+import mage.constants.Outcome;
+import mage.game.Game;
+import mage.players.Player;
+import mage.util.CardUtil;
+
+import java.util.UUID;
+
+public class CostModificationSourceEffect extends CostModificationEffectImpl {
+
+    private final Class<? extends Ability> abilityType;
+    private final DynamicValue value;
+    private final boolean reducesCost;
+
+    public CostModificationSourceEffect(Duration duration, Outcome outcome, Class<? extends Ability> abilityType, DynamicValue value, boolean reducesCost) {
+        super(duration, outcome, reducesCost ? CostModificationType.REDUCE_COST : CostModificationType.INCREASE_COST);
+        this.abilityType = abilityType;
+        this.value = value;
+        this.reducesCost = reducesCost;
+        this.staticText = "this ability costs {1} " + (reducesCost ? "less" : "more") + " to activate for each " + value.getMessage();
+    }
+
+    private CostModificationSourceEffect(CostModificationSourceEffect effect) {
+        super(effect);
+        this.abilityType = effect.abilityType;
+        this.value = effect.value;
+        this.reducesCost = effect.reducesCost;
+    }
+
+    @Override
+    public CostModificationSourceEffect copy() {
+        return new CostModificationSourceEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source, Ability abilityToModify) {
+        Player controller = game.getPlayer(source.getControllerId());
+        if (controller != null) {
+            int count = value.calculate(game, source, this);
+            if (reducesCost) {
+                CardUtil.reduceCost(abilityToModify, count);
+            } else {
+                CardUtil.increaseCost(abilityToModify, count);
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public boolean applies(Ability abilityToModify, Ability source, Game game) {
+        return (abilityToModify.getClass().isAssignableFrom(abilityType)) && abilityToModify.getSourceId().equals(source.getSourceId());
+    }
+
+    @Override
+    public String getText(Mode mode) {
+        return super.getText(mode);
+    }
+}

From aeef4e1193b786727ebba915f4eefdcc6b6b423a Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Thu, 25 Jun 2020 09:52:54 +0400
Subject: [PATCH 502/586] Fixed some tokens image

---
 .../src/main/resources/card-pictures-tok.txt  |  2 +-
 .../mage/game/permanent/token/BeastToken.java | 11 ++-----
 .../mage/game/permanent/token/CatToken.java   | 27 +++-------------
 .../mage/game/permanent/token/CatToken2.java  | 15 ++++-----
 .../token/ElementalTokenWithHaste.java        | 32 +++++++++----------
 5 files changed, 28 insertions(+), 59 deletions(-)

diff --git a/Mage.Client/src/main/resources/card-pictures-tok.txt b/Mage.Client/src/main/resources/card-pictures-tok.txt
index 5ba4ee8863..068eead09f 100644
--- a/Mage.Client/src/main/resources/card-pictures-tok.txt
+++ b/Mage.Client/src/main/resources/card-pictures-tok.txt
@@ -373,7 +373,7 @@
 |Generate|TOK:C16|Worm||
 |Generate|TOK:C16|Zombie||
 |Generate|TOK:C17|Bat||
-|Generate|TOK:C17|Cat||
+|Generate|TOK:C17|Cat|||CatToken|
 |Generate|TOK:C17|Cat Dragon|||WasitoraCatDragonToken|
 |Generate|TOK:C17|Cat Warrior||
 |Generate|TOK:C17|Dragon|1||DragonToken|
diff --git a/Mage/src/main/java/mage/game/permanent/token/BeastToken.java b/Mage/src/main/java/mage/game/permanent/token/BeastToken.java
index 8e3a85a3bb..b4452e1c24 100644
--- a/Mage/src/main/java/mage/game/permanent/token/BeastToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/BeastToken.java
@@ -4,21 +4,13 @@ import mage.MageInt;
 import mage.constants.CardType;
 import mage.constants.SubType;
 
-import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.List;
 
 /**
  * @author BetaSteward_at_googlemail.com
  */
 public final class BeastToken extends TokenImpl {
 
-    static final private List<String> tokenImageSets = new ArrayList<>();
-
-    static {
-        tokenImageSets.addAll(Arrays.asList("C14", "LRW", "M15", "M14", "DDL", "M13", "M12", "DD3GVL", "NPH", "M11", "M10", "EVE", "MM3", "CMA", "E01", "C19", "IKO"));
-    }
-
     public BeastToken() {
         this(null, 0);
     }
@@ -29,7 +21,6 @@ public final class BeastToken extends TokenImpl {
 
     public BeastToken(String setCode, int tokenType) {
         super("Beast", "3/3 green Beast creature token");
-        availableImageSetCodes = tokenImageSets;
         setOriginalExpansionSetCode(setCode);
         cardType.add(CardType.CREATURE);
         color.setGreen(true);
@@ -37,11 +28,13 @@ public final class BeastToken extends TokenImpl {
         power = new MageInt(3);
         toughness = new MageInt(3);
 
+        availableImageSetCodes = Arrays.asList("C14", "LRW", "M15", "M14", "DDL", "M13", "M12", "DD3GVL", "NPH", "M11", "M10", "EVE", "MM3", "CMA", "E01", "C19", "IKO");
     }
 
     @Override
     public void setExpansionSetCodeForImage(String code) {
         super.setExpansionSetCodeForImage(code);
+
         if (getOriginalExpansionSetCode().equals("M15")) {
             this.setTokenType(2);
         }
diff --git a/Mage/src/main/java/mage/game/permanent/token/CatToken.java b/Mage/src/main/java/mage/game/permanent/token/CatToken.java
index bfc82b3969..7848d6cff8 100644
--- a/Mage/src/main/java/mage/game/permanent/token/CatToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/CatToken.java
@@ -1,43 +1,25 @@
-
-
 package mage.game.permanent.token;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
 import mage.MageInt;
 import mage.constants.CardType;
 import mage.constants.SubType;
 
+import java.util.Arrays;
+
 /**
- *
  * @author LoneFox
  */
 public final class CatToken extends TokenImpl {
 
-    static final private List<String> tokenImageSets = new ArrayList<>();
-
-    static {
-        tokenImageSets.addAll(Arrays.asList("SOM", "M13", "M14", "C14", "C15", "C17"));
-    }
-
     public CatToken() {
-        this(null, 0);
-    }
-
-    public CatToken(String setCode) {
-        this(setCode, 0);
-    }
-
-    public CatToken(String setCode, int tokenType) {
         super("Cat", "2/2 white Cat creature token");
-        availableImageSetCodes = tokenImageSets;
-        setOriginalExpansionSetCode(setCode);
         cardType.add(CardType.CREATURE);
         color.setWhite(true);
         subtype.add(SubType.CAT);
         power = new MageInt(2);
         toughness = new MageInt(2);
+
+        availableImageSetCodes = Arrays.asList("MBP", "C14", "C15", "C17", "C18", "M13", "M14", "MBS", "SOM");
     }
 
     public CatToken(final CatToken token) {
@@ -47,5 +29,4 @@ public final class CatToken extends TokenImpl {
     public CatToken copy() {
         return new CatToken(this);
     }
-    
 }
diff --git a/Mage/src/main/java/mage/game/permanent/token/CatToken2.java b/Mage/src/main/java/mage/game/permanent/token/CatToken2.java
index f751d049e0..3e9ce3a75d 100644
--- a/Mage/src/main/java/mage/game/permanent/token/CatToken2.java
+++ b/Mage/src/main/java/mage/game/permanent/token/CatToken2.java
@@ -1,5 +1,3 @@
-
-
 package mage.game.permanent.token;
 
 import mage.MageInt;
@@ -7,26 +5,25 @@ import mage.abilities.keyword.LifelinkAbility;
 import mage.constants.CardType;
 import mage.constants.SubType;
 
+import java.util.Arrays;
+
 /**
- *
  * @author fireshoes
  */
 public final class CatToken2 extends TokenImpl {
 
     public CatToken2() {
-        this((String)null);
-    }
-    
-    public CatToken2(String setCode) {
         super("Cat", "1/1 white Cat creature token with lifelink");
-        setOriginalExpansionSetCode("AKH");
         cardType.add(CardType.CREATURE);
         color.setWhite(true);
         subtype.add(SubType.CAT);
         power = new MageInt(1);
         toughness = new MageInt(1);
         addAbility(LifelinkAbility.getInstance());
+
+        availableImageSetCodes = Arrays.asList("AKH", "M19", "IKO");
     }
+
     public CatToken2(final CatToken2 token) {
         super(token);
     }
@@ -34,5 +31,5 @@ public final class CatToken2 extends TokenImpl {
     public CatToken2 copy() {
         return new CatToken2(this);
     }
-    
+
 }
diff --git a/Mage/src/main/java/mage/game/permanent/token/ElementalTokenWithHaste.java b/Mage/src/main/java/mage/game/permanent/token/ElementalTokenWithHaste.java
index f916272c55..b01be1d548 100644
--- a/Mage/src/main/java/mage/game/permanent/token/ElementalTokenWithHaste.java
+++ b/Mage/src/main/java/mage/game/permanent/token/ElementalTokenWithHaste.java
@@ -5,24 +5,15 @@ import mage.abilities.keyword.HasteAbility;
 import mage.constants.CardType;
 import mage.constants.SubType;
 
-import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.List;
 
 /**
  * @author magenoxx
  */
 public final class ElementalTokenWithHaste extends TokenImpl {
 
-    static final private List<String> tokenImageSets = new ArrayList<>();
-
-    static {
-        tokenImageSets.addAll(Arrays.asList("C20", "MBP", "OGW", "SOK", "MRD"));
-    }
-
     public ElementalTokenWithHaste() {
         super("Elemental", "3/1 red Elemental creature token with haste");
-        availableImageSetCodes = tokenImageSets;
         cardType.add(CardType.CREATURE);
         color.setRed(true);
         subtype.add(SubType.ELEMENTAL);
@@ -30,6 +21,21 @@ public final class ElementalTokenWithHaste extends TokenImpl {
         toughness = new MageInt(1);
         this.addAbility(HasteAbility.getInstance());
 
+        availableImageSetCodes = Arrays.asList("C20", "MBP", "OGW", "SOK", "MRD");
+    }
+
+    public ElementalTokenWithHaste(final ElementalTokenWithHaste token) {
+        super(token);
+    }
+
+    public ElementalTokenWithHaste copy() {
+        return new ElementalTokenWithHaste(this);
+    }
+
+    @Override
+    public void setExpansionSetCodeForImage(String code) {
+        super.setExpansionSetCodeForImage(code);
+
         if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("OGW")) {
             setTokenType(2);
         }
@@ -40,12 +46,4 @@ public final class ElementalTokenWithHaste extends TokenImpl {
             setTokenType(1);
         }
     }
-
-    public ElementalTokenWithHaste(final ElementalTokenWithHaste token) {
-        super(token);
-    }
-
-    public ElementalTokenWithHaste copy() {
-        return new ElementalTokenWithHaste(this);
-    }
 }

From f589d861f4ccb728e9187b0c3c0b9392e064f09a Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Thu, 25 Jun 2020 10:08:55 +0400
Subject: [PATCH 503/586] [M21] Terror of the Peaks - fixed missing target for
 damage effect

---
 Mage.Sets/src/mage/cards/t/TerrorOfThePeaks.java | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/Mage.Sets/src/mage/cards/t/TerrorOfThePeaks.java b/Mage.Sets/src/mage/cards/t/TerrorOfThePeaks.java
index 705677402b..fc080a11e4 100644
--- a/Mage.Sets/src/mage/cards/t/TerrorOfThePeaks.java
+++ b/Mage.Sets/src/mage/cards/t/TerrorOfThePeaks.java
@@ -18,6 +18,7 @@ import mage.game.Game;
 import mage.game.events.GameEvent;
 import mage.game.permanent.Permanent;
 import mage.target.Target;
+import mage.target.common.TargetAnyTarget;
 
 import java.util.Collection;
 import java.util.UUID;
@@ -116,7 +117,9 @@ class TerrorOfThePeaksTriggeredAbility extends EntersBattlefieldAllTriggeredAbil
             return false;
         }
         this.getEffects().clear();
+        this.getTargets().clear();
         this.addEffect(new DamageTargetEffect(permanent.getPower().getValue()));
+        this.addTarget(new TargetAnyTarget());
         return true;
     }
 

From 7750dde6b87e36c71d95ed1dc03f7ff9eb4dca5e Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 25 Jun 2020 08:38:54 -0400
Subject: [PATCH 504/586] Implemented Emiel the Blessed

---
 .../src/mage/cards/e/EmielTheBlessed.java     | 96 +++++++++++++++++++
 Mage.Sets/src/mage/sets/Jumpstart.java        |  1 +
 2 files changed, 97 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/e/EmielTheBlessed.java

diff --git a/Mage.Sets/src/mage/cards/e/EmielTheBlessed.java b/Mage.Sets/src/mage/cards/e/EmielTheBlessed.java
new file mode 100644
index 0000000000..8210d4f6aa
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/e/EmielTheBlessed.java
@@ -0,0 +1,96 @@
+package mage.cards.e;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.mana.GenericManaCost;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.DoIfCostPaid;
+import mage.abilities.effects.common.ExileTargetForSourceEffect;
+import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.*;
+import mage.counters.CounterType;
+import mage.filter.FilterPermanent;
+import mage.filter.StaticFilters;
+import mage.filter.common.FilterControlledCreaturePermanent;
+import mage.filter.predicate.permanent.AnotherPredicate;
+import mage.game.Game;
+import mage.game.permanent.Permanent;
+import mage.target.TargetPermanent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class EmielTheBlessed extends CardImpl {
+
+    private static final FilterPermanent filter
+            = new FilterControlledCreaturePermanent("another target creature you control");
+
+    static {
+        filter.add(AnotherPredicate.instance);
+    }
+
+    public EmielTheBlessed(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{W}");
+
+        this.addSuperType(SuperType.LEGENDARY);
+        this.subtype.add(SubType.UNICORN);
+        this.power = new MageInt(4);
+        this.toughness = new MageInt(4);
+
+        // {3}: Exile another target creature you control, then return it to the battlefield under its owner's control.
+        Ability ability = new SimpleActivatedAbility(new ExileTargetForSourceEffect(), new GenericManaCost(3));
+        ability.addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect());
+        ability.addTarget(new TargetPermanent(filter));
+        this.addAbility(ability);
+
+        // Whenever another creature enters the battlefield under your control, you may pay {G/W}. If you do, put a +1/+1 counter on it. If it's a Unicorn, put two +1/+1 counters on it instead.
+        this.addAbility(new EntersBattlefieldControlledTriggeredAbility(
+                Zone.BATTLEFIELD, new DoIfCostPaid(new EmielTheBlessedEffect(), new ManaCostsImpl<>("{G/W}")),
+                StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE, false, SetTargetPointer.PERMANENT,
+                "Whenever another creature enters the battlefield under your control, you may pay {G/W}. " +
+                        "If you do, put a +1/+1 counter on it. If it's a Unicorn, put two +1/+1 counters on it instead."
+        ));
+    }
+
+    private EmielTheBlessed(final EmielTheBlessed card) {
+        super(card);
+    }
+
+    @Override
+    public EmielTheBlessed copy() {
+        return new EmielTheBlessed(this);
+    }
+}
+
+class EmielTheBlessedEffect extends OneShotEffect {
+
+    EmielTheBlessedEffect() {
+        super(Outcome.Benefit);
+    }
+
+    private EmielTheBlessedEffect(final EmielTheBlessedEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public EmielTheBlessedEffect copy() {
+        return new EmielTheBlessedEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
+        if (permanent == null) {
+            return false;
+        }
+        int counters = permanent.hasSubtype(SubType.UNICORN, game) ? 2 : 1;
+        return permanent.addCounters(CounterType.P1P1.createInstance(counters), source, game);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index 87de816a49..9a9f252dd2 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -148,6 +148,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Elemental Uprising", 390, Rarity.COMMON, mage.cards.e.ElementalUprising.class));
         cards.add(new SetCardInfo("Elvish Archdruid", 391, Rarity.RARE, mage.cards.e.ElvishArchdruid.class));
         cards.add(new SetCardInfo("Emancipation Angel", 102, Rarity.UNCOMMON, mage.cards.e.EmancipationAngel.class));
+        cards.add(new SetCardInfo("Emiel the Blessed", 3, Rarity.MYTHIC, mage.cards.e.EmielTheBlessed.class));
         cards.add(new SetCardInfo("Enlarge", 392, Rarity.UNCOMMON, mage.cards.e.Enlarge.class));
         cards.add(new SetCardInfo("Entomber Exarch", 227, Rarity.UNCOMMON, mage.cards.e.EntomberExarch.class));
         cards.add(new SetCardInfo("Erratic Visionary", 150, Rarity.COMMON, mage.cards.e.ErraticVisionary.class));

From 1fec71920e9b06af3b2d21a46adae84d60c3737e Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 25 Jun 2020 09:01:05 -0400
Subject: [PATCH 505/586] Implemented Scholar of the Lost Trove

---
 .../mage/cards/s/ScholarOfTheLostTrove.java   | 154 ++++++++++++++++++
 Mage.Sets/src/mage/sets/Jumpstart.java        |   1 +
 2 files changed, 155 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/s/ScholarOfTheLostTrove.java

diff --git a/Mage.Sets/src/mage/cards/s/ScholarOfTheLostTrove.java b/Mage.Sets/src/mage/cards/s/ScholarOfTheLostTrove.java
new file mode 100644
index 0000000000..d518ad24f6
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/ScholarOfTheLostTrove.java
@@ -0,0 +1,154 @@
+package mage.cards.s;
+
+import mage.MageInt;
+import mage.MageObjectReference;
+import mage.abilities.Ability;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.effects.ContinuousEffect;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.ReplacementEffectImpl;
+import mage.abilities.keyword.FlyingAbility;
+import mage.cards.Card;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.*;
+import mage.filter.FilterCard;
+import mage.filter.predicate.Predicates;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+import mage.game.events.ZoneChangeEvent;
+import mage.players.Player;
+import mage.target.common.TargetCardInYourGraveyard;
+import mage.target.targetpointer.FixedTarget;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class ScholarOfTheLostTrove extends CardImpl {
+
+    private static final FilterCard filter
+            = new FilterCard("instant, sorcery, or artifact card from your graveyard");
+
+    static {
+        filter.add(Predicates.or(
+                CardType.INSTANT.getPredicate(),
+                CardType.SORCERY.getPredicate(),
+                CardType.ARTIFACT.getPredicate()
+        ));
+    }
+
+    public ScholarOfTheLostTrove(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{U}{U}");
+
+        this.subtype.add(SubType.SPHINX);
+        this.power = new MageInt(5);
+        this.toughness = new MageInt(5);
+
+        // Flying
+        this.addAbility(FlyingAbility.getInstance());
+
+        // When Scholar of the Lost Trove enters the battlefield, you may cast target instant, sorcery, or artifact card from your graveyard without paying its mana cost. If an instant or sorcery spell cast this way would be put into your graveyard this turn, exile it instead.
+        Ability ability = new EntersBattlefieldTriggeredAbility(new ScholarOfTheLostTroveEffect(), true);
+        ability.addTarget(new TargetCardInYourGraveyard(filter));
+        this.addAbility(ability);
+    }
+
+    private ScholarOfTheLostTrove(final ScholarOfTheLostTrove card) {
+        super(card);
+    }
+
+    @Override
+    public ScholarOfTheLostTrove copy() {
+        return new ScholarOfTheLostTrove(this);
+    }
+}
+
+class ScholarOfTheLostTroveEffect extends OneShotEffect {
+
+    ScholarOfTheLostTroveEffect() {
+        super(Outcome.PlayForFree);
+        this.staticText = "you may cast target instant, sorcery, or artifact card from your graveyard without paying its mana cost. " +
+                "If an instant or sorcery spell cast this way would be put into your graveyard this turn, exile it instead";
+    }
+
+    private ScholarOfTheLostTroveEffect(final ScholarOfTheLostTroveEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public ScholarOfTheLostTroveEffect copy() {
+        return new ScholarOfTheLostTroveEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Player controller = game.getPlayer(source.getControllerId());
+        if (controller == null) {
+            return false;
+        }
+        Card card = game.getCard(this.getTargetPointer().getFirst(game, source));
+        if (card == null) {
+            return true;
+        }
+        if (!controller.chooseUse(Outcome.PlayForFree, "Cast " + card.getLogName() + '?', source, game)) {
+            return true;
+        }
+        game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE);
+        Boolean cardWasCast = controller.cast(controller.chooseAbilityForCast(card, game, true),
+                game, true, new MageObjectReference(source.getSourceObject(game), game));
+        game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null);
+        if (!cardWasCast || !card.isInstantOrSorcery()) {
+            return true;
+        }
+        ContinuousEffect effect = new ScholarOfTheLostTroveReplacementEffect(card.getId());
+        effect.setTargetPointer(new FixedTarget(card.getId(), game.getState().getZoneChangeCounter(card.getId())));
+        game.addEffect(effect, source);
+        return true;
+    }
+}
+
+class ScholarOfTheLostTroveReplacementEffect extends ReplacementEffectImpl {
+
+    private final UUID cardId;
+
+    ScholarOfTheLostTroveReplacementEffect(UUID cardId) {
+        super(Duration.EndOfTurn, Outcome.Exile);
+        this.cardId = cardId;
+        staticText = "If an instant or sorcery spell cast this way would be put into your graveyard this turn, exile it instead";
+    }
+
+    private ScholarOfTheLostTroveReplacementEffect(final ScholarOfTheLostTroveReplacementEffect effect) {
+        super(effect);
+        this.cardId = effect.cardId;
+    }
+
+    @Override
+    public ScholarOfTheLostTroveReplacementEffect copy() {
+        return new ScholarOfTheLostTroveReplacementEffect(this);
+    }
+
+    @Override
+    public boolean replaceEvent(GameEvent event, Ability source, Game game) {
+        Player controller = game.getPlayer(source.getControllerId());
+        Card card = game.getCard(this.cardId);
+        if (controller == null || card == null) {
+            return false;
+        }
+        controller.moveCards(card, Zone.EXILED, source, game);
+        return true;
+    }
+
+    @Override
+    public boolean checksEventType(GameEvent event, Game game) {
+        return event.getType() == GameEvent.EventType.ZONE_CHANGE;
+    }
+
+    @Override
+    public boolean applies(GameEvent event, Ability source, Game game) {
+        ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
+        return zEvent.getToZone() == Zone.GRAVEYARD
+                && zEvent.getTargetId().equals(this.cardId);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index 9a9f252dd2..dbc4bf9223 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -373,6 +373,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Sarkhan's Unsealing", 361, Rarity.RARE, mage.cards.s.SarkhansUnsealing.class));
         cards.add(new SetCardInfo("Savage Stomp", 427, Rarity.UNCOMMON, mage.cards.s.SavageStomp.class));
         cards.add(new SetCardInfo("Scarecrone", 482, Rarity.RARE, mage.cards.s.Scarecrone.class));
+        cards.add(new SetCardInfo("Scholar of the Lost Trove", 14, Rarity.RARE, mage.cards.s.ScholarOfTheLostTrove.class));
         cards.add(new SetCardInfo("Scourge of Nel Toth", 274, Rarity.RARE, mage.cards.s.ScourgeOfNelToth.class));
         cards.add(new SetCardInfo("Scroll of Avacyn", 483, Rarity.COMMON, mage.cards.s.ScrollOfAvacyn.class));
         cards.add(new SetCardInfo("Scrounging Bandar", 428, Rarity.COMMON, mage.cards.s.ScroungingBandar.class));

From a5f9b8fb640b769878e8d9fca40dc015da0ab497 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 25 Jun 2020 09:22:09 -0400
Subject: [PATCH 506/586] Implemented Sethron, Hurloon General

---
 .../mage/cards/s/SethronHurloonGeneral.java   | 76 +++++++++++++++++++
 Mage.Sets/src/mage/sets/Jumpstart.java        |  1 +
 .../game/permanent/token/MinotaurToken.java   | 29 +++++++
 3 files changed, 106 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/s/SethronHurloonGeneral.java
 create mode 100644 Mage/src/main/java/mage/game/permanent/token/MinotaurToken.java

diff --git a/Mage.Sets/src/mage/cards/s/SethronHurloonGeneral.java b/Mage.Sets/src/mage/cards/s/SethronHurloonGeneral.java
new file mode 100644
index 0000000000..11260153d0
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/SethronHurloonGeneral.java
@@ -0,0 +1,76 @@
+package mage.cards.s;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.effects.common.CreateTokenEffect;
+import mage.abilities.effects.common.continuous.BoostControlledEffect;
+import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
+import mage.abilities.keyword.HasteAbility;
+import mage.abilities.keyword.MenaceAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+import mage.constants.SuperType;
+import mage.filter.FilterPermanent;
+import mage.filter.common.FilterControlledPermanent;
+import mage.filter.common.FilterCreaturePermanent;
+import mage.filter.predicate.Predicates;
+import mage.filter.predicate.permanent.TokenPredicate;
+import mage.game.permanent.token.MinotaurToken;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class SethronHurloonGeneral extends CardImpl {
+
+    private static final FilterPermanent filter = new FilterControlledPermanent(
+            SubType.MINOTAUR, "{this} or another nontoken Minotaur"
+    );
+    private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent(SubType.MINOTAUR, "");
+    private static final FilterPermanent filter3 = new FilterPermanent(SubType.MINOTAUR, "");
+
+    static {
+        filter.add(Predicates.not(TokenPredicate.instance));
+    }
+
+    public SethronHurloonGeneral(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{R}");
+
+        this.addSuperType(SuperType.LEGENDARY);
+        this.subtype.add(SubType.MINOTAUR);
+        this.subtype.add(SubType.WARRIOR);
+        this.power = new MageInt(4);
+        this.toughness = new MageInt(4);
+
+        // Whenever Sethron, Hurloon General or another nontoken Minotaur enters the battlefield under your control, create a 2/3 red Minotaur creature token.
+        this.addAbility(new EntersBattlefieldControlledTriggeredAbility(new CreateTokenEffect(new MinotaurToken()), filter));
+
+        // {2}{B/R}: Minotaurs you control get +1/+0 and gain menace and haste until end of turn.
+        Ability ability = new SimpleActivatedAbility(new BoostControlledEffect(
+                1, 0, Duration.EndOfTurn, filter2
+        ).setText("Minotaurs you control get +1/+0"), new ManaCostsImpl("{2}{B/R}"));
+        ability.addEffect(new GainAbilityControlledEffect(
+                new MenaceAbility(), Duration.EndOfTurn, filter3
+        ).setText("and gain menace"));
+        ability.addEffect(new GainAbilityControlledEffect(
+                HasteAbility.getInstance(), Duration.EndOfTurn, filter3
+        ).setText("and haste until end of turn"));
+        this.addAbility(ability);
+    }
+
+    private SethronHurloonGeneral(final SethronHurloonGeneral card) {
+        super(card);
+    }
+
+    @Override
+    public SethronHurloonGeneral copy() {
+        return new SethronHurloonGeneral(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index dbc4bf9223..7337c1f08e 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -385,6 +385,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Sengir Vampire", 275, Rarity.UNCOMMON, mage.cards.s.SengirVampire.class));
         cards.add(new SetCardInfo("Serendib Efreet", 175, Rarity.RARE, mage.cards.s.SerendibEfreet.class));
         cards.add(new SetCardInfo("Serra Angel", 132, Rarity.UNCOMMON, mage.cards.s.SerraAngel.class));
+        cards.add(new SetCardInfo("Sethron, Hurloon General", 25, Rarity.RARE, mage.cards.s.SethronHurloonGeneral.class));
         cards.add(new SetCardInfo("Settle the Score", 276, Rarity.UNCOMMON, mage.cards.s.SettleTheScore.class));
         cards.add(new SetCardInfo("Shambling Goblin", 277, Rarity.COMMON, mage.cards.s.ShamblingGoblin.class));
         cards.add(new SetCardInfo("Sharding Sphinx", 176, Rarity.RARE, mage.cards.s.ShardingSphinx.class));
diff --git a/Mage/src/main/java/mage/game/permanent/token/MinotaurToken.java b/Mage/src/main/java/mage/game/permanent/token/MinotaurToken.java
new file mode 100644
index 0000000000..2e9d4e7d90
--- /dev/null
+++ b/Mage/src/main/java/mage/game/permanent/token/MinotaurToken.java
@@ -0,0 +1,29 @@
+package mage.game.permanent.token;
+
+import mage.MageInt;
+import mage.ObjectColor;
+import mage.constants.CardType;
+import mage.constants.SubType;
+
+/**
+ * @author TheElk801
+ */
+public final class MinotaurToken extends TokenImpl {
+
+    public MinotaurToken() {
+        super("Minotaur", "2/3 red Minotaur creature token");
+        cardType.add(CardType.CREATURE);
+        color.setColor(ObjectColor.RED);
+        subtype.add(SubType.MINOTAUR);
+        power = new MageInt(2);
+        toughness = new MageInt(3);
+    }
+
+    private MinotaurToken(final MinotaurToken token) {
+        super(token);
+    }
+
+    public MinotaurToken copy() {
+        return new MinotaurToken(this);
+    }
+}

From a003f95c35d80e6930f9742df078046cda90dce9 Mon Sep 17 00:00:00 2001
From: gp66 <garyprince66@hotmail.com>
Date: Thu, 25 Jun 2020 08:46:27 -0500
Subject: [PATCH 507/586] m20 demon token

---
 Mage/src/main/java/mage/game/permanent/token/DemonToken.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Mage/src/main/java/mage/game/permanent/token/DemonToken.java b/Mage/src/main/java/mage/game/permanent/token/DemonToken.java
index 9f494ab5cb..413adf5614 100644
--- a/Mage/src/main/java/mage/game/permanent/token/DemonToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/DemonToken.java
@@ -22,7 +22,7 @@ public final class DemonToken extends TokenImpl {
         power = new MageInt(5);
         toughness = new MageInt(5);
         addAbility(FlyingAbility.getInstance());
-        availableImageSetCodes.addAll(Arrays.asList("ISD", "AVR", "C14", "ORI"));
+        availableImageSetCodes.addAll(Arrays.asList("ISD", "AVR", "C14", "ORI", "M20"));
     }
 
     public DemonToken(final DemonToken token) {

From c030779c7bc651cbc14fbfd41ed786eafa3c7cc5 Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Thu, 25 Jun 2020 09:04:56 -0500
Subject: [PATCH 508/586] - Fixed #6714

---
 .../src/mage/cards/b/BenthicExplorers.java    | 26 ++++++++++++++-----
 1 file changed, 20 insertions(+), 6 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/b/BenthicExplorers.java b/Mage.Sets/src/mage/cards/b/BenthicExplorers.java
index 8e9d18a989..9e9ad15128 100644
--- a/Mage.Sets/src/mage/cards/b/BenthicExplorers.java
+++ b/Mage.Sets/src/mage/cards/b/BenthicExplorers.java
@@ -4,7 +4,6 @@ import mage.MageInt;
 import mage.Mana;
 import mage.abilities.Abilities;
 import mage.abilities.Ability;
-import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.Cost;
 import mage.abilities.costs.CostImpl;
 import mage.abilities.costs.common.TapSourceCost;
@@ -22,7 +21,6 @@ import mage.game.permanent.Permanent;
 import mage.players.Player;
 import mage.target.common.TargetLandPermanent;
 import mage.util.CardUtil;
-
 import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
@@ -48,8 +46,10 @@ public final class BenthicExplorers extends CardImpl {
         this.toughness = new MageInt(4);
 
         // {tap}, Untap a tapped land an opponent controls: Add one mana of any type that land could produce.
-        Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BenthicExplorersManaEffect(), new TapSourceCost());
-        ability.addCost(new BenthicExplorersCost(new TargetLandPermanent(filter)));
+        Ability ability = new BenthicExplorersManaAbility();
+        TargetLandPermanent targetOpponentLand = new TargetLandPermanent(filter);
+        targetOpponentLand.setNotTarget(true);  // not a target, it is chosen
+        ability.addCost(new BenthicExplorersCost(targetOpponentLand));
         this.addAbility(ability);
 
     }
@@ -107,6 +107,22 @@ class BenthicExplorersCost extends CostImpl {
 
 }
 
+class BenthicExplorersManaAbility extends ActivatedManaAbilityImpl {
+
+    BenthicExplorersManaAbility() {
+        super(Zone.BATTLEFIELD, new BenthicExplorersManaEffect(), new TapSourceCost());
+    }
+
+    private BenthicExplorersManaAbility(BenthicExplorersManaAbility ability) {
+        super(ability);
+    }
+
+    @Override
+    public BenthicExplorersManaAbility copy() {
+        return new BenthicExplorersManaAbility(this);
+    }
+}
+
 class BenthicExplorersManaEffect extends ManaEffect {
 
     public BenthicExplorersManaEffect() {
@@ -226,7 +242,6 @@ class BenthicExplorersManaEffect extends ManaEffect {
         }
         Permanent land = (Permanent) game.getState().getValue("UntapTargetCost" + source.getSourceId().toString());
         if (land != null) {
-            System.out.println("The land is " + land.getName());
             Abilities<ActivatedManaAbilityImpl> mana = land.getAbilities().getActivatedManaAbilities(Zone.BATTLEFIELD);
             for (ActivatedManaAbilityImpl ability : mana) {
                 if (ability.definesMana(game)) {
@@ -236,7 +251,6 @@ class BenthicExplorersManaEffect extends ManaEffect {
                 }
             }
         }
-        System.out.println("The types : " + types.toString());
         return types;
     }
 

From a20bc6c41441453934502ccba241b806629e08f7 Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Thu, 25 Jun 2020 16:45:38 -0500
Subject: [PATCH 509/586] - Fixed some text issues related to Bug 6675.  Not
 closed because it keep growing...

---
 Mage.Sets/src/mage/cards/l/LightningCoreExcavator.java | 8 ++++----
 Mage.Sets/src/mage/cards/o/OrmosArchiveKeeper.java     | 2 +-
 Mage.Sets/src/mage/cards/w/WitchOfTheMoors.java        | 9 +++++----
 Mage/src/main/java/mage/target/TargetImpl.java         | 5 ++++-
 4 files changed, 14 insertions(+), 10 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/l/LightningCoreExcavator.java b/Mage.Sets/src/mage/cards/l/LightningCoreExcavator.java
index bceff2ac0f..b0ee776149 100644
--- a/Mage.Sets/src/mage/cards/l/LightningCoreExcavator.java
+++ b/Mage.Sets/src/mage/cards/l/LightningCoreExcavator.java
@@ -11,9 +11,9 @@ import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.SubType;
-import mage.target.common.TargetAnyTarget;
-
 import java.util.UUID;
+import mage.filter.common.FilterCreaturePlayerOrPlaneswalker;
+import mage.target.common.TargetAnyTarget;
 
 /**
  * @author TheElk801
@@ -29,11 +29,11 @@ public final class LightningCoreExcavator extends CardImpl {
 
         // {5}, {T}, Sacrifice Lightning-Core Excavator: It deals 3 damage to any target.
         Ability ability = new SimpleActivatedAbility(
-                new DamageTargetEffect(3, "it"), new GenericManaCost(5)
+                new DamageTargetEffect(3), new GenericManaCost(5)
         );
         ability.addCost(new TapSourceCost());
         ability.addCost(new SacrificeSourceCost());
-        ability.addTarget(new TargetAnyTarget());
+        ability.addTarget(new TargetAnyTarget(new FilterCreaturePlayerOrPlaneswalker("any target")));
         this.addAbility(ability);
     }
 
diff --git a/Mage.Sets/src/mage/cards/o/OrmosArchiveKeeper.java b/Mage.Sets/src/mage/cards/o/OrmosArchiveKeeper.java
index b57dbfc34d..23d40fbc68 100644
--- a/Mage.Sets/src/mage/cards/o/OrmosArchiveKeeper.java
+++ b/Mage.Sets/src/mage/cards/o/OrmosArchiveKeeper.java
@@ -111,7 +111,7 @@ class OrmosArchiveKeeperEffect extends ReplacementEffectImpl {
 
 class OrmosArchiveKeeperTarget extends TargetCardInHand {
 
-    private static final FilterCard filter = new FilterCard("cards with different names");
+    private static final FilterCard filter = new FilterCard("3 cards with different names");
 
     OrmosArchiveKeeperTarget() {
         super(3, filter);
diff --git a/Mage.Sets/src/mage/cards/w/WitchOfTheMoors.java b/Mage.Sets/src/mage/cards/w/WitchOfTheMoors.java
index 0659eaf221..4a3f809262 100644
--- a/Mage.Sets/src/mage/cards/w/WitchOfTheMoors.java
+++ b/Mage.Sets/src/mage/cards/w/WitchOfTheMoors.java
@@ -39,14 +39,15 @@ public final class WitchOfTheMoors extends CardImpl {
         // Deathtouch
         this.addAbility(DeathtouchAbility.getInstance());
 
-        // At the beginning of your end step, if you gained life this turn, each opponent sacrifices a creature and you return up to one target creature card from your graveyard to your hand.
+        // At the beginning of your end step, if you gained life this turn, each opponent sacrifices a 
+        // creature and you return up to one target creature card from your graveyard to your hand.
         Ability ability = new ConditionalInterveningIfTriggeredAbility(
                 new BeginningOfEndStepTriggeredAbility(new SacrificeOpponentsEffect(
                         StaticFilters.FILTER_PERMANENT_A_CREATURE
                 ), TargetController.YOU, false),
-                condition, "At the beginning of your end step, if you gained life this turn, " +
-                "each opponent sacrifices a creature and you return up to one target creature card " +
-                "from your graveyard to your hand"
+                condition, "At the beginning of your end step, if you gained life this turn, "
+                + "each opponent sacrifices a creature and you return up to one target creature card "
+                + "from your graveyard to your hand."
         );
         ability.addEffect(new ReturnFromGraveyardToHandTargetEffect());
         ability.addTarget(new TargetCardInYourGraveyard(
diff --git a/Mage/src/main/java/mage/target/TargetImpl.java b/Mage/src/main/java/mage/target/TargetImpl.java
index 20972f39d7..8c36e138c8 100644
--- a/Mage/src/main/java/mage/target/TargetImpl.java
+++ b/Mage/src/main/java/mage/target/TargetImpl.java
@@ -112,7 +112,10 @@ public abstract class TargetImpl implements Target {
             sb.append(suffix);
             return sb.toString();
         }
-        if (targetName.startsWith("another") || targetName.startsWith("a ") || targetName.startsWith("an ")) {
+        if (targetName.startsWith("another") 
+                || targetName.startsWith("a ") 
+                || targetName.startsWith("an ")
+                || targetName.startsWith("any ")) {
             return "Select " + targetName + suffix;
         } else if (targetName.startsWith("a") || targetName.startsWith("e") || targetName.startsWith("i") || targetName.startsWith("o") || targetName.startsWith("u")) {
             return "Select an " + targetName + suffix;

From 6c69939c0dee468c36bb8742c42f20170653d9e1 Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Thu, 25 Jun 2020 17:45:24 -0500
Subject: [PATCH 510/586] - Fixed some text issues related to Bug 6675...

---
 .../src/mage/cards/a/AkromaAngelOfFury.java   |  4 +--
 .../src/mage/cards/a/AllosaurusShepherd.java  | 13 ++++----
 Mage.Sets/src/mage/cards/a/AlteredEgo.java    |  4 +--
 .../src/mage/cards/b/BlurredMongoose.java     |  4 +--
 Mage.Sets/src/mage/cards/c/CarnageTyrant.java |  4 +--
 .../mage/cards/c/ChandraAwakenedInferno.java  |  4 +--
 .../src/mage/cards/c/ChromiumTheMutable.java  |  4 +--
 .../src/mage/cards/c/CommenceTheEndgame.java  |  4 +--
 .../src/mage/cards/d/DragonlordDromoka.java   |  4 +--
 .../src/mage/cards/e/EmrakulTheAeonsTorn.java |  4 +--
 Mage.Sets/src/mage/cards/f/Fry.java           |  4 +--
 .../src/mage/cards/i/InescapableBlaze.java    |  4 +--
 .../mage/cards/i/IsaoEnlightenedBushi.java    |  4 +--
 Mage.Sets/src/mage/cards/l/LastWord.java      |  4 +--
 Mage.Sets/src/mage/cards/l/LightningMare.java |  4 +--
 Mage.Sets/src/mage/cards/l/LoxodonSmiter.java |  4 +--
 .../src/mage/cards/m/MistcutterHydra.java     |  4 +--
 .../src/mage/cards/n/NezahalPrimalTide.java   |  4 +--
 .../src/mage/cards/n/NivMizzetParun.java      |  4 +--
 Mage.Sets/src/mage/cards/o/Obliterate.java    |  4 +--
 .../src/mage/cards/p/PearlLakeAncient.java    |  4 +--
 .../src/mage/cards/p/PetrifiedWoodKin.java    |  4 +--
 .../src/mage/cards/p/ProwlingSerpopard.java   |  4 +--
 .../src/mage/cards/s/SavageSummoning.java     |  4 +--
 Mage.Sets/src/mage/cards/s/Scragnoth.java     |  4 +--
 .../src/mage/cards/s/ShiftingCeratops.java    |  4 +--
 Mage.Sets/src/mage/cards/s/Skylasher.java     |  4 +--
 .../mage/cards/s/SphinxOfTheFinalWord.java    |  4 +--
 .../src/mage/cards/s/SupremeVerdict.java      |  4 +--
 .../src/mage/cards/s/SurrakDragonclaw.java    |  4 +--
 .../src/mage/cards/t/ThoughtDistortion.java   |  4 +--
 Mage.Sets/src/mage/cards/v/VexingShusher.java |  4 +--
 .../src/mage/cards/v/VolcanicFallout.java     |  4 +--
 Mage.Sets/src/mage/cards/w/WreakHavoc.java    |  4 +--
 .../common/CantBeCounteredSourceAbility.java  | 32 +++++++++++++++++++
 35 files changed, 105 insertions(+), 72 deletions(-)
 create mode 100644 Mage/src/main/java/mage/abilities/common/CantBeCounteredSourceAbility.java

diff --git a/Mage.Sets/src/mage/cards/a/AkromaAngelOfFury.java b/Mage.Sets/src/mage/cards/a/AkromaAngelOfFury.java
index 6e65dda3ce..42aaaa5096 100644
--- a/Mage.Sets/src/mage/cards/a/AkromaAngelOfFury.java
+++ b/Mage.Sets/src/mage/cards/a/AkromaAngelOfFury.java
@@ -4,7 +4,7 @@ package mage.cards.a;
 import java.util.UUID;
 import mage.MageInt;
 import mage.ObjectColor;
-import mage.abilities.common.CantBeCounteredAbility;
+import mage.abilities.common.CantBeCounteredSourceAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.common.continuous.BoostSourceEffect;
@@ -31,7 +31,7 @@ public final class AkromaAngelOfFury extends CardImpl {
         this.toughness = new MageInt(6);
 
         // Akroma, Angel of Fury can't be countered.
-        this.addAbility(new CantBeCounteredAbility());
+        this.addAbility(new CantBeCounteredSourceAbility());
         // Flying
         this.addAbility(FlyingAbility.getInstance());
         // Trample
diff --git a/Mage.Sets/src/mage/cards/a/AllosaurusShepherd.java b/Mage.Sets/src/mage/cards/a/AllosaurusShepherd.java
index a9ec318ff6..dc32fd4fd6 100644
--- a/Mage.Sets/src/mage/cards/a/AllosaurusShepherd.java
+++ b/Mage.Sets/src/mage/cards/a/AllosaurusShepherd.java
@@ -3,7 +3,7 @@ package mage.cards.a;
 import mage.MageInt;
 import mage.ObjectColor;
 import mage.abilities.Ability;
-import mage.abilities.common.CantBeCounteredAbility;
+import mage.abilities.common.CantBeCounteredSourceAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
@@ -14,15 +14,15 @@ import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.*;
 import mage.filter.FilterPermanent;
-import mage.filter.FilterSpell;
 import mage.filter.common.FilterControlledCreaturePermanent;
 import mage.filter.predicate.mageobject.ColorPredicate;
 
 import java.util.UUID;
+import mage.filter.FilterSpell;
 
 public class AllosaurusShepherd extends CardImpl {
 
-    private static final FilterSpell greenSpellsFilter = new FilterSpell("green spells");
+    private static final FilterSpell greenSpellsFilter = new FilterSpell("Green spells you control");
     private static final FilterPermanent elvesFilter = new FilterControlledCreaturePermanent("each Elf creature you control");
 
     static {
@@ -31,7 +31,7 @@ public class AllosaurusShepherd extends CardImpl {
     }
 
     public AllosaurusShepherd(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{G}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}");
         this.subtype.add(SubType.ELF);
         this.subtype.add(SubType.SHAMAN);
 
@@ -39,13 +39,14 @@ public class AllosaurusShepherd extends CardImpl {
         this.toughness = new MageInt(1);
 
         //Allosaurus Shepherd can't be countered.
-        this.addAbility(new CantBeCounteredAbility());
+        this.addAbility(new CantBeCounteredSourceAbility());
 
         //Green spells you control can't be countered.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
                 new CantBeCounteredControlledEffect(greenSpellsFilter, null, Duration.WhileOnBattlefield)));
 
-        //4GG: Until end of turn, each Elf creature you control has base power and toughness 5/5 and becomes a Dinosaur in addition to its other creature types.
+        //4GG: Until end of turn, each Elf creature you control has base power and toughness 5/5 
+        // and becomes a Dinosaur in addition to its other creature types.
         Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD,
                 new SetPowerToughnessAllEffect(5, 5, Duration.EndOfTurn, elvesFilter, true)
                         .setText("Until end of turn, each Elf creature you control has base power and toughness 5/5"),
diff --git a/Mage.Sets/src/mage/cards/a/AlteredEgo.java b/Mage.Sets/src/mage/cards/a/AlteredEgo.java
index ae603d0566..3cb307ca5b 100644
--- a/Mage.Sets/src/mage/cards/a/AlteredEgo.java
+++ b/Mage.Sets/src/mage/cards/a/AlteredEgo.java
@@ -4,7 +4,7 @@ package mage.cards.a;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.CantBeCounteredAbility;
+import mage.abilities.common.CantBeCounteredSourceAbility;
 import mage.abilities.common.EntersBattlefieldAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.CopyPermanentEffect;
@@ -32,7 +32,7 @@ public final class AlteredEgo extends CardImpl {
         this.toughness = new MageInt(0);
 
         // Altered Ego can't be countered.
-        this.addAbility(new CantBeCounteredAbility());
+        this.addAbility(new CantBeCounteredSourceAbility());
 
         // You may have Altered Ego enter the battlefield as a copy of any creature on the battlefield, except it enters with an additional X +1/+1 counters on it.
         Effect effect = new CopyPermanentEffect(StaticFilters.FILTER_PERMANENT_CREATURE, null);
diff --git a/Mage.Sets/src/mage/cards/b/BlurredMongoose.java b/Mage.Sets/src/mage/cards/b/BlurredMongoose.java
index 2ea3ba023c..675a16d753 100644
--- a/Mage.Sets/src/mage/cards/b/BlurredMongoose.java
+++ b/Mage.Sets/src/mage/cards/b/BlurredMongoose.java
@@ -3,7 +3,7 @@ package mage.cards.b;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.CantBeCounteredAbility;
+import mage.abilities.common.CantBeCounteredSourceAbility;
 import mage.abilities.keyword.ShroudAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -24,7 +24,7 @@ public final class BlurredMongoose extends CardImpl {
         this.toughness = new MageInt(1);
 
         // Blurred Mongoose can't be countered.
-        this.addAbility(new CantBeCounteredAbility());
+        this.addAbility(new CantBeCounteredSourceAbility());
         this.addAbility(ShroudAbility.getInstance());
     }
 
diff --git a/Mage.Sets/src/mage/cards/c/CarnageTyrant.java b/Mage.Sets/src/mage/cards/c/CarnageTyrant.java
index d252b78390..ed47c44b28 100644
--- a/Mage.Sets/src/mage/cards/c/CarnageTyrant.java
+++ b/Mage.Sets/src/mage/cards/c/CarnageTyrant.java
@@ -3,7 +3,7 @@ package mage.cards.c;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.CantBeCounteredAbility;
+import mage.abilities.common.CantBeCounteredSourceAbility;
 import mage.abilities.keyword.TrampleAbility;
 import mage.abilities.keyword.HexproofAbility;
 import mage.cards.CardImpl;
@@ -25,7 +25,7 @@ public final class CarnageTyrant extends CardImpl {
         this.toughness = new MageInt(6);
 
         // Carnage Tyrant can't be countered.
-        this.addAbility(new CantBeCounteredAbility());
+        this.addAbility(new CantBeCounteredSourceAbility());
         
         // Trample
         this.addAbility(TrampleAbility.getInstance());
diff --git a/Mage.Sets/src/mage/cards/c/ChandraAwakenedInferno.java b/Mage.Sets/src/mage/cards/c/ChandraAwakenedInferno.java
index c0f61cb557..8a28de03eb 100644
--- a/Mage.Sets/src/mage/cards/c/ChandraAwakenedInferno.java
+++ b/Mage.Sets/src/mage/cards/c/ChandraAwakenedInferno.java
@@ -2,7 +2,7 @@ package mage.cards.c;
 
 import mage.abilities.Ability;
 import mage.abilities.LoyaltyAbility;
-import mage.abilities.common.CantBeCounteredAbility;
+import mage.abilities.common.CantBeCounteredSourceAbility;
 import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
 import mage.abilities.costs.Cost;
 import mage.abilities.costs.common.PayVariableLoyaltyCost;
@@ -46,7 +46,7 @@ public final class ChandraAwakenedInferno extends CardImpl {
         this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(6));
 
         // This spell can't be countered.
-        this.addAbility(new CantBeCounteredAbility());
+        this.addAbility(new CantBeCounteredSourceAbility());
 
         // +2: Each opponent gets an emblem with "At the beginning of your upkeep, this emblem deals 1 damage to you."
         this.addAbility(new LoyaltyAbility(new ChandraAwakenedInfernoEffect(), 2));
diff --git a/Mage.Sets/src/mage/cards/c/ChromiumTheMutable.java b/Mage.Sets/src/mage/cards/c/ChromiumTheMutable.java
index 4ec39507c5..8de4373ece 100644
--- a/Mage.Sets/src/mage/cards/c/ChromiumTheMutable.java
+++ b/Mage.Sets/src/mage/cards/c/ChromiumTheMutable.java
@@ -3,7 +3,7 @@ package mage.cards.c;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.CantBeCounteredAbility;
+import mage.abilities.common.CantBeCounteredSourceAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.common.DiscardCardCost;
 import mage.abilities.effects.common.combat.CantBeBlockedSourceEffect;
@@ -38,7 +38,7 @@ public final class ChromiumTheMutable extends CardImpl {
         this.addAbility(FlashAbility.getInstance());
 
         // This spell can't be countered.
-        this.addAbility(new CantBeCounteredAbility());
+        this.addAbility(new CantBeCounteredSourceAbility());
 
         // Flying
         this.addAbility(FlyingAbility.getInstance());
diff --git a/Mage.Sets/src/mage/cards/c/CommenceTheEndgame.java b/Mage.Sets/src/mage/cards/c/CommenceTheEndgame.java
index b9d1fe568c..1c58d22cf9 100644
--- a/Mage.Sets/src/mage/cards/c/CommenceTheEndgame.java
+++ b/Mage.Sets/src/mage/cards/c/CommenceTheEndgame.java
@@ -1,7 +1,7 @@
 package mage.cards.c;
 
 import mage.abilities.Ability;
-import mage.abilities.common.CantBeCounteredAbility;
+import mage.abilities.common.CantBeCounteredSourceAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.keyword.AmassEffect;
 import mage.cards.CardImpl;
@@ -22,7 +22,7 @@ public final class CommenceTheEndgame extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{U}{U}");
 
         // This spell can't be countered.
-        this.addAbility(new CantBeCounteredAbility());
+        this.addAbility(new CantBeCounteredSourceAbility());
 
         // Draw two cards, then amass X, where X is the number of cards in your hand.
         this.getSpellAbility().addEffect(new CommenceTheEndgameEffect());
diff --git a/Mage.Sets/src/mage/cards/d/DragonlordDromoka.java b/Mage.Sets/src/mage/cards/d/DragonlordDromoka.java
index c24d6985bc..36d69f9f50 100644
--- a/Mage.Sets/src/mage/cards/d/DragonlordDromoka.java
+++ b/Mage.Sets/src/mage/cards/d/DragonlordDromoka.java
@@ -4,7 +4,7 @@ package mage.cards.d;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.CantBeCounteredAbility;
+import mage.abilities.common.CantBeCounteredSourceAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
 import mage.abilities.keyword.FlyingAbility;
@@ -30,7 +30,7 @@ public final class DragonlordDromoka extends CardImpl {
         this.toughness = new MageInt(7);
 
         // Dragonlord Dromoka can't be countered
-        this.addAbility(new CantBeCounteredAbility());
+        this.addAbility(new CantBeCounteredSourceAbility());
         // Flying
         this.addAbility(FlyingAbility.getInstance());
         // Lifelink
diff --git a/Mage.Sets/src/mage/cards/e/EmrakulTheAeonsTorn.java b/Mage.Sets/src/mage/cards/e/EmrakulTheAeonsTorn.java
index b08775abcb..91caa63e32 100644
--- a/Mage.Sets/src/mage/cards/e/EmrakulTheAeonsTorn.java
+++ b/Mage.Sets/src/mage/cards/e/EmrakulTheAeonsTorn.java
@@ -3,7 +3,7 @@ package mage.cards.e;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.CantBeCounteredAbility;
+import mage.abilities.common.CantBeCounteredSourceAbility;
 import mage.abilities.common.PutIntoGraveFromAnywhereSourceTriggeredAbility;
 import mage.abilities.effects.common.CastSourceTriggeredAbility;
 import mage.abilities.effects.common.ShuffleIntoLibraryGraveOfSourceOwnerEffect;
@@ -39,7 +39,7 @@ public final class EmrakulTheAeonsTorn extends CardImpl {
         this.toughness = new MageInt(15);
 
         // Emrakul, the Aeons Torn can't be countered.
-        this.addAbility(new CantBeCounteredAbility());
+        this.addAbility(new CantBeCounteredSourceAbility());
         // When you cast Emrakul, take an extra turn after this one.
         this.addAbility(new CastSourceTriggeredAbility(new AddExtraTurnControllerEffect()));
 
diff --git a/Mage.Sets/src/mage/cards/f/Fry.java b/Mage.Sets/src/mage/cards/f/Fry.java
index c9c9f7eca3..faa409a226 100644
--- a/Mage.Sets/src/mage/cards/f/Fry.java
+++ b/Mage.Sets/src/mage/cards/f/Fry.java
@@ -1,7 +1,7 @@
 package mage.cards.f;
 
 import mage.ObjectColor;
-import mage.abilities.common.CantBeCounteredAbility;
+import mage.abilities.common.CantBeCounteredSourceAbility;
 import mage.abilities.effects.common.DamageTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -33,7 +33,7 @@ public final class Fry extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{R}");
 
         // This spell can't be countered.
-        this.addAbility(new CantBeCounteredAbility());
+        this.addAbility(new CantBeCounteredSourceAbility());
 
         // Fry deals 5 damage to target creature or planeswalker that's white or blue.
         this.getSpellAbility().addEffect(new DamageTargetEffect(5));
diff --git a/Mage.Sets/src/mage/cards/i/InescapableBlaze.java b/Mage.Sets/src/mage/cards/i/InescapableBlaze.java
index 2aa756c1ba..ba670d533c 100644
--- a/Mage.Sets/src/mage/cards/i/InescapableBlaze.java
+++ b/Mage.Sets/src/mage/cards/i/InescapableBlaze.java
@@ -1,7 +1,7 @@
 package mage.cards.i;
 
 import java.util.UUID;
-import mage.abilities.common.CantBeCounteredAbility;
+import mage.abilities.common.CantBeCounteredSourceAbility;
 import mage.abilities.effects.common.DamageTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -18,7 +18,7 @@ public final class InescapableBlaze extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{R}{R}");
 
         // This spell can't be countered.        
-        this.addAbility(new CantBeCounteredAbility().setRuleAtTheTop(true));
+        this.addAbility(new CantBeCounteredSourceAbility().setRuleAtTheTop(true));
 
         // Inescapable Flame deals 6 damage to any target.
         this.getSpellAbility().addEffect(new DamageTargetEffect(6));
diff --git a/Mage.Sets/src/mage/cards/i/IsaoEnlightenedBushi.java b/Mage.Sets/src/mage/cards/i/IsaoEnlightenedBushi.java
index 7f69d6bb9f..d0f044d1ed 100644
--- a/Mage.Sets/src/mage/cards/i/IsaoEnlightenedBushi.java
+++ b/Mage.Sets/src/mage/cards/i/IsaoEnlightenedBushi.java
@@ -4,7 +4,7 @@ package mage.cards.i;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.CantBeCounteredAbility;
+import mage.abilities.common.CantBeCounteredSourceAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.mana.GenericManaCost;
 import mage.abilities.effects.common.RegenerateTargetEffect;
@@ -40,7 +40,7 @@ public final class IsaoEnlightenedBushi extends CardImpl {
         this.toughness = new MageInt(1);
 
         // Isao, Enlightened Bushi can't be countered.
-        this.addAbility(new CantBeCounteredAbility());
+        this.addAbility(new CantBeCounteredSourceAbility());
         this.addAbility(new BushidoAbility(2));
         // {2}: Regenerate target Samurai.
         Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateTargetEffect(), new GenericManaCost(2));
diff --git a/Mage.Sets/src/mage/cards/l/LastWord.java b/Mage.Sets/src/mage/cards/l/LastWord.java
index aa94e9991a..bf8cb38408 100644
--- a/Mage.Sets/src/mage/cards/l/LastWord.java
+++ b/Mage.Sets/src/mage/cards/l/LastWord.java
@@ -3,7 +3,7 @@
 package mage.cards.l;
 
 import java.util.UUID;
-import mage.abilities.common.CantBeCounteredAbility;
+import mage.abilities.common.CantBeCounteredSourceAbility;
 import mage.abilities.effects.common.CounterTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -19,7 +19,7 @@ public final class LastWord extends CardImpl {
     public LastWord (UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{U}{U}");
 
-        this.addAbility(new CantBeCounteredAbility());
+        this.addAbility(new CantBeCounteredSourceAbility());
         this.getSpellAbility().addEffect(new CounterTargetEffect());
         this.getSpellAbility().addTarget(new TargetSpell());
     }
diff --git a/Mage.Sets/src/mage/cards/l/LightningMare.java b/Mage.Sets/src/mage/cards/l/LightningMare.java
index 557cd1daa8..7d33dde8d3 100644
--- a/Mage.Sets/src/mage/cards/l/LightningMare.java
+++ b/Mage.Sets/src/mage/cards/l/LightningMare.java
@@ -3,7 +3,7 @@ package mage.cards.l;
 import java.util.UUID;
 import mage.MageInt;
 import mage.ObjectColor;
-import mage.abilities.common.CantBeCounteredAbility;
+import mage.abilities.common.CantBeCounteredSourceAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
@@ -39,7 +39,7 @@ public final class LightningMare extends CardImpl {
         this.toughness = new MageInt(1);
 
         // This spell can't be countered.
-        this.addAbility(new CantBeCounteredAbility());
+        this.addAbility(new CantBeCounteredSourceAbility());
 
         // Lightning Mare can't be blocked by blue creatures.
         this.addAbility(new SimpleStaticAbility(
diff --git a/Mage.Sets/src/mage/cards/l/LoxodonSmiter.java b/Mage.Sets/src/mage/cards/l/LoxodonSmiter.java
index f43e90beba..834e7544cd 100644
--- a/Mage.Sets/src/mage/cards/l/LoxodonSmiter.java
+++ b/Mage.Sets/src/mage/cards/l/LoxodonSmiter.java
@@ -3,7 +3,7 @@ package mage.cards.l;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.CantBeCounteredAbility;
+import mage.abilities.common.CantBeCounteredSourceAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.common.DiscardOntoBattlefieldEffect;
 import mage.cards.CardImpl;
@@ -26,7 +26,7 @@ public final class LoxodonSmiter extends CardImpl {
         this.toughness = new MageInt(4);
 
         // Loxodon Smiter can't be countered.
-        this.addAbility(new CantBeCounteredAbility());
+        this.addAbility(new CantBeCounteredSourceAbility());
 
         // If a spell or ability an opponent controls causes you to discard Loxodon Smiter, put it onto the battlefield instead of putting it into your graveyard.
         this.addAbility(new SimpleStaticAbility(Zone.HAND, new DiscardOntoBattlefieldEffect()));
diff --git a/Mage.Sets/src/mage/cards/m/MistcutterHydra.java b/Mage.Sets/src/mage/cards/m/MistcutterHydra.java
index 92fb0c947a..6078203b18 100644
--- a/Mage.Sets/src/mage/cards/m/MistcutterHydra.java
+++ b/Mage.Sets/src/mage/cards/m/MistcutterHydra.java
@@ -4,7 +4,7 @@ package mage.cards.m;
 import java.util.UUID;
 import mage.MageInt;
 import mage.ObjectColor;
-import mage.abilities.common.CantBeCounteredAbility;
+import mage.abilities.common.CantBeCounteredSourceAbility;
 import mage.abilities.common.EntersBattlefieldAbility;
 import mage.abilities.effects.common.EntersBattlefieldWithXCountersEffect;
 import mage.abilities.keyword.HasteAbility;
@@ -29,7 +29,7 @@ public final class MistcutterHydra extends CardImpl {
         this.toughness = new MageInt(0);
 
         // Mistcutter Hydra can't be countered.
-        this.addAbility(new CantBeCounteredAbility());
+        this.addAbility(new CantBeCounteredSourceAbility());
         // Haste
         this.addAbility(HasteAbility.getInstance());
         // protection from blue
diff --git a/Mage.Sets/src/mage/cards/n/NezahalPrimalTide.java b/Mage.Sets/src/mage/cards/n/NezahalPrimalTide.java
index 3a28e370c3..4332784352 100644
--- a/Mage.Sets/src/mage/cards/n/NezahalPrimalTide.java
+++ b/Mage.Sets/src/mage/cards/n/NezahalPrimalTide.java
@@ -3,7 +3,7 @@ package mage.cards.n;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.CantBeCounteredAbility;
+import mage.abilities.common.CantBeCounteredSourceAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.common.SpellCastOpponentTriggeredAbility;
@@ -47,7 +47,7 @@ public final class NezahalPrimalTide extends CardImpl {
         this.toughness = new MageInt(7);
 
         // Nezahal, Primal Tide can't be countered.
-        this.addAbility(new CantBeCounteredAbility());
+        this.addAbility(new CantBeCounteredSourceAbility());
 
         // You have no maximum hand size.
         Effect effect = new MaximumHandSizeControllerEffect(Integer.MAX_VALUE, Duration.WhileOnBattlefield, MaximumHandSizeControllerEffect.HandSizeModification.SET);
diff --git a/Mage.Sets/src/mage/cards/n/NivMizzetParun.java b/Mage.Sets/src/mage/cards/n/NivMizzetParun.java
index 8cfccd5137..eabca9b30c 100644
--- a/Mage.Sets/src/mage/cards/n/NivMizzetParun.java
+++ b/Mage.Sets/src/mage/cards/n/NivMizzetParun.java
@@ -3,7 +3,7 @@ package mage.cards.n;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.CantBeCounteredAbility;
+import mage.abilities.common.CantBeCounteredSourceAbility;
 import mage.abilities.common.DrawCardControllerTriggeredAbility;
 import mage.abilities.common.SpellCastAllTriggeredAbility;
 import mage.abilities.effects.common.DamageTargetEffect;
@@ -33,7 +33,7 @@ public final class NivMizzetParun extends CardImpl {
         this.toughness = new MageInt(5);
 
         // This spell can't be countered.
-        this.addAbility(new CantBeCounteredAbility());
+        this.addAbility(new CantBeCounteredSourceAbility());
 
         // Flying
         this.addAbility(FlyingAbility.getInstance());
diff --git a/Mage.Sets/src/mage/cards/o/Obliterate.java b/Mage.Sets/src/mage/cards/o/Obliterate.java
index 8cfd66e0b3..03b8ddba0d 100644
--- a/Mage.Sets/src/mage/cards/o/Obliterate.java
+++ b/Mage.Sets/src/mage/cards/o/Obliterate.java
@@ -2,7 +2,7 @@
 package mage.cards.o;
 
 import java.util.UUID;
-import mage.abilities.common.CantBeCounteredAbility;
+import mage.abilities.common.CantBeCounteredSourceAbility;
 import mage.abilities.effects.common.DestroyAllEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -30,7 +30,7 @@ public final class Obliterate extends CardImpl {
 
 
         // Obliterate can't be countered.
-        this.addAbility(new CantBeCounteredAbility());
+        this.addAbility(new CantBeCounteredSourceAbility());
         // Destroy all artifacts, creatures, and lands. They can't be regenerated.
         this.getSpellAbility().addEffect(new DestroyAllEffect(filter));
     }
diff --git a/Mage.Sets/src/mage/cards/p/PearlLakeAncient.java b/Mage.Sets/src/mage/cards/p/PearlLakeAncient.java
index 194cb43923..09d36f2957 100644
--- a/Mage.Sets/src/mage/cards/p/PearlLakeAncient.java
+++ b/Mage.Sets/src/mage/cards/p/PearlLakeAncient.java
@@ -3,7 +3,7 @@ package mage.cards.p;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.CantBeCounteredAbility;
+import mage.abilities.common.CantBeCounteredSourceAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.common.ReturnToHandChosenControlledPermanentCost;
 import mage.abilities.effects.common.ReturnToHandSourceEffect;
@@ -34,7 +34,7 @@ public final class PearlLakeAncient extends CardImpl {
         this.addAbility(FlashAbility.getInstance());
         
         // Pearl Lake Ancient can't be countered.
-        this.addAbility(new CantBeCounteredAbility());
+        this.addAbility(new CantBeCounteredSourceAbility());
         
         // Prowess
         this.addAbility(new ProwessAbility());
diff --git a/Mage.Sets/src/mage/cards/p/PetrifiedWoodKin.java b/Mage.Sets/src/mage/cards/p/PetrifiedWoodKin.java
index a42b819797..140cb43463 100644
--- a/Mage.Sets/src/mage/cards/p/PetrifiedWoodKin.java
+++ b/Mage.Sets/src/mage/cards/p/PetrifiedWoodKin.java
@@ -7,7 +7,7 @@ import java.util.UUID;
 import mage.MageInt;
 import mage.MageObjectReference;
 import mage.abilities.Ability;
-import mage.abilities.common.CantBeCounteredAbility;
+import mage.abilities.common.CantBeCounteredSourceAbility;
 import mage.abilities.common.EntersBattlefieldAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.constants.SubType;
@@ -44,7 +44,7 @@ public final class PetrifiedWoodKin extends CardImpl {
         this.toughness = new MageInt(3);
 
         // Petrified Wood-Kin can't be countered.
-        this.addAbility(new CantBeCounteredAbility());
+        this.addAbility(new CantBeCounteredSourceAbility());
 
         // Bloodthirst X
         this.addAbility(new EntersBattlefieldAbility(
diff --git a/Mage.Sets/src/mage/cards/p/ProwlingSerpopard.java b/Mage.Sets/src/mage/cards/p/ProwlingSerpopard.java
index e676d6beb9..c3b547c582 100644
--- a/Mage.Sets/src/mage/cards/p/ProwlingSerpopard.java
+++ b/Mage.Sets/src/mage/cards/p/ProwlingSerpopard.java
@@ -3,7 +3,7 @@ package mage.cards.p;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.CantBeCounteredAbility;
+import mage.abilities.common.CantBeCounteredSourceAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.common.CantBeCounteredControlledEffect;
 import mage.cards.CardImpl;
@@ -35,7 +35,7 @@ public final class ProwlingSerpopard extends CardImpl {
         this.toughness = new MageInt(3);
 
         // Prowling Serpopard can't be countered.
-        this.addAbility(new CantBeCounteredAbility());
+        this.addAbility(new CantBeCounteredSourceAbility());
 
         // Creature spells you control can't be countered.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantBeCounteredControlledEffect(filterTarget, null, Duration.WhileOnBattlefield)));
diff --git a/Mage.Sets/src/mage/cards/s/SavageSummoning.java b/Mage.Sets/src/mage/cards/s/SavageSummoning.java
index 8c4d07ebba..15228dd844 100644
--- a/Mage.Sets/src/mage/cards/s/SavageSummoning.java
+++ b/Mage.Sets/src/mage/cards/s/SavageSummoning.java
@@ -3,7 +3,7 @@ package mage.cards.s;
 
 import mage.MageObject;
 import mage.abilities.Ability;
-import mage.abilities.common.CantBeCounteredAbility;
+import mage.abilities.common.CantBeCounteredSourceAbility;
 import mage.abilities.effects.AsThoughEffectImpl;
 import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
 import mage.abilities.effects.ReplacementEffectImpl;
@@ -34,7 +34,7 @@ public final class SavageSummoning extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{G}");
 
         // Savage Summoning can't be countered.
-        Ability ability = new CantBeCounteredAbility();
+        Ability ability = new CantBeCounteredSourceAbility();
         ability.setRuleAtTheTop(true);
         this.addAbility(ability);
 
diff --git a/Mage.Sets/src/mage/cards/s/Scragnoth.java b/Mage.Sets/src/mage/cards/s/Scragnoth.java
index c6497821d7..d1a5604e18 100644
--- a/Mage.Sets/src/mage/cards/s/Scragnoth.java
+++ b/Mage.Sets/src/mage/cards/s/Scragnoth.java
@@ -4,7 +4,7 @@ package mage.cards.s;
 import java.util.UUID;
 import mage.MageInt;
 import mage.ObjectColor;
-import mage.abilities.common.CantBeCounteredAbility;
+import mage.abilities.common.CantBeCounteredSourceAbility;
 import mage.abilities.keyword.ProtectionAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -25,7 +25,7 @@ public final class Scragnoth extends CardImpl {
         this.toughness = new MageInt(4);
 
         // Scragnoth can't be countered.
-        this.addAbility(new CantBeCounteredAbility());
+        this.addAbility(new CantBeCounteredSourceAbility());
         // Protection from blue
         this.addAbility(ProtectionAbility.from(ObjectColor.BLUE));
     }
diff --git a/Mage.Sets/src/mage/cards/s/ShiftingCeratops.java b/Mage.Sets/src/mage/cards/s/ShiftingCeratops.java
index 6a501a6451..efc57339c0 100644
--- a/Mage.Sets/src/mage/cards/s/ShiftingCeratops.java
+++ b/Mage.Sets/src/mage/cards/s/ShiftingCeratops.java
@@ -3,7 +3,7 @@ package mage.cards.s;
 import mage.MageInt;
 import mage.ObjectColor;
 import mage.abilities.Ability;
-import mage.abilities.common.CantBeCounteredAbility;
+import mage.abilities.common.CantBeCounteredSourceAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.OneShotEffect;
@@ -40,7 +40,7 @@ public final class ShiftingCeratops extends CardImpl {
         this.toughness = new MageInt(4);
 
         // This spell can't be countered.
-        this.addAbility(new CantBeCounteredAbility());
+        this.addAbility(new CantBeCounteredSourceAbility());
 
         // Protection from blue
         this.addAbility(ProtectionAbility.from(ObjectColor.BLUE));
diff --git a/Mage.Sets/src/mage/cards/s/Skylasher.java b/Mage.Sets/src/mage/cards/s/Skylasher.java
index af737ceda6..0d0f8c8df8 100644
--- a/Mage.Sets/src/mage/cards/s/Skylasher.java
+++ b/Mage.Sets/src/mage/cards/s/Skylasher.java
@@ -5,7 +5,7 @@ package mage.cards.s;
 import java.util.UUID;
 import mage.MageInt;
 import mage.ObjectColor;
-import mage.abilities.common.CantBeCounteredAbility;
+import mage.abilities.common.CantBeCounteredSourceAbility;
 import mage.abilities.keyword.FlashAbility;
 import mage.abilities.keyword.ProtectionAbility;
 import mage.abilities.keyword.ReachAbility;
@@ -30,7 +30,7 @@ public final class Skylasher extends CardImpl {
         // Flash
         this.addAbility(FlashAbility.getInstance());
         // Skylasher can't be countered.
-        this.addAbility(new CantBeCounteredAbility());
+        this.addAbility(new CantBeCounteredSourceAbility());
         // Reach, protection from blue
         this.addAbility(ReachAbility.getInstance());
         this.addAbility(ProtectionAbility.from(ObjectColor.BLUE));
diff --git a/Mage.Sets/src/mage/cards/s/SphinxOfTheFinalWord.java b/Mage.Sets/src/mage/cards/s/SphinxOfTheFinalWord.java
index 038b392885..678629eaee 100644
--- a/Mage.Sets/src/mage/cards/s/SphinxOfTheFinalWord.java
+++ b/Mage.Sets/src/mage/cards/s/SphinxOfTheFinalWord.java
@@ -3,7 +3,7 @@ package mage.cards.s;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.CantBeCounteredAbility;
+import mage.abilities.common.CantBeCounteredSourceAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.common.CantBeCounteredControlledEffect;
 import mage.abilities.keyword.FlyingAbility;
@@ -37,7 +37,7 @@ public final class SphinxOfTheFinalWord extends CardImpl {
         this.toughness = new MageInt(5);
 
         // Sphinx of the Final Word can't be countered.
-        this.addAbility(new CantBeCounteredAbility());
+        this.addAbility(new CantBeCounteredSourceAbility());
         
         // Flying
         this.addAbility(FlyingAbility.getInstance());
diff --git a/Mage.Sets/src/mage/cards/s/SupremeVerdict.java b/Mage.Sets/src/mage/cards/s/SupremeVerdict.java
index e7249c4f7b..7dd6932c06 100644
--- a/Mage.Sets/src/mage/cards/s/SupremeVerdict.java
+++ b/Mage.Sets/src/mage/cards/s/SupremeVerdict.java
@@ -3,7 +3,7 @@ package mage.cards.s;
 
 import java.util.UUID;
 import mage.abilities.Ability;
-import mage.abilities.common.CantBeCounteredAbility;
+import mage.abilities.common.CantBeCounteredSourceAbility;
 import mage.abilities.effects.common.DestroyAllEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -21,7 +21,7 @@ public final class SupremeVerdict extends CardImpl {
         super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{W}{W}{U}");
 
         // Supreme Verdict can't be countered.
-        Ability ability = new CantBeCounteredAbility();
+        Ability ability = new CantBeCounteredSourceAbility();
         ability.setRuleAtTheTop(true);
         this.addAbility(ability);
 
diff --git a/Mage.Sets/src/mage/cards/s/SurrakDragonclaw.java b/Mage.Sets/src/mage/cards/s/SurrakDragonclaw.java
index bb5cfb8624..8b38d589d5 100644
--- a/Mage.Sets/src/mage/cards/s/SurrakDragonclaw.java
+++ b/Mage.Sets/src/mage/cards/s/SurrakDragonclaw.java
@@ -3,7 +3,7 @@ package mage.cards.s;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.CantBeCounteredAbility;
+import mage.abilities.common.CantBeCounteredSourceAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.common.CantBeCounteredControlledEffect;
 import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
@@ -44,7 +44,7 @@ public final class SurrakDragonclaw extends CardImpl {
         this.addAbility(FlashAbility.getInstance());
 
         // Surrak Dragonclaw can't be countered.
-        this.addAbility(new CantBeCounteredAbility());
+        this.addAbility(new CantBeCounteredSourceAbility());
 
         // Creature spells you control can't be countered.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantBeCounteredControlledEffect(filterTarget, null, Duration.WhileOnBattlefield)));
diff --git a/Mage.Sets/src/mage/cards/t/ThoughtDistortion.java b/Mage.Sets/src/mage/cards/t/ThoughtDistortion.java
index 743f64978c..98ca1755d9 100644
--- a/Mage.Sets/src/mage/cards/t/ThoughtDistortion.java
+++ b/Mage.Sets/src/mage/cards/t/ThoughtDistortion.java
@@ -1,7 +1,7 @@
 package mage.cards.t;
 
 import mage.abilities.Ability;
-import mage.abilities.common.CantBeCounteredAbility;
+import mage.abilities.common.CantBeCounteredSourceAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -28,7 +28,7 @@ public final class ThoughtDistortion extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{B}{B}");
 
         // This spell can't be countered.
-        this.addAbility(new CantBeCounteredAbility());
+        this.addAbility(new CantBeCounteredSourceAbility());
 
         // Target opponent reveals their hand. Exile all noncreature, nonland cards from that player's hand and graveyard.
         this.getSpellAbility().addEffect(new ThoughtDistortionEffect());
diff --git a/Mage.Sets/src/mage/cards/v/VexingShusher.java b/Mage.Sets/src/mage/cards/v/VexingShusher.java
index a4a58098a4..c82812672e 100644
--- a/Mage.Sets/src/mage/cards/v/VexingShusher.java
+++ b/Mage.Sets/src/mage/cards/v/VexingShusher.java
@@ -5,7 +5,7 @@ import java.util.UUID;
 import mage.MageInt;
 import mage.MageObject;
 import mage.abilities.Ability;
-import mage.abilities.common.CantBeCounteredAbility;
+import mage.abilities.common.CantBeCounteredSourceAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
@@ -36,7 +36,7 @@ public final class VexingShusher extends CardImpl {
         this.toughness = new MageInt(2);
 
         // Vexing Shusher can't be countered.
-        this.addAbility(new CantBeCounteredAbility());
+        this.addAbility(new CantBeCounteredSourceAbility());
         // {R/G}: Target spell can't be countered.
         Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new VexingShusherCantCounterTargetEffect(), new ManaCostsImpl("{R/G}"));
         ability.addTarget(new TargetSpell());
diff --git a/Mage.Sets/src/mage/cards/v/VolcanicFallout.java b/Mage.Sets/src/mage/cards/v/VolcanicFallout.java
index cd3e245bf2..829ef5a25d 100644
--- a/Mage.Sets/src/mage/cards/v/VolcanicFallout.java
+++ b/Mage.Sets/src/mage/cards/v/VolcanicFallout.java
@@ -2,7 +2,7 @@
 package mage.cards.v;
 
 import java.util.UUID;
-import mage.abilities.common.CantBeCounteredAbility;
+import mage.abilities.common.CantBeCounteredSourceAbility;
 import mage.abilities.effects.common.DamageEverythingEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -19,7 +19,7 @@ public final class VolcanicFallout extends CardImpl {
 
 
         // Volcanic Fallout can't be countered.
-        this.addAbility(new CantBeCounteredAbility());
+        this.addAbility(new CantBeCounteredSourceAbility());
         // Volcanic Fallout deals 2 damage to each creature and each player.
         this.getSpellAbility().addEffect(new DamageEverythingEffect(2));
     }
diff --git a/Mage.Sets/src/mage/cards/w/WreakHavoc.java b/Mage.Sets/src/mage/cards/w/WreakHavoc.java
index ee85f28431..ea29cbbd8a 100644
--- a/Mage.Sets/src/mage/cards/w/WreakHavoc.java
+++ b/Mage.Sets/src/mage/cards/w/WreakHavoc.java
@@ -2,7 +2,7 @@
 package mage.cards.w;
 
 import java.util.UUID;
-import mage.abilities.common.CantBeCounteredAbility;
+import mage.abilities.common.CantBeCounteredSourceAbility;
 import mage.abilities.effects.common.DestroyTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -29,7 +29,7 @@ public final class WreakHavoc extends CardImpl {
         super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{R}{G}");
 
 
-        this.addAbility(new CantBeCounteredAbility());
+        this.addAbility(new CantBeCounteredSourceAbility());
         this.getSpellAbility().addEffect(new DestroyTargetEffect());
         this.getSpellAbility().addTarget(new TargetPermanent(filter));
     }
diff --git a/Mage/src/main/java/mage/abilities/common/CantBeCounteredSourceAbility.java b/Mage/src/main/java/mage/abilities/common/CantBeCounteredSourceAbility.java
new file mode 100644
index 0000000000..1d1fdaf76b
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/common/CantBeCounteredSourceAbility.java
@@ -0,0 +1,32 @@
+
+
+package mage.abilities.common;
+
+import mage.abilities.StaticAbility;
+import mage.abilities.effects.common.CantBeCounteredSourceEffect;
+import mage.constants.Zone;
+
+/**
+ *
+ * @author BetaSteward_at_googlemail.com
+ */
+public class CantBeCounteredSourceAbility extends StaticAbility {
+
+    public CantBeCounteredSourceAbility() {
+        super(Zone.STACK, new CantBeCounteredSourceEffect());
+    }
+
+    public CantBeCounteredSourceAbility(CantBeCounteredSourceAbility ability) {
+        super(ability);
+    }
+
+    @Override
+    public String getRule() {
+        return  "{this} can't be countered.";
+    }
+
+    @Override
+    public CantBeCounteredSourceAbility copy() {
+        return new CantBeCounteredSourceAbility(this);
+    }
+}

From a379a06485f65187bbbf996a4427483b67853685 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 25 Jun 2020 20:14:48 -0400
Subject: [PATCH 511/586] added new class for "this or another" triggered
 abilities

---
 .../src/mage/cards/a/AetherstormRoc.java      |  52 +++++----
 .../src/mage/cards/a/AmbuscadeShaman.java     |  65 +++--------
 .../src/mage/cards/a/ArchonOfRedemption.java  |  69 ++++++------
 .../mage/cards/a/AyaraFirstOfLocthwain.java   |  10 +-
 .../src/mage/cards/f/FieldOfTheDead.java      |   6 +-
 .../mage/cards/f/FlayerOfTheHatebound.java    |   2 +-
 .../mage/cards/g/GeneralKudroOfDrannith.java  |   6 +-
 .../src/mage/cards/g/GoblinAssassin.java      |  70 ++++--------
 .../src/mage/cards/g/GontisAetherHeart.java   |  25 ++---
 .../src/mage/cards/g/GruulRagebeast.java      |  18 +---
 .../src/mage/cards/h/HammerOfNazahn.java      |  24 ++---
 .../src/mage/cards/k/KapshoKitefins.java      |  35 +++---
 .../src/mage/cards/m/MarduWoeReaper.java      | 100 +++--------------
 .../src/mage/cards/m/MaritLagesSlumber.java   |   8 +-
 .../src/mage/cards/n/NebelgastHerald.java     |  31 +++---
 Mage.Sets/src/mage/cards/n/NoxiousGhoul.java  |  33 ++----
 .../mage/cards/o/OathOfTheAncientWood.java    |  29 ++---
 .../src/mage/cards/q/QasaliSlingers.java      |  64 +++--------
 Mage.Sets/src/mage/cards/r/RisenReef.java     |  11 +-
 .../src/mage/cards/s/SageOfTheFalls.java      |  11 +-
 .../src/mage/cards/s/ScourgeOfValkas.java     |  65 ++++++-----
 Mage.Sets/src/mage/cards/s/SerumTank.java     |  71 +++---------
 .../mage/cards/s/SethronHurloonGeneral.java   |  12 +--
 .../mage/cards/t/ThievesGuildEnforcer.java    |  12 +--
 Mage.Sets/src/mage/cards/t/ThornMammoth.java  |   9 +-
 Mage.Sets/src/mage/cards/t/ThrasherBrute.java |  31 ++----
 .../cards/u/UneshCriosphinxSovereign.java     | 101 +++++-------------
 .../abilityword/ConstellationAbility.java     |   2 +-
 .../EntersBattlefieldAllTriggeredAbility.java |  17 ++-
 ...tlefieldThisOrAnotherTriggeredAbility.java |  62 +++++++++++
 .../main/java/mage/filter/StaticFilters.java  |   8 +-
 .../common/FilterEquipmentPermanent.java      |   2 +-
 32 files changed, 389 insertions(+), 672 deletions(-)
 create mode 100644 Mage/src/main/java/mage/abilities/common/EntersBattlefieldThisOrAnotherTriggeredAbility.java

diff --git a/Mage.Sets/src/mage/cards/a/AetherstormRoc.java b/Mage.Sets/src/mage/cards/a/AetherstormRoc.java
index aff1bf10b0..a476bfef1a 100644
--- a/Mage.Sets/src/mage/cards/a/AetherstormRoc.java
+++ b/Mage.Sets/src/mage/cards/a/AetherstormRoc.java
@@ -4,7 +4,7 @@ package mage.cards.a;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.AttacksTriggeredAbility;
-import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility;
+import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility;
 import mage.abilities.costs.common.PayEnergyCost;
 import mage.abilities.effects.common.DoIfCostPaid;
 import mage.abilities.effects.common.TapTargetEffect;
@@ -16,11 +16,11 @@ import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.SubType;
 import mage.counters.CounterType;
+import mage.filter.FilterPermanent;
+import mage.filter.StaticFilters;
 import mage.filter.common.FilterCreaturePermanent;
-import mage.filter.predicate.permanent.ControllerIdPredicate;
-import mage.game.Game;
-import mage.target.common.TargetCreaturePermanent;
-import mage.target.targetadjustment.TargetAdjuster;
+import mage.filter.predicate.permanent.DefendingPlayerControlsPredicate;
+import mage.target.TargetPermanent;
 
 import java.util.UUID;
 
@@ -29,6 +29,13 @@ import java.util.UUID;
  */
 public final class AetherstormRoc extends CardImpl {
 
+    private static final FilterPermanent filter
+            = new FilterCreaturePermanent("creature defending player controls");
+
+    static {
+        filter.add(DefendingPlayerControlsPredicate.instance);
+    }
+
     public AetherstormRoc(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{W}");
         this.subtype.add(SubType.BIRD);
@@ -37,21 +44,24 @@ public final class AetherstormRoc extends CardImpl {
 
         // Flying
         this.addAbility(FlyingAbility.getInstance());
+
         // Whenever Aetherstorm Roc or another creature enters the battlefield under your control, you get {E}.
-        this.addAbility(new EntersBattlefieldControlledTriggeredAbility(new GetEnergyCountersControllerEffect(1), new FilterCreaturePermanent("{this} or another creature")));
+        this.addAbility(new EntersBattlefieldThisOrAnotherTriggeredAbility(
+                new GetEnergyCountersControllerEffect(1),
+                StaticFilters.FILTER_PERMANENT_CREATURE, false, true
+        ));
 
         // Whenever Aetherstorm Roc attacks, you may pay {E}{E}. If you do, put a +1/+1 counter on it and tap up to one target creature defending player controls.
-        DoIfCostPaid doIfCostPaidEffect = new DoIfCostPaid(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), new PayEnergyCost(2));
-        doIfCostPaidEffect.addEffect(new TapTargetEffect());
-        Ability ability = new AttacksTriggeredAbility(doIfCostPaidEffect, false,
-                "Whenever {this} attacks you may pay {E}{E}. If you do, put a +1/+1 counter on it and tap up to one target creature defending player controls.");
-        ability.addTarget(new TargetCreaturePermanent(0, 1, new FilterCreaturePermanent("creature defending player controls"), false));
-        ability.setTargetAdjuster(AetherstormRocAdjuster.instance);
+        Ability ability = new AttacksTriggeredAbility(new DoIfCostPaid(
+                new AddCountersSourceEffect(CounterType.P1P1.createInstance())
+                        .setText("put a +1/+1 counter on it"),
+                new PayEnergyCost(2)
+        ).addEffect(new TapTargetEffect().concatBy("and")), false);
+        ability.addTarget(new TargetPermanent(0, 1, filter, false));
         this.addAbility(ability);
-
     }
 
-    public AetherstormRoc(final AetherstormRoc card) {
+    private AetherstormRoc(final AetherstormRoc card) {
         super(card);
     }
 
@@ -61,17 +71,3 @@ public final class AetherstormRoc extends CardImpl {
         return new AetherstormRoc(this);
     }
 }
-
-enum AetherstormRocAdjuster implements TargetAdjuster {
-    instance;
-
-    @Override
-    public void adjustTargets(Ability ability, Game game) {
-        ability.getTargets().clear();
-        FilterCreaturePermanent filter = new FilterCreaturePermanent("creature defending player controls");
-        UUID defenderId = game.getCombat().getDefenderId(ability.getSourceId());
-        filter.add(new ControllerIdPredicate(defenderId));
-        TargetCreaturePermanent target = new TargetCreaturePermanent(0, 1, filter, false);
-        ability.addTarget(target);
-    }
-}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/a/AmbuscadeShaman.java b/Mage.Sets/src/mage/cards/a/AmbuscadeShaman.java
index 2c8e04683a..65390caee2 100644
--- a/Mage.Sets/src/mage/cards/a/AmbuscadeShaman.java
+++ b/Mage.Sets/src/mage/cards/a/AmbuscadeShaman.java
@@ -1,25 +1,20 @@
-
 package mage.cards.a;
 
-import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.TriggeredAbilityImpl;
-import mage.abilities.effects.Effect;
+import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility;
 import mage.abilities.effects.common.continuous.BoostTargetEffect;
 import mage.abilities.keyword.DashAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.Duration;
+import mage.constants.SetTargetPointer;
 import mage.constants.SubType;
-import mage.constants.Zone;
-import mage.game.Game;
-import mage.game.events.GameEvent;
-import mage.game.permanent.Permanent;
-import mage.target.targetpointer.FixedTarget;
+import mage.filter.StaticFilters;
+
+import java.util.UUID;
 
 /**
- *
  * @author LevelX2
  */
 public final class AmbuscadeShaman extends CardImpl {
@@ -32,16 +27,18 @@ public final class AmbuscadeShaman extends CardImpl {
         this.toughness = new MageInt(2);
 
         // Whenever Ambuscade Shaman or another creature enters the battlefield under your control, that creature gets +2/+2 until end of turn.
-        Effect effect = new BoostTargetEffect(2, 2, Duration.EndOfTurn);
-        effect.setText("that creature gets +2/+2 until end of turn");
-        this.addAbility(new AmbuscadeShamanTriggeredAbility(effect));
+        this.addAbility(new EntersBattlefieldThisOrAnotherTriggeredAbility(
+                new BoostTargetEffect(2, 2, Duration.EndOfTurn)
+                        .setText("that creature gets +2/+2 until end of turn"),
+                StaticFilters.FILTER_PERMANENT_CREATURE, false,
+                SetTargetPointer.PERMANENT, true
+        ));
 
         // Dash {3}{B} <i>(You may cast this spell for its dash cost. If you do, it gains haste, and it's returned from the battlefield to its owner's hand at the beginning of the next end step.)</i>);
         this.addAbility(new DashAbility(this, "{3}{B}"));
-
     }
 
-    public AmbuscadeShaman(final AmbuscadeShaman card) {
+    private AmbuscadeShaman(final AmbuscadeShaman card) {
         super(card);
     }
 
@@ -50,41 +47,3 @@ public final class AmbuscadeShaman extends CardImpl {
         return new AmbuscadeShaman(this);
     }
 }
-
-class AmbuscadeShamanTriggeredAbility extends TriggeredAbilityImpl {
-
-    AmbuscadeShamanTriggeredAbility(Effect effect) {
-        super(Zone.BATTLEFIELD, effect, false);
-    }
-
-    AmbuscadeShamanTriggeredAbility(final AmbuscadeShamanTriggeredAbility ability) {
-        super(ability);
-    }
-
-    @Override
-    public AmbuscadeShamanTriggeredAbility copy() {
-        return new AmbuscadeShamanTriggeredAbility(this);
-    }
-
-    @Override
-    public boolean checkEventType(GameEvent event, Game game) {
-        return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD;
-    }
-
-    @Override
-    public boolean checkTrigger(GameEvent event, Game game) {
-        UUID targetId = event.getTargetId();
-        Permanent permanent = game.getPermanent(targetId);
-        if (permanent.isControlledBy(this.controllerId)
-                && permanent.isCreature()) {
-            this.getEffects().setTargetPointer(new FixedTarget(permanent, game));
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    public String getRule() {
-        return "Whenever {this} or another creature enters the battlefield under your control, that creature gets +2/+2 until end of turn.";
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/a/ArchonOfRedemption.java b/Mage.Sets/src/mage/cards/a/ArchonOfRedemption.java
index c450ed3074..f382139061 100644
--- a/Mage.Sets/src/mage/cards/a/ArchonOfRedemption.java
+++ b/Mage.Sets/src/mage/cards/a/ArchonOfRedemption.java
@@ -1,18 +1,22 @@
 package mage.cards.a;
 
 import mage.MageInt;
-import mage.abilities.TriggeredAbilityImpl;
-import mage.abilities.effects.common.GainLifeEffect;
+import mage.abilities.Ability;
+import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility;
+import mage.abilities.effects.OneShotEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
+import mage.constants.Outcome;
+import mage.constants.SetTargetPointer;
 import mage.constants.SubType;
-import mage.constants.Zone;
+import mage.filter.FilterPermanent;
+import mage.filter.common.FilterCreaturePermanent;
+import mage.filter.predicate.mageobject.AbilityPredicate;
 import mage.game.Game;
-import mage.game.events.GameEvent;
-import mage.game.events.GameEvent.EventType;
 import mage.game.permanent.Permanent;
+import mage.players.Player;
 
 import java.util.UUID;
 
@@ -21,6 +25,12 @@ import java.util.UUID;
  */
 public final class ArchonOfRedemption extends CardImpl {
 
+    private static final FilterPermanent filter = new FilterCreaturePermanent("creature with flying");
+
+    static {
+        filter.add(new AbilityPredicate(FlyingAbility.class));
+    }
+
     public ArchonOfRedemption(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}{W}");
         this.subtype.add(SubType.ARCHON);
@@ -29,11 +39,14 @@ public final class ArchonOfRedemption extends CardImpl {
         this.toughness = new MageInt(4);
 
         this.addAbility(FlyingAbility.getInstance());
+
         // Whenever Archon of Redemption or another creature with flying enters the battlefield under your control, you may gain life equal to that creature's power.
-        this.addAbility(new ArchonOfRedemptionTriggeredAbility());
+        this.addAbility(new EntersBattlefieldThisOrAnotherTriggeredAbility(
+                new ArchonOfRedemptionEffect(), filter, true, SetTargetPointer.PERMANENT, true
+        ));
     }
 
-    public ArchonOfRedemption(final ArchonOfRedemption card) {
+    private ArchonOfRedemption(final ArchonOfRedemption card) {
         super(card);
     }
 
@@ -43,43 +56,29 @@ public final class ArchonOfRedemption extends CardImpl {
     }
 }
 
-class ArchonOfRedemptionTriggeredAbility extends TriggeredAbilityImpl {
+class ArchonOfRedemptionEffect extends OneShotEffect {
 
-    ArchonOfRedemptionTriggeredAbility() {
-        super(Zone.BATTLEFIELD, null, true);
+    ArchonOfRedemptionEffect() {
+        super(Outcome.Benefit);
+        staticText = "gain life equal to that creature's power";
     }
 
-    ArchonOfRedemptionTriggeredAbility(final ArchonOfRedemptionTriggeredAbility ability) {
-        super(ability);
+    private ArchonOfRedemptionEffect(final ArchonOfRedemptionEffect effect) {
+        super(effect);
     }
 
     @Override
-    public ArchonOfRedemptionTriggeredAbility copy() {
-        return new ArchonOfRedemptionTriggeredAbility(this);
+    public ArchonOfRedemptionEffect copy() {
+        return new ArchonOfRedemptionEffect(this);
     }
 
     @Override
-    public boolean checkEventType(GameEvent event, Game game) {
-        return event.getType() == EventType.ENTERS_THE_BATTLEFIELD;
-    }
-
-    @Override
-    public boolean checkTrigger(GameEvent event, Game game) {
-        Permanent permanent = game.getPermanent(event.getTargetId());
-        if (permanent != null
-                && permanent.isControlledBy(getControllerId())
-                && permanent.isCreature()
-                && (permanent.getId().equals(getSourceId())
-                || (permanent.getAbilities().contains(FlyingAbility.getInstance())))) {
-            this.getEffects().clear();
-            this.addEffect(new GainLifeEffect(permanent.getPower().getValue()));
-            return true;
+    public boolean apply(Game game, Ability source) {
+        Player player = game.getPlayer(source.getControllerId());
+        Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
+        if (player == null || permanent == null) {
+            return false;
         }
-        return false;
-    }
-
-    @Override
-    public String getRule() {
-        return "Whenever {this} or another creature with flying enters the battlefield under your control, you may gain life equal to that creature's power.";
+        return player.gainLife(permanent.getPower().getValue(), game, source) > 0;
     }
 }
diff --git a/Mage.Sets/src/mage/cards/a/AyaraFirstOfLocthwain.java b/Mage.Sets/src/mage/cards/a/AyaraFirstOfLocthwain.java
index b7f6b297ca..0e45252d44 100644
--- a/Mage.Sets/src/mage/cards/a/AyaraFirstOfLocthwain.java
+++ b/Mage.Sets/src/mage/cards/a/AyaraFirstOfLocthwain.java
@@ -3,7 +3,7 @@ package mage.cards.a;
 import mage.MageInt;
 import mage.ObjectColor;
 import mage.abilities.Ability;
-import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility;
+import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.common.SacrificeTargetCost;
 import mage.abilities.costs.common.TapSourceCost;
@@ -20,10 +20,10 @@ import mage.filter.common.FilterControlledCreaturePermanent;
 import mage.filter.common.FilterControlledPermanent;
 import mage.filter.common.FilterCreaturePermanent;
 import mage.filter.predicate.mageobject.ColorPredicate;
+import mage.filter.predicate.permanent.AnotherPredicate;
 import mage.target.common.TargetControlledPermanent;
 
 import java.util.UUID;
-import mage.filter.predicate.permanent.AnotherPredicate;
 
 /**
  * @author TheElk801
@@ -31,7 +31,7 @@ import mage.filter.predicate.permanent.AnotherPredicate;
 public final class AyaraFirstOfLocthwain extends CardImpl {
 
     private static final FilterPermanent filter
-            = new FilterCreaturePermanent("{this} or another black creature");
+            = new FilterCreaturePermanent("black creature");
     private static final FilterControlledPermanent filter2
             = new FilterControlledCreaturePermanent("another black creature");
 
@@ -51,7 +51,9 @@ public final class AyaraFirstOfLocthwain extends CardImpl {
         this.toughness = new MageInt(3);
 
         // Whenever Ayara, First of Locthwain or another black creature enters the battlefield under your control, each opponent loses 1 life and you gain 1 life.
-        Ability ability = new EntersBattlefieldControlledTriggeredAbility(new LoseLifeOpponentsEffect(1), filter);
+        Ability ability = new EntersBattlefieldThisOrAnotherTriggeredAbility(
+                new LoseLifeOpponentsEffect(1), filter, false, true
+        );
         ability.addEffect(new GainLifeEffect(1).concatBy("and"));
         this.addAbility(ability);
 
diff --git a/Mage.Sets/src/mage/cards/f/FieldOfTheDead.java b/Mage.Sets/src/mage/cards/f/FieldOfTheDead.java
index e4eca676bd..f224145654 100644
--- a/Mage.Sets/src/mage/cards/f/FieldOfTheDead.java
+++ b/Mage.Sets/src/mage/cards/f/FieldOfTheDead.java
@@ -1,8 +1,8 @@
 package mage.cards.f;
 
 import mage.abilities.Ability;
-import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldTappedAbility;
+import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility;
 import mage.abilities.condition.Condition;
 import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
@@ -33,8 +33,8 @@ public final class FieldOfTheDead extends CardImpl {
 
         // Whenever Field of the Dead or another land enters the battlefield under your control, if you control seven or more lands with different names, create a 2/2 black Zombie creature token.
         this.addAbility(new ConditionalInterveningIfTriggeredAbility(
-                new EntersBattlefieldControlledTriggeredAbility(
-                        new CreateTokenEffect(new ZombieToken()), StaticFilters.FILTER_LAND
+                new EntersBattlefieldThisOrAnotherTriggeredAbility(
+                        new CreateTokenEffect(new ZombieToken()), StaticFilters.FILTER_LAND, false, true
                 ), FieldOfTheDeadCondition.instance, "Whenever {this} or another land " +
                 "enters the battlefield under your control, if you control seven or more lands with different names, " +
                 "create a 2/2 black Zombie creature token."
diff --git a/Mage.Sets/src/mage/cards/f/FlayerOfTheHatebound.java b/Mage.Sets/src/mage/cards/f/FlayerOfTheHatebound.java
index b22d7cd689..c25c1cc6fb 100644
--- a/Mage.Sets/src/mage/cards/f/FlayerOfTheHatebound.java
+++ b/Mage.Sets/src/mage/cards/f/FlayerOfTheHatebound.java
@@ -73,7 +73,7 @@ class FlayerTriggeredAbility extends TriggeredAbilityImpl {
         if (permanent != null
                 && ((EntersTheBattlefieldEvent) event).getFromZone() == Zone.GRAVEYARD
                 && permanent.isOwnedBy(controllerId)
-                && permanent.isCreature()) {
+                && (permanent.isCreature() || permanent.getId().equals(getSourceId()))) {
             Effect effect = this.getEffects().get(0);
             effect.setValue("damageSource", event.getTargetId());
             return true;
diff --git a/Mage.Sets/src/mage/cards/g/GeneralKudroOfDrannith.java b/Mage.Sets/src/mage/cards/g/GeneralKudroOfDrannith.java
index 4a24e6d934..a9c81282e6 100644
--- a/Mage.Sets/src/mage/cards/g/GeneralKudroOfDrannith.java
+++ b/Mage.Sets/src/mage/cards/g/GeneralKudroOfDrannith.java
@@ -2,7 +2,7 @@ package mage.cards.g;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility;
+import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.costs.common.SacrificeTargetCost;
@@ -32,7 +32,7 @@ public final class GeneralKudroOfDrannith extends CardImpl {
     private static final FilterCreaturePermanent filter
             = new FilterCreaturePermanent(SubType.HUMAN, "Humans");
     private static final FilterPermanent filter2
-            = new FilterPermanent(SubType.HUMAN, "{this} or another Human");
+            = new FilterPermanent(SubType.HUMAN, "Human");
     private static final FilterCard filter3
             = new FilterCard("card from an opponent's graveyard");
     private static final FilterControlledPermanent filter4
@@ -59,7 +59,7 @@ public final class GeneralKudroOfDrannith extends CardImpl {
         )));
 
         // Whenever General Kudro of Drannith or another Human enters the battlefield under your control, exile target card from an opponent's graveyard.
-        Ability ability = new EntersBattlefieldControlledTriggeredAbility(new ExileTargetEffect(), filter2);
+        Ability ability = new EntersBattlefieldThisOrAnotherTriggeredAbility(new ExileTargetEffect(), filter2, false, true);
         ability.addTarget(new TargetCardInOpponentsGraveyard(filter3));
         this.addAbility(ability);
 
diff --git a/Mage.Sets/src/mage/cards/g/GoblinAssassin.java b/Mage.Sets/src/mage/cards/g/GoblinAssassin.java
index c94cb90e39..20e0ebebe1 100644
--- a/Mage.Sets/src/mage/cards/g/GoblinAssassin.java
+++ b/Mage.Sets/src/mage/cards/g/GoblinAssassin.java
@@ -1,44 +1,44 @@
-
 package mage.cards.g;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.TriggeredAbilityImpl;
+import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.constants.SubType;
-import mage.constants.Zone;
+import mage.filter.FilterPermanent;
 import mage.game.Game;
-import mage.game.events.GameEvent;
-import mage.game.events.GameEvent.EventType;
 import mage.game.permanent.Permanent;
 import mage.players.Player;
 import mage.target.common.TargetControlledCreaturePermanent;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
 /**
  * @author BursegSardaukar
  */
 public final class GoblinAssassin extends CardImpl {
 
+    private static final FilterPermanent filter = new FilterPermanent(SubType.GOBLIN, "Goblin");
+
     public GoblinAssassin(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{R}{R}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{R}");
         this.subtype.add(SubType.GOBLIN);
         this.subtype.add(SubType.ASSASSIN);
-        
+
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
 
         // Whenever Goblin Assassin or another Goblin enters the battlefield, each player flips a coin. Each player whose coin comes up tails sacrifices a creature.
-        this.addAbility(new GoblinAssassinTriggeredAbiliy());
+        this.addAbility(new EntersBattlefieldThisOrAnotherTriggeredAbility(new GoblinAssassinTriggeredEffect(), filter));
     }
 
-    public GoblinAssassin(final GoblinAssassin card) {
+    private GoblinAssassin(final GoblinAssassin card) {
         super(card);
     }
 
@@ -48,55 +48,21 @@ public final class GoblinAssassin extends CardImpl {
     }
 }
 
-class GoblinAssassinTriggeredAbiliy extends TriggeredAbilityImpl {
-    GoblinAssassinTriggeredAbiliy() {
-        super(Zone.BATTLEFIELD, new GoblinAssassinTriggeredEffect(), false);
-    }
-
-    GoblinAssassinTriggeredAbiliy(final GoblinAssassinTriggeredAbiliy ability) {
-        super(ability);
-    }
-
-    @Override
-    public GoblinAssassinTriggeredAbiliy copy() {
-        return new GoblinAssassinTriggeredAbiliy(this);
-    }
-
-    @Override
-    public boolean checkEventType(GameEvent event, Game game) {
-        return event.getType() == EventType.ENTERS_THE_BATTLEFIELD;
-    }
-
-    @Override
-    public boolean checkTrigger(GameEvent event, Game game) {
-        UUID targetId = event.getTargetId();
-        Permanent permanent = game.getPermanent(targetId);
-        if ((targetId.equals(this.getSourceId())) || (permanent.hasSubtype(SubType.GOBLIN, game) && !targetId.equals(this.getSourceId()))) {
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    public String getRule() {
-        return "Whenever {this} or another Goblin enters battlefield, each player flips a coin. Each player whose coin comes up tails sacrifices a creature.";
-    }
-}
-
 class GoblinAssassinTriggeredEffect extends OneShotEffect {
+
     GoblinAssassinTriggeredEffect() {
         super(Outcome.Sacrifice);
     }
 
-    GoblinAssassinTriggeredEffect(final GoblinAssassinTriggeredEffect effect) {
+    private GoblinAssassinTriggeredEffect(final GoblinAssassinTriggeredEffect effect) {
         super(effect);
     }
 
     @Override
     public boolean apply(Game game, Ability source) {
-       List<UUID> perms = new ArrayList<>();
+        List<UUID> perms = new ArrayList<>();
         Player controller = game.getPlayer(source.getControllerId());
-        if (controller != null) {            
+        if (controller != null) {
             for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
                 Player player = game.getPlayer(playerId);
                 if (player != null && !player.flipCoin(source, game, false)) {
@@ -114,7 +80,7 @@ class GoblinAssassinTriggeredEffect extends OneShotEffect {
                     permanent.sacrifice(source.getSourceId(), game);
                 }
             }
-            return true;        
+            return true;
         }
         return false;
     }
@@ -123,4 +89,4 @@ class GoblinAssassinTriggeredEffect extends OneShotEffect {
     public GoblinAssassinTriggeredEffect copy() {
         return new GoblinAssassinTriggeredEffect(this);
     }
-}
\ No newline at end of file
+}
diff --git a/Mage.Sets/src/mage/cards/g/GontisAetherHeart.java b/Mage.Sets/src/mage/cards/g/GontisAetherHeart.java
index edac7052ff..4f35197bd9 100644
--- a/Mage.Sets/src/mage/cards/g/GontisAetherHeart.java
+++ b/Mage.Sets/src/mage/cards/g/GontisAetherHeart.java
@@ -1,9 +1,8 @@
 
 package mage.cards.g;
 
-import java.util.UUID;
 import mage.abilities.Ability;
-import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility;
+import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.common.ExileSourceCost;
 import mage.abilities.costs.common.PayEnergyCost;
@@ -13,37 +12,33 @@ import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.SuperType;
-import mage.constants.TargetController;
-import mage.constants.Zone;
-import mage.filter.common.FilterArtifactPermanent;
+import mage.filter.StaticFilters;
+
+import java.util.UUID;
 
 /**
- *
  * @author fireshoes
  */
 public final class GontisAetherHeart extends CardImpl {
 
-    private static final FilterArtifactPermanent filter = new FilterArtifactPermanent("{this} or another artifact");
-
-    static {
-        filter.add(TargetController.YOU.getControllerPredicate());
-    }
-
     public GontisAetherHeart(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{6}");
 
         addSuperType(SuperType.LEGENDARY);
 
         // Whenever Gonti's Aether Heart or another artifact enters the battlefield under your control, you get {E}{E} <i>(two energy counters).
-        this.addAbility(new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, new GetEnergyCountersControllerEffect(2), filter, false));
+        this.addAbility(new EntersBattlefieldThisOrAnotherTriggeredAbility(
+                new GetEnergyCountersControllerEffect(2),
+                StaticFilters.FILTER_PERMANENT_ARTIFACT, false, true
+        ));
 
         // Pay {E}{E}{E}{E}{E}{E}{E}{E}, Exile Gonti's Aether Heart: Take an extra turn after this one.
-        Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddExtraTurnControllerEffect(), new PayEnergyCost(8));
+        Ability ability = new SimpleActivatedAbility(new AddExtraTurnControllerEffect(), new PayEnergyCost(8));
         ability.addCost(new ExileSourceCost());
         this.addAbility(ability);
     }
 
-    public GontisAetherHeart(final GontisAetherHeart card) {
+    private GontisAetherHeart(final GontisAetherHeart card) {
         super(card);
     }
 
diff --git a/Mage.Sets/src/mage/cards/g/GruulRagebeast.java b/Mage.Sets/src/mage/cards/g/GruulRagebeast.java
index 7a7ec1517a..2ac9f509ae 100644
--- a/Mage.Sets/src/mage/cards/g/GruulRagebeast.java
+++ b/Mage.Sets/src/mage/cards/g/GruulRagebeast.java
@@ -20,6 +20,7 @@ import mage.game.events.GameEvent;
 import mage.game.events.GameEvent.EventType;
 import mage.game.permanent.Permanent;
 import mage.target.common.TargetCreaturePermanent;
+import mage.target.common.TargetOpponentsCreaturePermanent;
 import mage.target.targetpointer.FixedTarget;
 
 /**
@@ -28,12 +29,6 @@ import mage.target.targetpointer.FixedTarget;
  */
 public final class GruulRagebeast extends CardImpl {
 
-    private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent("creature an opponent controls");
-
-    static {
-        filter2.add(TargetController.OPPONENT.getControllerPredicate());
-    }
-
     public GruulRagebeast(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{R}{G}");
         this.subtype.add(SubType.BEAST);
@@ -42,10 +37,7 @@ public final class GruulRagebeast extends CardImpl {
         this.toughness = new MageInt(6);
 
         // Whenever Gruul Ragebeast or another creature enters the battlefield under your control, that creature fights target creature an opponent controls.
-        Ability ability = new GruulRagebeastTriggeredAbility();
-
-        ability.addTarget(new TargetCreaturePermanent(filter2));
-        this.addAbility(ability);
+        this.addAbility(new GruulRagebeastTriggeredAbility());
     }
 
     public GruulRagebeast(final GruulRagebeast card) {
@@ -61,10 +53,10 @@ public final class GruulRagebeast extends CardImpl {
 class GruulRagebeastTriggeredAbility extends TriggeredAbilityImpl {
 
     GruulRagebeastTriggeredAbility() {
-        super(Zone.BATTLEFIELD, new GruulRagebeastEffect(), false);
+        super(Zone.BATTLEFIELD, new GruulRagebeastEffect(), false);this.addTarget(new TargetOpponentsCreaturePermanent());
     }
 
-    GruulRagebeastTriggeredAbility(final GruulRagebeastTriggeredAbility ability) {
+  private   GruulRagebeastTriggeredAbility(final GruulRagebeastTriggeredAbility ability) {
         super(ability);
     }
 
@@ -106,7 +98,7 @@ class GruulRagebeastEffect extends OneShotEffect {
         super(Outcome.Damage);
     }
 
-    GruulRagebeastEffect(final GruulRagebeastEffect effect) {
+  private   GruulRagebeastEffect(final GruulRagebeastEffect effect) {
         super(effect);
     }
 
diff --git a/Mage.Sets/src/mage/cards/h/HammerOfNazahn.java b/Mage.Sets/src/mage/cards/h/HammerOfNazahn.java
index a973a49601..44e9f58776 100644
--- a/Mage.Sets/src/mage/cards/h/HammerOfNazahn.java
+++ b/Mage.Sets/src/mage/cards/h/HammerOfNazahn.java
@@ -1,9 +1,8 @@
 
 package mage.cards.h;
 
-import java.util.UUID;
 import mage.abilities.Ability;
-import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility;
+import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.costs.mana.GenericManaCost;
 import mage.abilities.effects.Effect;
@@ -14,34 +13,29 @@ import mage.abilities.keyword.EquipAbility;
 import mage.abilities.keyword.IndestructibleAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.AttachmentType;
-import mage.constants.CardType;
-import mage.constants.Outcome;
-import mage.constants.SetTargetPointer;
-import mage.constants.SubType;
-import mage.constants.SuperType;
-import mage.constants.Zone;
-import mage.filter.common.FilterEquipmentPermanent;
+import mage.constants.*;
+import mage.filter.StaticFilters;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
 import mage.players.Player;
 import mage.target.common.TargetControlledCreaturePermanent;
 
+import java.util.UUID;
+
 /**
- *
  * @author Saga
  */
 public final class HammerOfNazahn extends CardImpl {
-    
-    private static final FilterEquipmentPermanent filter = new FilterEquipmentPermanent("{this} or another Equipment");
 
     public HammerOfNazahn(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}");
         this.addSuperType(SuperType.LEGENDARY);
         this.subtype.add(SubType.EQUIPMENT);
-        
+
         // Whenever Hammer of Nazahn or another Equipment enters the battlefiend under your control, you may attach that Equipment to target creature you control.
-        Ability ability = new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, new HammerOfNazahnEffect(), filter, true, SetTargetPointer.PERMANENT, "");
+        Ability ability = new EntersBattlefieldThisOrAnotherTriggeredAbility(
+                new HammerOfNazahnEffect(), StaticFilters.FILTER_PERMANENT_EQUIPMENT, true, SetTargetPointer.PERMANENT, true
+        );
         ability.addTarget(new TargetControlledCreaturePermanent());
         this.addAbility(ability);
 
diff --git a/Mage.Sets/src/mage/cards/k/KapshoKitefins.java b/Mage.Sets/src/mage/cards/k/KapshoKitefins.java
index db6aa7c954..da025e429c 100644
--- a/Mage.Sets/src/mage/cards/k/KapshoKitefins.java
+++ b/Mage.Sets/src/mage/cards/k/KapshoKitefins.java
@@ -1,37 +1,26 @@
-
 package mage.cards.k;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility;
+import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility;
 import mage.abilities.effects.common.TapTargetEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.SubType;
-import mage.constants.TargetController;
-import mage.constants.Zone;
-import mage.filter.common.FilterCreaturePermanent;
-import mage.target.common.TargetCreaturePermanent;
+import mage.filter.StaticFilters;
+import mage.target.common.TargetOpponentsCreaturePermanent;
+
+import java.util.UUID;
 
 /**
- *
  * @author LevelX2
  */
 public final class KapshoKitefins extends CardImpl {
 
-    private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("{this} or another creature");
-    private static final FilterCreaturePermanent filterTarget = new FilterCreaturePermanent("creature an opponent controls");
-
-    static {
-        filter.add(TargetController.YOU.getControllerPredicate());
-        filterTarget.add(TargetController.OPPONENT.getControllerPredicate());
-    }
-
     public KapshoKitefins(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{U}{U}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}{U}");
         this.subtype.add(SubType.FISH);
 
         this.color.setBlue(true);
@@ -40,14 +29,16 @@ public final class KapshoKitefins extends CardImpl {
 
         // Flying
         this.addAbility(FlyingAbility.getInstance());
-        // Whenever Kapsho Kitefins or another creature enters the battlefield under your control, tap target creature an opponent controls.
-        Ability ability = new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, new TapTargetEffect(), filter,  false);
-        ability.addTarget(new TargetCreaturePermanent(filterTarget));
-        this.addAbility(ability);
 
+        // Whenever Kapsho Kitefins or another creature enters the battlefield under your control, tap target creature an opponent controls.
+        Ability ability = new EntersBattlefieldThisOrAnotherTriggeredAbility(
+                new TapTargetEffect(), StaticFilters.FILTER_PERMANENT_CREATURE, false, true
+        );
+        ability.addTarget(new TargetOpponentsCreaturePermanent());
+        this.addAbility(ability);
     }
 
-    public KapshoKitefins(final KapshoKitefins card) {
+    private KapshoKitefins(final KapshoKitefins card) {
         super(card);
     }
 
diff --git a/Mage.Sets/src/mage/cards/m/MarduWoeReaper.java b/Mage.Sets/src/mage/cards/m/MarduWoeReaper.java
index c87fd0e508..4a9e2061cc 100644
--- a/Mage.Sets/src/mage/cards/m/MarduWoeReaper.java
+++ b/Mage.Sets/src/mage/cards/m/MarduWoeReaper.java
@@ -1,45 +1,46 @@
 
 package mage.cards.m;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.TriggeredAbilityImpl;
-import mage.abilities.effects.OneShotEffect;
-import mage.cards.Card;
+import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility;
+import mage.abilities.effects.common.ExileTargetEffect;
+import mage.abilities.effects.common.GainLifeEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.Outcome;
 import mage.constants.SubType;
-import mage.constants.Zone;
-import mage.filter.common.FilterCreatureCard;
-import mage.game.Game;
-import mage.game.events.GameEvent;
-import mage.game.permanent.Permanent;
-import mage.players.Player;
+import mage.filter.FilterPermanent;
+import mage.filter.StaticFilters;
 import mage.target.common.TargetCardInGraveyard;
 
+import java.util.UUID;
+
 /**
- *
  * @author emerald000
  */
 public final class MarduWoeReaper extends CardImpl {
 
+    private static final FilterPermanent filter = new FilterPermanent(SubType.WARRIOR, "Warrior");
+
     public MarduWoeReaper(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{W}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}");
         this.subtype.add(SubType.HUMAN);
         this.subtype.add(SubType.WARRIOR);
         this.power = new MageInt(2);
         this.toughness = new MageInt(1);
 
         // Whenever Mardu Woe-Reaper or another Warrior enters the battlefield under your control, you may exile target creature card from a graveyard. If you do, you gain 1 life.
-        Ability ability = new MarduWoeReaperTriggeredAbility();
-        ability.addTarget(new TargetCardInGraveyard(new FilterCreatureCard("creature card from a graveyard")));
+        Ability ability = new EntersBattlefieldThisOrAnotherTriggeredAbility(
+                new ExileTargetEffect("exile target creature card from a graveyard.")
+                , filter, true, true
+        );
+        ability.addEffect(new GainLifeEffect(1).concatBy("If you do,"));
+        ability.addTarget(new TargetCardInGraveyard(StaticFilters.FILTER_CARD_CREATURE));
         this.addAbility(ability);
     }
 
-    public MarduWoeReaper(final MarduWoeReaper card) {
+    private MarduWoeReaper(final MarduWoeReaper card) {
         super(card);
     }
 
@@ -48,70 +49,3 @@ public final class MarduWoeReaper extends CardImpl {
         return new MarduWoeReaper(this);
     }
 }
-
-class MarduWoeReaperTriggeredAbility extends TriggeredAbilityImpl {
-
-    MarduWoeReaperTriggeredAbility() {
-        super(Zone.BATTLEFIELD, new MarduWoeReaperEffect(), true);
-    }
-
-    MarduWoeReaperTriggeredAbility(final MarduWoeReaperTriggeredAbility ability) {
-        super(ability);
-    }
-
-    @Override
-    public MarduWoeReaperTriggeredAbility copy() {
-        return new MarduWoeReaperTriggeredAbility(this);
-    }
-
-    @Override
-    public boolean checkEventType(GameEvent event, Game game) {
-        return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD;
-    }
-
-    @Override
-    public boolean checkTrigger(GameEvent event, Game game) {
-        if (event.getPlayerId().equals(this.getControllerId())) {
-            Permanent permanent = game.getPermanent(event.getTargetId());
-            if (permanent != null && (permanent.getId().equals(this.getSourceId()) || permanent.hasSubtype(SubType.WARRIOR, game))) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public String getRule() {
-        return "Whenever {this} or another Warrior enters the battlefield under your control, " + super.getRule();
-    }
-}
-
-class MarduWoeReaperEffect extends OneShotEffect {
-
-    MarduWoeReaperEffect() {
-        super(Outcome.GainLife);
-        this.staticText = "you may exile target creature card from a graveyard. If you do, you gain 1 life";
-    }
-
-    MarduWoeReaperEffect(final MarduWoeReaperEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public MarduWoeReaperEffect copy() {
-        return new MarduWoeReaperEffect(this);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        Player player = game.getPlayer(source.getControllerId());
-        Card card = game.getCard(this.getTargetPointer().getFirst(game, source));
-        if (player != null && card != null) {
-            if (player.moveCardToExileWithInfo(card, null, "", source.getSourceId(), game, Zone.GRAVEYARD, true)) {
-                player.gainLife(1, game, source);
-            }
-            return true;
-        }
-        return false;
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/m/MaritLagesSlumber.java b/Mage.Sets/src/mage/cards/m/MaritLagesSlumber.java
index 4588829ab9..5f2890ac7d 100644
--- a/Mage.Sets/src/mage/cards/m/MaritLagesSlumber.java
+++ b/Mage.Sets/src/mage/cards/m/MaritLagesSlumber.java
@@ -1,7 +1,7 @@
 package mage.cards.m;
 
 import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
-import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility;
+import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility;
 import mage.abilities.condition.Condition;
 import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
 import mage.abilities.costs.common.SacrificeSourceCost;
@@ -27,7 +27,7 @@ import java.util.UUID;
 public final class MaritLagesSlumber extends CardImpl {
 
     private static final FilterPermanent filter
-            = new FilterControlledPermanent("{this} or another snow permanent");
+            = new FilterControlledPermanent("snow permanent");
 
     static {
         filter.add(SuperType.SNOW.getPredicate());
@@ -43,7 +43,9 @@ public final class MaritLagesSlumber extends CardImpl {
         this.addSuperType(SuperType.SNOW);
 
         // Whenever Marit Lage's Slumber or another snow permanent enters the battlefield under your control, scry 1.
-        this.addAbility(new EntersBattlefieldControlledTriggeredAbility(new ScryEffect(1), filter));
+        this.addAbility(new EntersBattlefieldThisOrAnotherTriggeredAbility(
+                new ScryEffect(1), filter, false, true
+        ));
 
         // At the beginning of your upkeep, if you control ten or more snow permanents, sacrifice Marit Lage's Slumber. If you do, create Marit Lage, a legendary 20/20 black Avatar creature token with flying and indestructible.
         this.addAbility(new ConditionalInterveningIfTriggeredAbility(
diff --git a/Mage.Sets/src/mage/cards/n/NebelgastHerald.java b/Mage.Sets/src/mage/cards/n/NebelgastHerald.java
index 833bae9953..115a9c4d25 100644
--- a/Mage.Sets/src/mage/cards/n/NebelgastHerald.java
+++ b/Mage.Sets/src/mage/cards/n/NebelgastHerald.java
@@ -1,10 +1,9 @@
 
 package mage.cards.n;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility;
+import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility;
 import mage.abilities.effects.common.TapTargetEffect;
 import mage.abilities.keyword.FlashAbility;
 import mage.abilities.keyword.FlyingAbility;
@@ -12,39 +11,35 @@ import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.SubType;
-import mage.constants.TargetController;
-import mage.constants.Zone;
-import mage.filter.common.FilterCreaturePermanent;
-import mage.target.common.TargetCreaturePermanent;
+import mage.filter.FilterPermanent;
+import mage.target.common.TargetOpponentsCreaturePermanent;
+
+import java.util.UUID;
 
 /**
- *
  * @author LevelX2
  */
 public final class NebelgastHerald extends CardImpl {
 
-    private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("{this} or another Spirit");
-    private static final FilterCreaturePermanent filterTarget = new FilterCreaturePermanent("creature an opponent controls");
-
-    static {
-        filter.add(TargetController.YOU.getControllerPredicate());
-        filter.add(SubType.SPIRIT.getPredicate());
-        filterTarget.add(TargetController.OPPONENT.getControllerPredicate());
-    }
+    private static final FilterPermanent filter = new FilterPermanent(SubType.SPIRIT, "Spirit");
 
     public NebelgastHerald(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{U}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}");
         this.subtype.add(SubType.SPIRIT);
         this.power = new MageInt(2);
         this.toughness = new MageInt(1);
 
         // Flash
         this.addAbility(FlashAbility.getInstance());
+
         // Flying
         this.addAbility(FlyingAbility.getInstance());
+
         // Whenever Nebelgast Herald or another Spirit enters the battlefield under your control, tap target creature an opponent controls.
-        Ability ability = new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, new TapTargetEffect(), filter, false);
-        ability.addTarget(new TargetCreaturePermanent(filterTarget));
+        Ability ability = new EntersBattlefieldThisOrAnotherTriggeredAbility(
+                new TapTargetEffect(), filter, false, true
+        );
+        ability.addTarget(new TargetOpponentsCreaturePermanent());
         this.addAbility(ability);
     }
 
diff --git a/Mage.Sets/src/mage/cards/n/NoxiousGhoul.java b/Mage.Sets/src/mage/cards/n/NoxiousGhoul.java
index c8bf3c0ffe..1409bedb6f 100644
--- a/Mage.Sets/src/mage/cards/n/NoxiousGhoul.java
+++ b/Mage.Sets/src/mage/cards/n/NoxiousGhoul.java
@@ -1,8 +1,7 @@
-
 package mage.cards.n;
 
 import mage.MageInt;
-import mage.abilities.common.EntersBattlefieldAllTriggeredAbility;
+import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility;
 import mage.abilities.effects.common.continuous.BoostAllEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -11,11 +10,7 @@ import mage.constants.Duration;
 import mage.constants.SubType;
 import mage.filter.FilterPermanent;
 import mage.filter.common.FilterCreaturePermanent;
-import mage.filter.predicate.ObjectSourcePlayer;
-import mage.filter.predicate.ObjectSourcePlayerPredicate;
 import mage.filter.predicate.Predicates;
-import mage.game.Game;
-import mage.game.permanent.Permanent;
 
 import java.util.UUID;
 
@@ -24,13 +19,11 @@ import java.util.UUID;
  */
 public final class NoxiousGhoul extends CardImpl {
 
-    private static final FilterCreaturePermanent filter = new FilterCreaturePermanent();
-    private static final FilterPermanent filter2 = new FilterPermanent();
+    private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("all non-Zombie creatures");
+    private static final FilterPermanent filter2 = new FilterPermanent(SubType.ZOMBIE, "Zombie");
 
     static {
-        filter.add(CardType.CREATURE.getPredicate());
         filter.add(Predicates.not(SubType.ZOMBIE.getPredicate()));
-        filter2.add(NoxiousGhoulPredicate.instance);
     }
 
     public NoxiousGhoul(UUID ownerId, CardSetInfo setInfo) {
@@ -41,14 +34,12 @@ public final class NoxiousGhoul extends CardImpl {
         this.toughness = new MageInt(3);
 
         // Whenever Noxious Ghoul or another Zombie enters the battlefield, all non-Zombie creatures get -1/-1 until end of turn.
-        this.addAbility(new EntersBattlefieldAllTriggeredAbility(
-                new BoostAllEffect(-1, -1, Duration.EndOfTurn, filter, false),
-                filter2, "Whenever {this} or another Zombie enters the battlefield, " +
-                "all non-Zombie creatures get -1/-1 until end of turn."
-        ));
+        this.addAbility(new EntersBattlefieldThisOrAnotherTriggeredAbility(new BoostAllEffect(
+                -1, -1, Duration.EndOfTurn, filter, false
+        ), filter2, false, true));
     }
 
-    public NoxiousGhoul(final NoxiousGhoul card) {
+    private NoxiousGhoul(final NoxiousGhoul card) {
         super(card);
     }
 
@@ -57,13 +48,3 @@ public final class NoxiousGhoul extends CardImpl {
         return new NoxiousGhoul(this);
     }
 }
-
-enum NoxiousGhoulPredicate implements ObjectSourcePlayerPredicate<ObjectSourcePlayer<Permanent>> {
-    instance;
-
-    @Override
-    public boolean apply(ObjectSourcePlayer<Permanent> input, Game game) {
-        return input.getObject().hasSubtype(SubType.ZOMBIE, game)
-                || input.getObject().getId().equals(input.getSourceId());
-    }
-}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/o/OathOfTheAncientWood.java b/Mage.Sets/src/mage/cards/o/OathOfTheAncientWood.java
index a2f1acfa26..8723578699 100644
--- a/Mage.Sets/src/mage/cards/o/OathOfTheAncientWood.java
+++ b/Mage.Sets/src/mage/cards/o/OathOfTheAncientWood.java
@@ -1,44 +1,35 @@
-
 package mage.cards.o;
 
-import java.util.UUID;
 import mage.abilities.Ability;
-import mage.abilities.common.EntersBattlefieldAllTriggeredAbility;
-import mage.abilities.effects.Effect;
+import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility;
 import mage.abilities.effects.common.counter.AddCountersTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.SetTargetPointer;
-import mage.constants.TargetController;
-import mage.constants.Zone;
 import mage.counters.CounterType;
-import mage.filter.common.FilterEnchantmentPermanent;
+import mage.filter.StaticFilters;
 import mage.target.common.TargetCreaturePermanent;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class OathOfTheAncientWood extends CardImpl {
 
-    private static final FilterEnchantmentPermanent filter = new FilterEnchantmentPermanent("Oath of the Ancient Wood or another enchantment");
-
-    static {
-        filter.add(TargetController.YOU.getControllerPredicate());
-    }
-
     public OathOfTheAncientWood(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{G}");
+        super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}");
 
         // Whenever Oath of the Ancient Wood or another enchantment enters the battlefield under your control, you may put a +1/+1 counter on target creature.
-        Effect effect = new AddCountersTargetEffect(CounterType.P1P1.createInstance());
-        Ability ability = new EntersBattlefieldAllTriggeredAbility(Zone.BATTLEFIELD, effect, filter, true, SetTargetPointer.NONE, null, true);
+        Ability ability = new EntersBattlefieldThisOrAnotherTriggeredAbility(
+                new AddCountersTargetEffect(CounterType.P1P1.createInstance()),
+                StaticFilters.FILTER_ENCHANTMENT_PERMANENT, true, true
+        );
         ability.addTarget(new TargetCreaturePermanent());
         this.addAbility(ability);
     }
 
-    public OathOfTheAncientWood(final OathOfTheAncientWood card) {
+    private OathOfTheAncientWood(final OathOfTheAncientWood card) {
         super(card);
     }
 
diff --git a/Mage.Sets/src/mage/cards/q/QasaliSlingers.java b/Mage.Sets/src/mage/cards/q/QasaliSlingers.java
index a68dd90737..457e83c16f 100644
--- a/Mage.Sets/src/mage/cards/q/QasaliSlingers.java
+++ b/Mage.Sets/src/mage/cards/q/QasaliSlingers.java
@@ -1,28 +1,27 @@
-
 package mage.cards.q;
 
-import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.TriggeredAbilityImpl;
+import mage.abilities.Ability;
+import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility;
 import mage.abilities.effects.common.DestroyTargetEffect;
 import mage.abilities.keyword.ReachAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.SubType;
-import mage.constants.Zone;
+import mage.filter.FilterPermanent;
 import mage.filter.StaticFilters;
-import mage.game.Game;
-import mage.game.events.GameEvent;
-import mage.game.permanent.Permanent;
 import mage.target.TargetPermanent;
 
+import java.util.UUID;
+
 /**
- *
  * @author spjspj
  */
 public final class QasaliSlingers extends CardImpl {
 
+    private static final FilterPermanent filter = new FilterPermanent(SubType.CAT, "Cat");
+
     public QasaliSlingers(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}");
 
@@ -35,10 +34,14 @@ public final class QasaliSlingers extends CardImpl {
         this.addAbility(ReachAbility.getInstance());
 
         // Whenever Qasali Slingers or another Cat enters the battlefield under your control, you may destroy target artifact or enchantment.
-        this.addAbility(new QasaliSlingersTriggeredAbility());
+        Ability ability = new EntersBattlefieldThisOrAnotherTriggeredAbility(
+                new DestroyTargetEffect(), filter, true, true
+        );
+        ability.addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT));
+        this.addAbility(ability);
     }
 
-    public QasaliSlingers(final QasaliSlingers card) {
+    private QasaliSlingers(final QasaliSlingers card) {
         super(card);
     }
 
@@ -47,44 +50,3 @@ public final class QasaliSlingers extends CardImpl {
         return new QasaliSlingers(this);
     }
 }
-
-class QasaliSlingersTriggeredAbility extends TriggeredAbilityImpl {
-
-    public QasaliSlingersTriggeredAbility() {
-        super(Zone.BATTLEFIELD, new DestroyTargetEffect(), true);
-        this.addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT));
-    }
-
-    public QasaliSlingersTriggeredAbility(final QasaliSlingersTriggeredAbility ability) {
-        super(ability);
-    }
-
-    @Override
-    public QasaliSlingersTriggeredAbility copy() {
-        return new QasaliSlingersTriggeredAbility(this);
-    }
-
-    @Override
-    public boolean checkEventType(GameEvent event, Game game) {
-        return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD;
-    }
-
-    @Override
-    public boolean checkTrigger(GameEvent event, Game game) {
-        Permanent permanent = game.getPermanent(event.getTargetId());
-        if (permanent != null) {
-            if (permanent.getId().equals(this.getSourceId())) {
-                return true;
-            }
-            if (permanent.hasSubtype(SubType.CAT, game) && permanent.isControlledBy(this.getControllerId())) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public String getRule() {
-        return "Whenever {this} or another Cat enters the battlefield under your control, you may destroy target artifact or enchantment.";
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/r/RisenReef.java b/Mage.Sets/src/mage/cards/r/RisenReef.java
index 71c38de59a..8b58944b89 100644
--- a/Mage.Sets/src/mage/cards/r/RisenReef.java
+++ b/Mage.Sets/src/mage/cards/r/RisenReef.java
@@ -2,7 +2,7 @@ package mage.cards.r;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility;
+import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.cards.Card;
 import mage.cards.CardImpl;
@@ -22,8 +22,7 @@ import java.util.UUID;
  */
 public final class RisenReef extends CardImpl {
 
-    private static final FilterPermanent filter
-            = new FilterPermanent(SubType.ELEMENTAL, "{this} or another Elemental");
+    private static final FilterPermanent filter = new FilterPermanent(SubType.ELEMENTAL, "Elemental");
 
     public RisenReef(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}{U}");
@@ -33,7 +32,9 @@ public final class RisenReef extends CardImpl {
         this.toughness = new MageInt(1);
 
         // Whenever Risen Reef or another Elemental enters the battlefield under your control, look at the top card of your library. If it's a land card, you may put it onto the battlefield tapped. If you don't put the card onto the battlefield, put it into your hand.
-        this.addAbility(new EntersBattlefieldControlledTriggeredAbility(new RisenReefEffect(), filter));
+        this.addAbility(new EntersBattlefieldThisOrAnotherTriggeredAbility(
+                new RisenReefEffect(), filter, false, true
+        ));
     }
 
     private RisenReef(final RisenReef card) {
@@ -85,4 +86,4 @@ class RisenReefEffect extends OneShotEffect {
         }
         return true;
     }
-}
\ No newline at end of file
+}
diff --git a/Mage.Sets/src/mage/cards/s/SageOfTheFalls.java b/Mage.Sets/src/mage/cards/s/SageOfTheFalls.java
index 29ad92531e..b40b05b3e3 100644
--- a/Mage.Sets/src/mage/cards/s/SageOfTheFalls.java
+++ b/Mage.Sets/src/mage/cards/s/SageOfTheFalls.java
@@ -1,7 +1,7 @@
 package mage.cards.s;
 
 import mage.MageInt;
-import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility;
+import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility;
 import mage.abilities.effects.common.DrawDiscardControllerEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -18,8 +18,7 @@ import java.util.UUID;
  */
 public final class SageOfTheFalls extends CardImpl {
 
-    private static final FilterPermanent filter
-            = new FilterCreaturePermanent("{this} or another non-Human creature");
+    private static final FilterPermanent filter = new FilterCreaturePermanent("non-Human creature");
 
     static {
         filter.add(Predicates.not(SubType.HUMAN.getPredicate()));
@@ -34,9 +33,9 @@ public final class SageOfTheFalls extends CardImpl {
         this.toughness = new MageInt(5);
 
         // Whenever Sage of the Falls or another non-Human creature enters the battlefield under you control, you may draw a card. If you do, discard a card.
-        this.addAbility(new EntersBattlefieldControlledTriggeredAbility(
-                new DrawDiscardControllerEffect(1, 1, true), filter
-        ));
+        this.addAbility(new EntersBattlefieldThisOrAnotherTriggeredAbility(new DrawDiscardControllerEffect(
+                1, 1, true
+        ), filter, false, true));
     }
 
     private SageOfTheFalls(final SageOfTheFalls card) {
diff --git a/Mage.Sets/src/mage/cards/s/ScourgeOfValkas.java b/Mage.Sets/src/mage/cards/s/ScourgeOfValkas.java
index c2332f8c6a..e6e7f1c6c7 100644
--- a/Mage.Sets/src/mage/cards/s/ScourgeOfValkas.java
+++ b/Mage.Sets/src/mage/cards/s/ScourgeOfValkas.java
@@ -2,7 +2,7 @@ package mage.cards.s;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility;
+import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.OneShotEffect;
@@ -10,9 +10,12 @@ import mage.abilities.effects.common.continuous.BoostSourceEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.*;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.Outcome;
+import mage.constants.SubType;
 import mage.filter.FilterPermanent;
-import mage.filter.common.FilterCreaturePermanent;
+import mage.filter.common.FilterControlledPermanent;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
 import mage.players.Player;
@@ -25,13 +28,7 @@ import java.util.UUID;
  */
 public final class ScourgeOfValkas extends CardImpl {
 
-    private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("{this} or another Dragon");
-
-    static {
-        filter.add(SubType.DRAGON.getPredicate());
-    }
-
-    private static final String rule = "Whenever {this} or another Dragon enters the battlefield under your control, it deals X damage to any target, where X is the number of Dragons you control.";
+    private static final FilterPermanent filter = new FilterPermanent(SubType.DRAGON, "Dragon");
 
     public ScourgeOfValkas(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{R}{R}");
@@ -45,16 +42,17 @@ public final class ScourgeOfValkas extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
 
         // Whenever Scourge of Valkas or another Dragon enters the battlefield under your control, it deals X damage to any target, where X is the number of Dragons you control.
-        Ability ability = new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, new ScourgeOfValkasDamageEffect(), filter, false, rule);
+        Ability ability = new EntersBattlefieldThisOrAnotherTriggeredAbility(
+                new ScourgeOfValkasDamageEffect(), filter, false, true
+        );
         ability.addTarget(new TargetAnyTarget());
         this.addAbility(ability);
 
         // {R}: Scourge of Valkas gets +1/+0 until end of turn.
-        this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, 0, Duration.EndOfTurn), new ManaCostsImpl("{R}")));
-
+        this.addAbility(new SimpleActivatedAbility(new BoostSourceEffect(1, 0, Duration.EndOfTurn), new ManaCostsImpl("{R}")));
     }
 
-    public ScourgeOfValkas(final ScourgeOfValkas card) {
+    private ScourgeOfValkas(final ScourgeOfValkas card) {
         super(card);
     }
 
@@ -66,12 +64,14 @@ public final class ScourgeOfValkas extends CardImpl {
 
 class ScourgeOfValkasDamageEffect extends OneShotEffect {
 
-    public ScourgeOfValkasDamageEffect() {
+    private static final FilterPermanent filter = new FilterControlledPermanent(SubType.DRAGON, "");
+
+    ScourgeOfValkasDamageEffect() {
         super(Outcome.Damage);
         this.staticText = "it deals X damage to any target, where X is the number of Dragons you control";
     }
 
-    public ScourgeOfValkasDamageEffect(final ScourgeOfValkasDamageEffect effect) {
+    private ScourgeOfValkasDamageEffect(final ScourgeOfValkasDamageEffect effect) {
         super(effect);
     }
 
@@ -84,24 +84,23 @@ class ScourgeOfValkasDamageEffect extends OneShotEffect {
     public boolean apply(Game game, Ability source) {
         Player controller = game.getPlayer(source.getControllerId());
         Permanent enteringDragon = (Permanent) getValue("permanentEnteringBattlefield");
-        if (controller != null && enteringDragon != null) {
-            FilterPermanent filter = new FilterPermanent();
-            filter.add(SubType.DRAGON.getPredicate());
-            int dragons = game.getBattlefield().countAll(filter, source.getControllerId(), game);
-            if (dragons > 0) {
-                Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
-                if (permanent != null) {
-                    permanent.damage(dragons, enteringDragon.getId(), game, false, true);
-                } else {
-                    Player player = game.getPlayer(getTargetPointer().getFirst(game, source));
-                    if (player != null
-                            && player.isInGame()) {
-                        player.damage(dragons, enteringDragon.getId(), game);
-                    }
-                }
-            }
+        if (controller == null || enteringDragon == null) {
+            return false;
+        }
+        int dragons = game.getBattlefield().count(filter, source.getSourceId(), source.getControllerId(), game);
+        if (dragons < 1) {
             return true;
         }
-        return false;
+        Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
+        if (permanent != null) {
+            permanent.damage(dragons, enteringDragon.getId(), game, false, true);
+        } else {
+            Player player = game.getPlayer(getTargetPointer().getFirst(game, source));
+            if (player != null
+                    && player.isInGame()) {
+                player.damage(dragons, enteringDragon.getId(), game);
+            }
+        }
+        return true;
     }
 }
diff --git a/Mage.Sets/src/mage/cards/s/SerumTank.java b/Mage.Sets/src/mage/cards/s/SerumTank.java
index 59b4f5bbbe..52875eee5c 100644
--- a/Mage.Sets/src/mage/cards/s/SerumTank.java
+++ b/Mage.Sets/src/mage/cards/s/SerumTank.java
@@ -1,48 +1,44 @@
-
 package mage.cards.s;
 
-import java.util.UUID;
 import mage.abilities.Ability;
-import mage.abilities.TriggeredAbilityImpl;
+import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.common.RemoveCountersSourceCost;
 import mage.abilities.costs.common.TapSourceCost;
-import mage.abilities.costs.mana.ManaCostsImpl;
-import mage.abilities.effects.Effect;
+import mage.abilities.costs.mana.GenericManaCost;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.abilities.effects.common.counter.AddCountersSourceEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.Zone;
 import mage.counters.CounterType;
-import mage.game.Game;
-import mage.game.events.GameEvent;
-import mage.game.permanent.Permanent;
-import mage.target.targetpointer.FixedTarget;
+import mage.filter.StaticFilters;
+
+import java.util.UUID;
 
 /**
- *
  * @author CountAndromalius
  */
 public final class SerumTank extends CardImpl {
 
     public SerumTank(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}");
+        super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}");
 
         // Whenever {this} or another artifact comes into play, put a charge counter on {this}.
-        Effect effect = new AddCountersSourceEffect(CounterType.CHARGE.createInstance());
-        effect.setText("put a charge counter on {this}");
-        this.addAbility(new SerumTankTriggeredAbility(effect));
+        this.addAbility(new EntersBattlefieldThisOrAnotherTriggeredAbility(
+                new AddCountersSourceEffect(CounterType.CHARGE.createInstance()), StaticFilters.FILTER_PERMANENT_ARTIFACT
+        ));
 
         // {3}, {tap}, Remove a charge counter from {this}: Draw a card.
-        Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{3}"));
-        ability.addCost(new RemoveCountersSourceCost(CounterType.CHARGE.createInstance(1)));
+        Ability ability = new SimpleActivatedAbility(
+                new DrawCardSourceControllerEffect(1), new GenericManaCost(3)
+        );
         ability.addCost(new TapSourceCost());
+        ability.addCost(new RemoveCountersSourceCost(CounterType.CHARGE.createInstance()));
         this.addAbility(ability);
     }
 
-    public SerumTank(final SerumTank card) {
+    private SerumTank(final SerumTank card) {
         super(card);
     }
 
@@ -51,42 +47,3 @@ public final class SerumTank extends CardImpl {
         return new SerumTank(this);
     }
 }
-
-class SerumTankTriggeredAbility extends TriggeredAbilityImpl {
-
-    SerumTankTriggeredAbility(Effect effect) {
-        super(Zone.BATTLEFIELD, effect, false);
-    }
-
-    SerumTankTriggeredAbility(final SerumTankTriggeredAbility ability) {
-        super(ability);
-    }
-
-    @Override
-    public SerumTankTriggeredAbility copy() {
-        return new SerumTankTriggeredAbility(this);
-    }
-
-    @Override
-    public boolean checkEventType(GameEvent event, Game game) {
-        return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD;
-    }
-
-    @Override
-    public boolean checkTrigger(GameEvent event, Game game) {
-        UUID targetId = event.getTargetId();
-        Permanent permanent = game.getPermanent(targetId);
-        if (permanent.isArtifact()) {
-            for (Effect effect : this.getEffects()) {
-                effect.setTargetPointer(new FixedTarget(permanent, game));
-            }
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    public String getRule() {
-        return "Whenever {this} or another artifact enters the battlefield, put a charge counter on {this}.";
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/s/SethronHurloonGeneral.java b/Mage.Sets/src/mage/cards/s/SethronHurloonGeneral.java
index 11260153d0..596757a7dd 100644
--- a/Mage.Sets/src/mage/cards/s/SethronHurloonGeneral.java
+++ b/Mage.Sets/src/mage/cards/s/SethronHurloonGeneral.java
@@ -2,7 +2,7 @@ package mage.cards.s;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility;
+import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.common.CreateTokenEffect;
@@ -17,7 +17,6 @@ import mage.constants.Duration;
 import mage.constants.SubType;
 import mage.constants.SuperType;
 import mage.filter.FilterPermanent;
-import mage.filter.common.FilterControlledPermanent;
 import mage.filter.common.FilterCreaturePermanent;
 import mage.filter.predicate.Predicates;
 import mage.filter.predicate.permanent.TokenPredicate;
@@ -30,9 +29,8 @@ import java.util.UUID;
  */
 public final class SethronHurloonGeneral extends CardImpl {
 
-    private static final FilterPermanent filter = new FilterControlledPermanent(
-            SubType.MINOTAUR, "{this} or another nontoken Minotaur"
-    );
+    private static final FilterPermanent filter
+            = new FilterPermanent(SubType.MINOTAUR, "nontoken Minotaur");
     private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent(SubType.MINOTAUR, "");
     private static final FilterPermanent filter3 = new FilterPermanent(SubType.MINOTAUR, "");
 
@@ -50,7 +48,9 @@ public final class SethronHurloonGeneral extends CardImpl {
         this.toughness = new MageInt(4);
 
         // Whenever Sethron, Hurloon General or another nontoken Minotaur enters the battlefield under your control, create a 2/3 red Minotaur creature token.
-        this.addAbility(new EntersBattlefieldControlledTriggeredAbility(new CreateTokenEffect(new MinotaurToken()), filter));
+        this.addAbility(new EntersBattlefieldThisOrAnotherTriggeredAbility(
+                new CreateTokenEffect(new MinotaurToken()), filter, false, true
+        ));
 
         // {2}{B/R}: Minotaurs you control get +1/+0 and gain menace and haste until end of turn.
         Ability ability = new SimpleActivatedAbility(new BoostControlledEffect(
diff --git a/Mage.Sets/src/mage/cards/t/ThievesGuildEnforcer.java b/Mage.Sets/src/mage/cards/t/ThievesGuildEnforcer.java
index c62d5c63f3..40d870a3ef 100644
--- a/Mage.Sets/src/mage/cards/t/ThievesGuildEnforcer.java
+++ b/Mage.Sets/src/mage/cards/t/ThievesGuildEnforcer.java
@@ -1,11 +1,8 @@
 package mage.cards.t;
 
-import java.util.Objects;
-import java.util.UUID;
-
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility;
+import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.condition.Condition;
 import mage.abilities.decorator.ConditionalContinuousEffect;
@@ -25,6 +22,9 @@ import mage.game.Game;
 import mage.game.Graveyard;
 import mage.players.Player;
 
+import java.util.Objects;
+import java.util.UUID;
+
 /**
  * @author TheElk801
  */
@@ -44,10 +44,10 @@ public final class ThievesGuildEnforcer extends CardImpl {
         this.addAbility(FlashAbility.getInstance());
 
         // Whenever Thieves' Guild Enforcer or another Rogue enters the battlefield under your control, each opponent mills two cards.
-        this.addAbility(new EntersBattlefieldControlledTriggeredAbility(
+        this.addAbility(new EntersBattlefieldThisOrAnotherTriggeredAbility(
                 new PutTopCardOfLibraryIntoGraveEachPlayerEffect(
                         2, TargetController.OPPONENT
-                ), filter
+                ), filter, false, true
         ));
 
         // As long as an opponent has eight or more cards in their graveyard, Thieves' Guild Enforcer gets +2/+1 and has deathtouch.
diff --git a/Mage.Sets/src/mage/cards/t/ThornMammoth.java b/Mage.Sets/src/mage/cards/t/ThornMammoth.java
index 6d5a1e9627..9435c6c1ac 100644
--- a/Mage.Sets/src/mage/cards/t/ThornMammoth.java
+++ b/Mage.Sets/src/mage/cards/t/ThornMammoth.java
@@ -2,7 +2,7 @@ package mage.cards.t;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility;
+import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility;
 import mage.abilities.effects.common.FightTargetSourceEffect;
 import mage.abilities.keyword.TrampleAbility;
 import mage.cards.CardImpl;
@@ -30,10 +30,9 @@ public final class ThornMammoth extends CardImpl {
         this.addAbility(TrampleAbility.getInstance());
 
         // Whenever Thorn Mammoth or another creature enters the battlefield under your control, Thorn Mammoth fights up to one target creature you don't control.
-        Ability ability = new EntersBattlefieldControlledTriggeredAbility(
-                new FightTargetSourceEffect(), StaticFilters.FILTER_PERMANENT_CREATURE,
-                "Whenever {this} or another creature enters the battlefield under your control, " +
-                        "{this} fights up to one target creature you don't control."
+        Ability ability = new EntersBattlefieldThisOrAnotherTriggeredAbility(
+                new FightTargetSourceEffect().setText("{this} fights up to one target creature you don't control"),
+                StaticFilters.FILTER_PERMANENT_CREATURE, false, true
         );
         ability.addTarget(new TargetPermanent(0, 1, StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL, false));
         this.addAbility(ability);
diff --git a/Mage.Sets/src/mage/cards/t/ThrasherBrute.java b/Mage.Sets/src/mage/cards/t/ThrasherBrute.java
index e841325ff7..640395a0e4 100644
--- a/Mage.Sets/src/mage/cards/t/ThrasherBrute.java
+++ b/Mage.Sets/src/mage/cards/t/ThrasherBrute.java
@@ -1,24 +1,24 @@
 
 package mage.cards.t;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldAllTriggeredAbility;
 import mage.abilities.effects.common.GainLifeEffect;
 import mage.abilities.effects.common.LoseLifeTargetEffect;
-import mage.constants.SubType;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
+import mage.constants.SubType;
 import mage.constants.Zone;
 import mage.filter.common.FilterTeamPermanent;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
 import mage.target.common.TargetOpponent;
 
+import java.util.UUID;
+
 /**
- *
  * @author TheElk801
  */
 public final class ThrasherBrute extends CardImpl {
@@ -35,12 +35,9 @@ public final class ThrasherBrute extends CardImpl {
 
         // Whenever Thrasher Brute or another Warrior enters the battlefield under your team's control, target opponent loses 1 life and you gain 1 life.
         Ability ability = new EntersBattlefieldAllTriggeredAbility(
-                Zone.BATTLEFIELD,
-                new LoseLifeTargetEffect(1),
-                filter,
-                false,
+                Zone.BATTLEFIELD, new LoseLifeTargetEffect(1), filter, false,
                 "Whenever {this} or another Warrior enters the battlefield under your team's control, "
-                + "target opponent loses 1 life and you gain 1 life."
+                        + "target opponent loses 1 life and you gain 1 life."
         );
         ability.addEffect(new GainLifeEffect(1));
         ability.addTarget(new TargetOpponent());
@@ -63,8 +60,8 @@ class ThrasherBruteFilter extends FilterTeamPermanent {
         super();
     }
 
-    ThrasherBruteFilter(final ThrasherBruteFilter effect) {
-        super(effect);
+    private ThrasherBruteFilter(final ThrasherBruteFilter filter) {
+        super(filter);
     }
 
     @Override
@@ -74,16 +71,8 @@ class ThrasherBruteFilter extends FilterTeamPermanent {
 
     @Override
     public boolean match(Permanent permanent, UUID sourceId, UUID playerId, Game game) {
-        if (super.match(permanent, sourceId, playerId, game)) {
-            if (sourceId.equals(permanent.getId())) {
-                return true;
-            } else {
-                if (permanent.hasSubtype(SubType.WARRIOR, game)) {
-                    return true;
-                }
-            }
-        }
-        return false;
+        return super.match(permanent, sourceId, playerId, game)
+                && (sourceId.equals(permanent.getId())
+                || permanent.hasSubtype(SubType.WARRIOR, game));
     }
-
 }
diff --git a/Mage.Sets/src/mage/cards/u/UneshCriosphinxSovereign.java b/Mage.Sets/src/mage/cards/u/UneshCriosphinxSovereign.java
index a3539f9a0a..61a7d71316 100644
--- a/Mage.Sets/src/mage/cards/u/UneshCriosphinxSovereign.java
+++ b/Mage.Sets/src/mage/cards/u/UneshCriosphinxSovereign.java
@@ -1,44 +1,33 @@
-
 package mage.cards.u;
 
+import mage.MageInt;
+import mage.MageObject;
+import mage.abilities.Ability;
+import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect;
+import mage.abilities.keyword.FlyingAbility;
+import mage.cards.*;
+import mage.constants.*;
+import mage.filter.FilterCard;
+import mage.filter.FilterPermanent;
+import mage.game.Game;
+import mage.players.Player;
+import mage.target.TargetCard;
+
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
 import java.util.UUID;
-import mage.MageInt;
-import mage.MageObject;
-import mage.abilities.Ability;
-import mage.abilities.TriggeredAbilityImpl;
-import mage.abilities.common.SimpleStaticAbility;
-import mage.abilities.effects.OneShotEffect;
-import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect;
-import mage.abilities.keyword.FlyingAbility;
-import mage.cards.Card;
-import mage.cards.CardImpl;
-import mage.cards.CardSetInfo;
-import mage.cards.Cards;
-import mage.cards.CardsImpl;
-import mage.constants.CardType;
-import mage.constants.Outcome;
-import mage.constants.SubType;
-import mage.constants.SuperType;
-import mage.constants.Zone;
-import mage.filter.FilterCard;
-import mage.filter.common.FilterCreaturePermanent;
-import mage.game.Game;
-import mage.game.events.GameEvent;
-import mage.game.events.GameEvent.EventType;
-import mage.game.permanent.Permanent;
-import mage.players.Player;
-import mage.target.TargetCard;
 
 /**
- *
  * @author spjspj
  */
 public final class UneshCriosphinxSovereign extends CardImpl {
 
     private static final FilterCard filter = new FilterCard("Sphinx spells");
+    private static final FilterPermanent filter2 = new FilterPermanent(SubType.SPHINX, "Sphinx");
 
     static {
         filter.add(SubType.SPHINX.getPredicate());
@@ -56,13 +45,15 @@ public final class UneshCriosphinxSovereign extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
 
         // Sphinx spells you cast cost {2} less to cast.
-        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filter, 2)));
+        this.addAbility(new SimpleStaticAbility(new SpellsCostReductionControllerEffect(filter, 2)));
 
         // Whenever Unesh, Criosphinx Sovereign or another Sphinx enters the battlefield under your control, reveal the top four cards of your library. An opponent seperates those cards into two piles. Put one pile into your hand and the other into your graveyard.
-        this.addAbility(new UneshCriosphinxSovereignTriggeredAbility());
+        this.addAbility(new EntersBattlefieldThisOrAnotherTriggeredAbility(
+                new UneshCriosphinxSovereignEffect(), filter2, false, true
+        ));
     }
 
-    public UneshCriosphinxSovereign(final UneshCriosphinxSovereign card) {
+    private UneshCriosphinxSovereign(final UneshCriosphinxSovereign card) {
         super(card);
     }
 
@@ -72,58 +63,14 @@ public final class UneshCriosphinxSovereign extends CardImpl {
     }
 }
 
-class UneshCriosphinxSovereignTriggeredAbility extends TriggeredAbilityImpl {
-
-    private static final FilterCreaturePermanent filter = new FilterCreaturePermanent();
-
-    static {
-        filter.add(SubType.SPHINX.getPredicate());
-    }
-
-    public UneshCriosphinxSovereignTriggeredAbility() {
-        super(Zone.BATTLEFIELD, new UneshCriosphinxSovereignEffect(), false);
-    }
-
-    public UneshCriosphinxSovereignTriggeredAbility(UneshCriosphinxSovereignTriggeredAbility ability) {
-        super(ability);
-    }
-
-    @Override
-    public boolean checkEventType(GameEvent event, Game game) {
-        return event.getType() == EventType.ENTERS_THE_BATTLEFIELD;
-    }
-
-    @Override
-    public boolean checkTrigger(GameEvent event, Game game) {
-        Permanent permanent = game.getPermanent(event.getTargetId());
-        if (permanent != null
-                && permanent.isOwnedBy(controllerId)
-                && permanent.isCreature()
-                && (event.getTargetId().equals(getSourceId()) || filter.match(permanent, game))) {
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    public String getRule() {
-        return "Whenever {this} or another Sphinx enters the battlefield under your control, " + super.getRule();
-    }
-
-    @Override
-    public UneshCriosphinxSovereignTriggeredAbility copy() {
-        return new UneshCriosphinxSovereignTriggeredAbility(this);
-    }
-}
-
 class UneshCriosphinxSovereignEffect extends OneShotEffect {
 
-    public UneshCriosphinxSovereignEffect() {
+    UneshCriosphinxSovereignEffect() {
         super(Outcome.DrawCard);
         this.staticText = "reveal the top four cards of your library. An opponent separates those cards into two piles. Put one pile into your hand and the other into your graveyard";
     }
 
-    public UneshCriosphinxSovereignEffect(final UneshCriosphinxSovereignEffect effect) {
+    private UneshCriosphinxSovereignEffect(final UneshCriosphinxSovereignEffect effect) {
         super(effect);
     }
 
diff --git a/Mage/src/main/java/mage/abilities/abilityword/ConstellationAbility.java b/Mage/src/main/java/mage/abilities/abilityword/ConstellationAbility.java
index 5af021988b..1c5a4b23f5 100644
--- a/Mage/src/main/java/mage/abilities/abilityword/ConstellationAbility.java
+++ b/Mage/src/main/java/mage/abilities/abilityword/ConstellationAbility.java
@@ -53,7 +53,7 @@ public class ConstellationAbility extends TriggeredAbilityImpl {
             return false;
         }
         Permanent permanent = game.getPermanent(event.getTargetId());
-        return permanent != null && permanent.isEnchantment();
+        return permanent != null && ((thisOr && permanent.getId().equals(getSourceId())) || permanent.isEnchantment());
     }
 
     @Override
diff --git a/Mage/src/main/java/mage/abilities/common/EntersBattlefieldAllTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/EntersBattlefieldAllTriggeredAbility.java
index 3a7125f87a..41fb6e3d6d 100644
--- a/Mage/src/main/java/mage/abilities/common/EntersBattlefieldAllTriggeredAbility.java
+++ b/Mage/src/main/java/mage/abilities/common/EntersBattlefieldAllTriggeredAbility.java
@@ -1,7 +1,6 @@
 
 package mage.abilities.common;
 
-import java.util.UUID;
 import mage.abilities.TriggeredAbilityImpl;
 import mage.abilities.effects.Effect;
 import mage.constants.SetTargetPointer;
@@ -12,8 +11,9 @@ import mage.game.events.GameEvent;
 import mage.game.permanent.Permanent;
 import mage.target.targetpointer.FixedTarget;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public class EntersBattlefieldAllTriggeredAbility extends TriggeredAbilityImpl {
@@ -22,6 +22,7 @@ public class EntersBattlefieldAllTriggeredAbility extends TriggeredAbilityImpl {
     protected String rule;
     protected boolean controlledText;
     protected SetTargetPointer setTargetPointer;
+    protected final boolean thisOrAnother;
 
     /**
      * zone = BATTLEFIELD optional = false
@@ -54,11 +55,16 @@ public class EntersBattlefieldAllTriggeredAbility extends TriggeredAbilityImpl {
     }
 
     public EntersBattlefieldAllTriggeredAbility(Zone zone, Effect effect, FilterPermanent filter, boolean optional, SetTargetPointer setTargetPointer, String rule, boolean controlledText) {
+        this(zone, effect, filter, optional, setTargetPointer, rule, controlledText, false);
+    }
+
+    protected EntersBattlefieldAllTriggeredAbility(Zone zone, Effect effect, FilterPermanent filter, boolean optional, SetTargetPointer setTargetPointer, String rule, boolean controlledText, boolean thisOrAnother) {
         super(zone, effect, optional);
         this.filter = filter;
         this.rule = rule;
         this.controlledText = controlledText;
         this.setTargetPointer = setTargetPointer;
+        this.thisOrAnother = thisOrAnother;
     }
 
     public EntersBattlefieldAllTriggeredAbility(final EntersBattlefieldAllTriggeredAbility ability) {
@@ -67,6 +73,7 @@ public class EntersBattlefieldAllTriggeredAbility extends TriggeredAbilityImpl {
         this.rule = ability.rule;
         this.controlledText = ability.controlledText;
         this.setTargetPointer = ability.setTargetPointer;
+        this.thisOrAnother = ability.thisOrAnother;
     }
 
     @Override
@@ -105,7 +112,11 @@ public class EntersBattlefieldAllTriggeredAbility extends TriggeredAbilityImpl {
         if (rule != null && !rule.isEmpty()) {
             return rule;
         }
-        StringBuilder sb = new StringBuilder("Whenever ").append(filter.getMessage());
+        StringBuilder sb = new StringBuilder("Whenever ");
+        if (thisOrAnother) {
+            sb.append("{this} or another ");
+        }
+        sb.append(filter.getMessage());
         sb.append(" enters the battlefield");
         if (controlledText) {
             sb.append(" under your control, ");
diff --git a/Mage/src/main/java/mage/abilities/common/EntersBattlefieldThisOrAnotherTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/EntersBattlefieldThisOrAnotherTriggeredAbility.java
new file mode 100644
index 0000000000..ce631a0a3b
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/common/EntersBattlefieldThisOrAnotherTriggeredAbility.java
@@ -0,0 +1,62 @@
+package mage.abilities.common;
+
+import mage.abilities.effects.Effect;
+import mage.constants.SetTargetPointer;
+import mage.constants.Zone;
+import mage.filter.FilterPermanent;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+import mage.game.permanent.Permanent;
+
+/**
+ * @author TheElk801
+ */
+public class EntersBattlefieldThisOrAnotherTriggeredAbility extends EntersBattlefieldAllTriggeredAbility {
+
+    private final boolean onlyControlled;
+
+    public EntersBattlefieldThisOrAnotherTriggeredAbility(Effect effect, FilterPermanent filter) {
+        this(effect, filter, false, false);
+    }
+
+    public EntersBattlefieldThisOrAnotherTriggeredAbility(Effect effect, FilterPermanent filter, boolean optional, boolean onlyControlled) {
+        this(effect, filter, optional, SetTargetPointer.NONE, onlyControlled);
+    }
+
+    public EntersBattlefieldThisOrAnotherTriggeredAbility(Effect effect, FilterPermanent filter, boolean optional, SetTargetPointer setTargetPointer, boolean onlyControlled) {
+        this(Zone.BATTLEFIELD, effect, filter, optional, setTargetPointer, onlyControlled);
+    }
+
+    public EntersBattlefieldThisOrAnotherTriggeredAbility(Zone zone, Effect effect, FilterPermanent filter, boolean optional, SetTargetPointer setTargetPointer, boolean onlyControlled) {
+        super(zone, effect, filter, optional, setTargetPointer, null, onlyControlled, true);
+        this.onlyControlled = onlyControlled;
+    }
+
+    private EntersBattlefieldThisOrAnotherTriggeredAbility(final EntersBattlefieldThisOrAnotherTriggeredAbility ability) {
+        super(ability);
+        this.onlyControlled = ability.onlyControlled;
+    }
+
+    @Override
+    public boolean checkTrigger(GameEvent event, Game game) {
+        if (!super.checkTrigger(event, game)) {
+            return false;
+        }
+        Permanent permanent = game.getPermanent(event.getTargetId());
+        if (permanent == null) {
+            return false;
+        }
+        if (permanent.getId().equals(getSourceId())) {
+            return true;
+        }
+        if (onlyControlled && !permanent.isControlledBy(this.getControllerId())) {
+            return false;
+        }
+        return filter.match(permanent, getSourceId(), getControllerId(), game);
+    }
+
+    @Override
+    public EntersBattlefieldThisOrAnotherTriggeredAbility copy() {
+        return new EntersBattlefieldThisOrAnotherTriggeredAbility(this);
+    }
+}
diff --git a/Mage/src/main/java/mage/filter/StaticFilters.java b/Mage/src/main/java/mage/filter/StaticFilters.java
index 2a0dc193e1..1727c44168 100644
--- a/Mage/src/main/java/mage/filter/StaticFilters.java
+++ b/Mage/src/main/java/mage/filter/StaticFilters.java
@@ -615,13 +615,7 @@ public final class StaticFilters {
         FILTER_PERMANENT_AURA.setLockedFilter(true);
     }
 
-    public static final FilterPermanent FILTER_PERMANENT_EQUIPMENT = new FilterPermanent();
-
-    static {
-        FILTER_PERMANENT_EQUIPMENT.add(CardType.ARTIFACT.getPredicate());
-        FILTER_PERMANENT_EQUIPMENT.add(SubType.EQUIPMENT.getPredicate());
-        FILTER_PERMANENT_EQUIPMENT.setLockedFilter(true);
-    }
+    public static final FilterPermanent FILTER_PERMANENT_EQUIPMENT = new FilterEquipmentPermanent();
 
     public static final FilterPermanent FILTER_PERMANENT_FORTIFICATION = new FilterPermanent();
 
diff --git a/Mage/src/main/java/mage/filter/common/FilterEquipmentPermanent.java b/Mage/src/main/java/mage/filter/common/FilterEquipmentPermanent.java
index 34d729d289..40748c5e52 100644
--- a/Mage/src/main/java/mage/filter/common/FilterEquipmentPermanent.java
+++ b/Mage/src/main/java/mage/filter/common/FilterEquipmentPermanent.java
@@ -12,7 +12,7 @@ import mage.filter.FilterPermanent;
 public class FilterEquipmentPermanent extends FilterPermanent {
 
     public FilterEquipmentPermanent() {
-        this("equipment");
+        this("Equipment");
     }
 
     public FilterEquipmentPermanent(String name) {

From f4896710d8e3bccde40a773bea6dc365f76f51be Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 25 Jun 2020 20:21:35 -0400
Subject: [PATCH 512/586] fixed Undead Alchemist exiling cards from zones other
 than graveyards

---
 Mage.Sets/src/mage/cards/u/UndeadAlchemist.java | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/Mage.Sets/src/mage/cards/u/UndeadAlchemist.java b/Mage.Sets/src/mage/cards/u/UndeadAlchemist.java
index a61b5fd197..37341042a4 100644
--- a/Mage.Sets/src/mage/cards/u/UndeadAlchemist.java
+++ b/Mage.Sets/src/mage/cards/u/UndeadAlchemist.java
@@ -79,7 +79,9 @@ class UndeadAlchemistTriggeredAbility extends TriggeredAbilityImpl {
         if (zEvent.getFromZone() == Zone.LIBRARY && zEvent.getToZone() == Zone.GRAVEYARD && game.getOpponents(this.getControllerId()).contains(zEvent.getPlayerId())) {
             Card card = game.getCard(event.getTargetId());
             if (card != null && card.isCreature()) {
-                this.getEffects().get(0).setTargetPointer(new FixedTarget(card.getId()));
+                if (game.getState().getZone(card.getId()) == Zone.GRAVEYARD) {
+                    this.getEffects().get(0).setTargetPointer(new FixedTarget(card, game));
+                }
                 return true;
             }
         }

From cb918253c8faaa7ea51a25b9a1d17fe0b0821e5a Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Thu, 25 Jun 2020 21:11:56 -0400
Subject: [PATCH 513/586] Implemented Inniaz, the Gale Force

---
 .../src/mage/cards/i/InniazTheGaleForce.java  | 163 ++++++++++++++++++
 Mage.Sets/src/mage/sets/Jumpstart.java        |   1 +
 2 files changed, 164 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/i/InniazTheGaleForce.java

diff --git a/Mage.Sets/src/mage/cards/i/InniazTheGaleForce.java b/Mage.Sets/src/mage/cards/i/InniazTheGaleForce.java
new file mode 100644
index 0000000000..2b73d8c560
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/i/InniazTheGaleForce.java
@@ -0,0 +1,163 @@
+package mage.cards.i;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.AttacksWithCreaturesTriggeredAbility;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.continuous.BoostAllEffect;
+import mage.abilities.effects.common.continuous.GainControlTargetEffect;
+import mage.abilities.keyword.FlyingAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.*;
+import mage.filter.FilterPermanent;
+import mage.filter.common.FilterCreaturePermanent;
+import mage.filter.common.FilterNonlandPermanent;
+import mage.filter.predicate.mageobject.AbilityPredicate;
+import mage.filter.predicate.permanent.AttackingPredicate;
+import mage.filter.predicate.permanent.ControllerIdPredicate;
+import mage.game.Game;
+import mage.players.Player;
+import mage.players.PlayerList;
+import mage.target.TargetPermanent;
+import mage.target.targetpointer.FixedTarget;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class InniazTheGaleForce extends CardImpl {
+
+    private static final FilterCreaturePermanent filter
+            = new FilterCreaturePermanent("attacking creatures with flying");
+    private static final FilterCreaturePermanent filter2
+            = new FilterCreaturePermanent("creatures you control with flying");
+
+    static {
+        filter.add(AttackingPredicate.instance);
+        filter.add(new AbilityPredicate(FlyingAbility.class));
+        filter2.add(new AbilityPredicate(FlyingAbility.class));
+    }
+
+    public InniazTheGaleForce(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{U}");
+
+        this.addSuperType(SuperType.LEGENDARY);
+        this.subtype.add(SubType.DJINN);
+        this.power = new MageInt(4);
+        this.toughness = new MageInt(4);
+
+        // Flying
+        this.addAbility(FlyingAbility.getInstance());
+
+        // {2}{W/U}: Attacking creatures with flying get +1/+1 until end of turn.
+        this.addAbility(new SimpleActivatedAbility(new BoostAllEffect(
+                1, 1, Duration.EndOfTurn, filter, false
+        ), new ManaCostsImpl("{2}{W/U}")));
+
+        // Whenever three or more creatures you control with flying attack, each player gains control of a nonland permanent of your choice controlled by the player to their right.
+        this.addAbility(new AttacksWithCreaturesTriggeredAbility(
+                new InniazTheGaleForceEffect(), 3, filter2
+        ));
+    }
+
+    private InniazTheGaleForce(final InniazTheGaleForce card) {
+        super(card);
+    }
+
+    @Override
+    public InniazTheGaleForce copy() {
+        return new InniazTheGaleForce(this);
+    }
+}
+
+class InniazTheGaleForceEffect extends OneShotEffect {
+
+    private static final class PlayerPair {
+        private final Player leftPlayer;
+        private final Player rightPlayer;
+        private final FilterPermanent filter;
+        private final TargetPermanent target;
+
+        private PlayerPair(Player leftPlayer, Player rightPlayer) {
+            this.leftPlayer = leftPlayer;
+            this.rightPlayer = rightPlayer;
+            this.filter = this.makeFilter();
+            this.target = this.makeTarget();
+        }
+
+        private FilterPermanent makeFilter() {
+            FilterPermanent filter = new FilterNonlandPermanent(
+                    "nonland permanent controlled by " + rightPlayer.getName()
+                            + " to give to " + leftPlayer.getName()
+            );
+            filter.add(new ControllerIdPredicate(rightPlayer.getId()));
+            return filter;
+        }
+
+        private TargetPermanent makeTarget() {
+            return new TargetPermanent(1, this.filter);
+        }
+
+        private void chooseTargets(Player controller, Game game, Ability source) {
+            if (game.getBattlefield().count(this.filter, source.getSourceId(), source.getControllerId(), game) > 0) {
+                controller.choose(Outcome.Neutral, this.target, source.getSourceId(), game);
+            }
+        }
+
+        private void createEffect(Game game, Ability source) {
+            if (this.target.getFirstTarget() == null) {
+                return;
+            }
+            game.addEffect(new GainControlTargetEffect(
+                    Duration.Custom, true, leftPlayer.getId()
+            ).setTargetPointer(new FixedTarget(target.getFirstTarget(), game)), source);
+        }
+    }
+
+    InniazTheGaleForceEffect() {
+        super(Outcome.Benefit);
+        staticText = "each player gains control of a nonland permanent of your choice controlled by the player to their right.";
+    }
+
+    private InniazTheGaleForceEffect(final InniazTheGaleForceEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public InniazTheGaleForceEffect copy() {
+        return new InniazTheGaleForceEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Player controller = game.getPlayer(source.getControllerId());
+        if (controller == null) {
+            return false;
+        }
+        PlayerList playerList = game.getState().getPlayersInRange(source.getControllerId(), game);
+        List<PlayerPair> playerPairList = new ArrayList<>();
+        for (int i = 0; i < playerList.size() - 1; i++) {
+            playerPairList.add(new PlayerPair(
+                    game.getPlayer(playerList.get(i)),
+                    game.getPlayer(playerList.get(i + 1))
+            ));
+        }
+        playerPairList.add(new PlayerPair(
+                game.getPlayer(playerList.get(playerPairList.size() - 1)),
+                game.getPlayer(playerList.get(0))
+        ));
+        for (PlayerPair playerPair : playerPairList) {
+            playerPair.chooseTargets(controller, game, source);
+        }
+        for (PlayerPair playerPair : playerPairList) {
+            playerPair.createEffect(game, source);
+        }
+        return true;
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index 7337c1f08e..1017a848b8 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -221,6 +221,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Indomitable Will", 109, Rarity.COMMON, mage.cards.i.IndomitableWill.class));
         cards.add(new SetCardInfo("Inferno Hellion", 337, Rarity.UNCOMMON, mage.cards.i.InfernoHellion.class));
         cards.add(new SetCardInfo("Initiate's Companion", 403, Rarity.COMMON, mage.cards.i.InitiatesCompanion.class));
+        cards.add(new SetCardInfo("Inniaz, the Gale Force", 12, Rarity.RARE, mage.cards.i.InniazTheGaleForce.class));
         cards.add(new SetCardInfo("Innocent Blood", 244, Rarity.COMMON, mage.cards.i.InnocentBlood.class));
         cards.add(new SetCardInfo("Inspired Charge", 110, Rarity.COMMON, mage.cards.i.InspiredCharge.class));
         cards.add(new SetCardInfo("Inspiring Call", 404, Rarity.UNCOMMON, mage.cards.i.InspiringCall.class));

From 0050c8966e0662f799e11b60b1677ecf4ff63ca3 Mon Sep 17 00:00:00 2001
From: johnmeat <64497504+johnmeat@users.noreply.github.com>
Date: Fri, 26 Jun 2020 03:31:05 +0100
Subject: [PATCH 514/586] Ability fix for when Nacatl has left battlefield
 (#6681)

---
 Mage.Sets/src/mage/cards/n/NacatlWarPride.java | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/n/NacatlWarPride.java b/Mage.Sets/src/mage/cards/n/NacatlWarPride.java
index cf29d0ba96..3cb3c031b4 100644
--- a/Mage.Sets/src/mage/cards/n/NacatlWarPride.java
+++ b/Mage.Sets/src/mage/cards/n/NacatlWarPride.java
@@ -27,6 +27,7 @@ import mage.target.targetpointer.FixedTargets;
 import mage.abilities.effects.common.CreateTokenCopyTargetEffect;
 import mage.players.Player;
 import mage.target.targetpointer.FixedTarget;
+import mage.watchers.common.CreatureAttackedWhichPlayerWatcher;
 
 /**
  *
@@ -46,7 +47,10 @@ public final class NacatlWarPride extends CardImpl {
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new MustBeBlockedByAtLeastOneSourceEffect(Duration.WhileOnBattlefield)));
 
         // Whenever Nacatl War-Pride attacks, create X tokens that are copies of Nacatl War-Pride tapped and attacking, where X is the number of creatures defending player controls. Exile the tokens at the beginning of the next end step.
-        this.addAbility(new AttacksTriggeredAbility(new NacatlWarPrideEffect(), false));
+        Ability ability = new AttacksTriggeredAbility(new NacatlWarPrideEffect(), false);
+        ability.addWatcher(new CreatureAttackedWhichPlayerWatcher());
+        this.addAbility(ability);       
+        
     }
 
     public NacatlWarPride(final NacatlWarPride card) {
@@ -77,13 +81,16 @@ class NacatlWarPrideEffect extends OneShotEffect {
 
     @Override
     public boolean apply(Game game, Ability source) {
-        Permanent origNactalWarPride = game.getPermanent(source.getSourceId());
+        Permanent origNactalWarPride = game.getPermanentOrLKIBattlefield(source.getSourceId());
         if (origNactalWarPride == null) {
             return false;
         }
+        
+        CreatureAttackedWhichPlayerWatcher PlayerAttackedWatcher = game.getState().getWatcher(CreatureAttackedWhichPlayerWatcher.class);
 
         // Count the number of creatures attacked opponent controls
-        UUID defenderId = game.getCombat().getDefendingPlayerId(origNactalWarPride.getId(), game);
+        UUID defenderId = PlayerAttackedWatcher.getPlayerAttackedThisTurnByCreature(source.getSourceId());        
+
         int count = 0;
         if (defenderId != null) {
             count = game.getBattlefield().countAll(new FilterControlledCreaturePermanent(), defenderId, game);

From 7632cb417cdc3db61bd01666bf6e868f0a037340 Mon Sep 17 00:00:00 2001
From: arcox <10953229+arcox@users.noreply.github.com>
Date: Thu, 25 Jun 2020 22:37:23 -0400
Subject: [PATCH 515/586] Fix Track Down oracle text (#6643)

---
 Mage.Sets/src/mage/cards/t/TrackDown.java | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/t/TrackDown.java b/Mage.Sets/src/mage/cards/t/TrackDown.java
index 468de32576..e7128ae629 100644
--- a/Mage.Sets/src/mage/cards/t/TrackDown.java
+++ b/Mage.Sets/src/mage/cards/t/TrackDown.java
@@ -3,7 +3,6 @@ package mage.cards.t;
 import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
-import mage.abilities.effects.keyword.ScryEffect;
 import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -23,8 +22,7 @@ public final class TrackDown extends CardImpl {
     public TrackDown(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{G}");
 
-        // Scry 3, then reveal the top card of your library. If it’s a creature or land card, draw a card.
-        this.getSpellAbility().addEffect(new ScryEffect(3));
+        // Scry 3, then reveal the top card of your library. If it's a creature or land card, draw a card.
         this.getSpellAbility().addEffect(new TrackDownEffect());
     }
 
@@ -42,7 +40,7 @@ class TrackDownEffect extends OneShotEffect {
 
     public TrackDownEffect() {
         super(Outcome.DrawCard);
-        this.staticText = "then reveal the top card of your library. If it's a creature or land card, draw a card";
+        this.staticText = "Scry 3, then reveal the top card of your library. If it's a creature or land card, draw a card";
     }
 
     public TrackDownEffect(final TrackDownEffect effect) {
@@ -67,6 +65,8 @@ class TrackDownEffect extends OneShotEffect {
             return false;
         }
 
+        controller.scry(3, source, game);
+
         Card card = controller.getLibrary().getFromTop(game);
         if (card == null) {
             return false;

From 2dc502b535eec39f29f9eb7a3e46c3aa3d42373f Mon Sep 17 00:00:00 2001
From: arcox <10953229+arcox@users.noreply.github.com>
Date: Fri, 26 Jun 2020 01:02:34 -0400
Subject: [PATCH 516/586] Fix Choking Vines oracle text

---
 Mage.Sets/src/mage/cards/c/ChokingVines.java | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/Mage.Sets/src/mage/cards/c/ChokingVines.java b/Mage.Sets/src/mage/cards/c/ChokingVines.java
index 24b284da90..3dce3af147 100644
--- a/Mage.Sets/src/mage/cards/c/ChokingVines.java
+++ b/Mage.Sets/src/mage/cards/c/ChokingVines.java
@@ -37,7 +37,8 @@ public final class ChokingVines extends CardImpl {
 
         // X target attacking creatures become blocked. Choking Vines deals 1 damage to each of those creatures.
         this.getSpellAbility().addEffect(new ChokingVinesEffect());
-        this.getSpellAbility().addEffect(new DamageTargetEffect(1));
+        this.getSpellAbility().addEffect(new DamageTargetEffect(1)
+                .setText("{this} deals 1 damage to each of those creatures"));
         this.getSpellAbility().setTargetAdjuster(ChokingVinesAdjuster.instance);
     }
 

From c03d0f07d6538595649bddac88f4b1ff8b2d7453 Mon Sep 17 00:00:00 2001
From: arcox <10953229+arcox@users.noreply.github.com>
Date: Fri, 26 Jun 2020 01:44:12 -0400
Subject: [PATCH 517/586] Restore scry effect

---
 Mage.Sets/src/mage/cards/t/TrackDown.java | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/t/TrackDown.java b/Mage.Sets/src/mage/cards/t/TrackDown.java
index e7128ae629..d4451d193e 100644
--- a/Mage.Sets/src/mage/cards/t/TrackDown.java
+++ b/Mage.Sets/src/mage/cards/t/TrackDown.java
@@ -3,6 +3,7 @@ package mage.cards.t;
 import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.keyword.ScryEffect;
 import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -23,6 +24,7 @@ public final class TrackDown extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{G}");
 
         // Scry 3, then reveal the top card of your library. If it's a creature or land card, draw a card.
+        this.getSpellAbility().addEffect(new ScryEffect(3).setText("Scry 3,"));
         this.getSpellAbility().addEffect(new TrackDownEffect());
     }
 
@@ -40,7 +42,7 @@ class TrackDownEffect extends OneShotEffect {
 
     public TrackDownEffect() {
         super(Outcome.DrawCard);
-        this.staticText = "Scry 3, then reveal the top card of your library. If it's a creature or land card, draw a card";
+        this.staticText = "then reveal the top card of your library. If it's a creature or land card, draw a card";
     }
 
     public TrackDownEffect(final TrackDownEffect effect) {
@@ -65,8 +67,6 @@ class TrackDownEffect extends OneShotEffect {
             return false;
         }
 
-        controller.scry(3, source, game);
-
         Card card = controller.getLibrary().getFromTop(game);
         if (card == null) {
             return false;

From 445686a1adafc71de187a458d9c4728a688cb214 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Fri, 26 Jun 2020 09:59:55 +0400
Subject: [PATCH 518/586] Improved scry effect text generation

---
 .../src/mage/cards/b/BontuTheGlorified.java   | 10 ++++-----
 Mage.Sets/src/mage/cards/c/ChainToMemory.java |  2 +-
 .../src/mage/cards/f/FatedConflagration.java  |  2 +-
 .../src/mage/cards/f/FatedIntervention.java   |  3 +--
 .../src/mage/cards/f/FatedRetribution.java    |  2 +-
 Mage.Sets/src/mage/cards/f/Foresee.java       |  5 +++--
 .../src/mage/cards/m/MischievousChimera.java  |  2 +-
 Mage.Sets/src/mage/cards/o/OmenOfTheSea.java  |  4 ++--
 .../mage/cards/o/OverwhelmedApprentice.java   |  2 +-
 Mage.Sets/src/mage/cards/p/Preordain.java     |  8 +++----
 .../src/mage/cards/p/PsychicImpetus.java      |  2 +-
 .../mage/cards/s/ScourAllPossibilities.java   |  4 ++--
 .../src/mage/cards/t/TamiyosEpiphany.java     |  4 ++--
 .../cards/t/TenthDistrictLegionnaire.java     |  5 +++--
 Mage.Sets/src/mage/cards/t/TrackDown.java     |  6 ++---
 .../abilities/effects/keyword/ScryEffect.java | 22 +++++++++++++------
 16 files changed, 46 insertions(+), 37 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/b/BontuTheGlorified.java b/Mage.Sets/src/mage/cards/b/BontuTheGlorified.java
index 79e561b86e..eac46899b4 100644
--- a/Mage.Sets/src/mage/cards/b/BontuTheGlorified.java
+++ b/Mage.Sets/src/mage/cards/b/BontuTheGlorified.java
@@ -37,16 +37,16 @@ public final class BontuTheGlorified extends CardImpl {
         this.power = new MageInt(4);
         this.toughness = new MageInt(6);
 
-        //Menace
-        this.addAbility(new MenaceAbility());
+        // Menace
+        this.addAbility(new MenaceAbility(false));
 
-        //Indestructible
+        // Indestructible
         this.addAbility(IndestructibleAbility.getInstance());
 
-        //Bontu the Glorified can't attack or block unless a creature died under your control this turn.
+        // Bontu the Glorified can't attack or block unless a creature died under your control this turn.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BontuTheGlorifiedRestrictionEffect()), new CreaturesDiedWatcher());
 
-        //{1}{B}, Sacrifice another creature: Scry 1.  Each opponent loses 1 life and you gain 1 life.
+        // {1}{B}, Sacrifice another creature: Scry 1.  Each opponent loses 1 life and you gain 1 life.
         Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ScryEffect(1), new ManaCostsImpl("{1}{B}"));
         ability.addEffect(new LoseLifeOpponentsEffect(1));
         Effect effect = new GainLifeEffect(1);
diff --git a/Mage.Sets/src/mage/cards/c/ChainToMemory.java b/Mage.Sets/src/mage/cards/c/ChainToMemory.java
index 9a58196956..4e3637b748 100644
--- a/Mage.Sets/src/mage/cards/c/ChainToMemory.java
+++ b/Mage.Sets/src/mage/cards/c/ChainToMemory.java
@@ -19,7 +19,7 @@ public final class ChainToMemory extends CardImpl {
 
         // Target creature gets -4/-0 until end of turn. Scry 2.
         this.getSpellAbility().addEffect(new BoostTargetEffect(-4, 0));
-        this.getSpellAbility().addEffect(new ScryEffect(2).setText("Scry 2"));
+        this.getSpellAbility().addEffect(new ScryEffect(2, false));
         this.getSpellAbility().addTarget(new TargetCreaturePermanent());
     }
 
diff --git a/Mage.Sets/src/mage/cards/f/FatedConflagration.java b/Mage.Sets/src/mage/cards/f/FatedConflagration.java
index be6f34b0b6..7c1b4b9a8d 100644
--- a/Mage.Sets/src/mage/cards/f/FatedConflagration.java
+++ b/Mage.Sets/src/mage/cards/f/FatedConflagration.java
@@ -24,7 +24,7 @@ public final class FatedConflagration extends CardImpl {
         // Fated Conflagration deals 5 damage to target creature or planewalker. If it's your turn, scry 2.
         this.getSpellAbility().addEffect(new DamageTargetEffect(5));
         this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker());
-        this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new ScryEffect(2), MyTurnCondition.instance, "If it's your turn, scry 2"));
+        this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new ScryEffect(2, false), MyTurnCondition.instance, "If it's your turn, scry 2"));
         this.getSpellAbility().addHint(MyTurnHint.instance);
     }
 
diff --git a/Mage.Sets/src/mage/cards/f/FatedIntervention.java b/Mage.Sets/src/mage/cards/f/FatedIntervention.java
index 92d6442d13..2b1ea48ef0 100644
--- a/Mage.Sets/src/mage/cards/f/FatedIntervention.java
+++ b/Mage.Sets/src/mage/cards/f/FatedIntervention.java
@@ -20,10 +20,9 @@ public final class FatedIntervention extends CardImpl {
     public FatedIntervention(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{G}{G}{G}");
 
-
         // Create two 3/3 green Centaur enchantment creature tokens. If it's your turn, scry 2.
         this.getSpellAbility().addEffect(new CreateTokenEffect(new CentaurEnchantmentCreatureToken(), 2));
-        this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new ScryEffect(2), MyTurnCondition.instance, "If it's your turn, scry 2"));
+        this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new ScryEffect(2, false), MyTurnCondition.instance, "If it's your turn, scry 2"));
         this.getSpellAbility().addHint(MyTurnHint.instance);
     }
 
diff --git a/Mage.Sets/src/mage/cards/f/FatedRetribution.java b/Mage.Sets/src/mage/cards/f/FatedRetribution.java
index d0a35a9ed8..23654106e7 100644
--- a/Mage.Sets/src/mage/cards/f/FatedRetribution.java
+++ b/Mage.Sets/src/mage/cards/f/FatedRetribution.java
@@ -32,7 +32,7 @@ public final class FatedRetribution extends CardImpl {
 
         // Destroy all creatures and planeswalkers. If it's your turn, scry 2.
         this.getSpellAbility().addEffect(new DestroyAllEffect(filter, false));
-        this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new ScryEffect(2), MyTurnCondition.instance, "If it's your turn, scry 2"));
+        this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new ScryEffect(2, false), MyTurnCondition.instance, "If it's your turn, scry 2"));
         this.getSpellAbility().addHint(MyTurnHint.instance);
     }
 
diff --git a/Mage.Sets/src/mage/cards/f/Foresee.java b/Mage.Sets/src/mage/cards/f/Foresee.java
index 52b112e3a0..d081d3acc7 100644
--- a/Mage.Sets/src/mage/cards/f/Foresee.java
+++ b/Mage.Sets/src/mage/cards/f/Foresee.java
@@ -16,8 +16,9 @@ public final class Foresee extends CardImpl {
     public Foresee(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{U}");
 
-        this.getSpellAbility().addEffect(new ScryEffect(4).setText("scry 4,"));
-        this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2).setText("then draw two cards"));
+        // Scry 4, then draw two cards. (To scry 4, look at the top four cards of your library, then put any number of them on the bottom of your library and the rest on top in any order.)
+        this.getSpellAbility().addEffect(new ScryEffect(4, false));
+        this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2).concatBy(", then"));
     }
 
     public Foresee(final Foresee card) {
diff --git a/Mage.Sets/src/mage/cards/m/MischievousChimera.java b/Mage.Sets/src/mage/cards/m/MischievousChimera.java
index 09bf177b4e..0806c73797 100644
--- a/Mage.Sets/src/mage/cards/m/MischievousChimera.java
+++ b/Mage.Sets/src/mage/cards/m/MischievousChimera.java
@@ -33,7 +33,7 @@ public final class MischievousChimera extends CardImpl {
         Ability ability = new FirstSpellOpponentsTurnTriggeredAbility(
                 new DamagePlayersEffect(1, TargetController.OPPONENT), false
         );
-        ability.addEffect(new ScryEffect(1).setText("Scry 1"));
+        ability.addEffect(new ScryEffect(1, false));
         this.addAbility(ability);
     }
 
diff --git a/Mage.Sets/src/mage/cards/o/OmenOfTheSea.java b/Mage.Sets/src/mage/cards/o/OmenOfTheSea.java
index 2cc325328b..6b9ddb1d34 100644
--- a/Mage.Sets/src/mage/cards/o/OmenOfTheSea.java
+++ b/Mage.Sets/src/mage/cards/o/OmenOfTheSea.java
@@ -26,8 +26,8 @@ public final class OmenOfTheSea extends CardImpl {
         this.addAbility(FlashAbility.getInstance());
 
         // When Omen of the Sea enters the battlefield, scry 2, then draw a card.
-        Ability ability = new EntersBattlefieldTriggeredAbility(new ScryEffect(2).setText("scry 2,"));
-        ability.addEffect(new DrawCardSourceControllerEffect(1).concatBy("then"));
+        Ability ability = new EntersBattlefieldTriggeredAbility(new ScryEffect(2, false));
+        ability.addEffect(new DrawCardSourceControllerEffect(1).concatBy(", then"));
         this.addAbility(ability);
 
         // {2}{U}, Sacrifice Omen of the Sea: Scry 2.
diff --git a/Mage.Sets/src/mage/cards/o/OverwhelmedApprentice.java b/Mage.Sets/src/mage/cards/o/OverwhelmedApprentice.java
index c62134e566..32f9fe239f 100644
--- a/Mage.Sets/src/mage/cards/o/OverwhelmedApprentice.java
+++ b/Mage.Sets/src/mage/cards/o/OverwhelmedApprentice.java
@@ -30,7 +30,7 @@ public final class OverwhelmedApprentice extends CardImpl {
         Ability ability = new EntersBattlefieldTriggeredAbility(
                 new PutTopCardOfLibraryIntoGraveEachPlayerEffect(2, TargetController.OPPONENT)
         );
-        ability.addEffect(new ScryEffect(2).setText("Then you scry 2."));
+        ability.addEffect(new ScryEffect(2, false).setText("Then you scry 2."));
         this.addAbility(ability);
     }
 
diff --git a/Mage.Sets/src/mage/cards/p/Preordain.java b/Mage.Sets/src/mage/cards/p/Preordain.java
index 4eae38cd40..39ce81a2c4 100644
--- a/Mage.Sets/src/mage/cards/p/Preordain.java
+++ b/Mage.Sets/src/mage/cards/p/Preordain.java
@@ -1,14 +1,14 @@
 package mage.cards.p;
 
-import java.util.UUID;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.abilities.effects.keyword.ScryEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 
+import java.util.UUID;
+
 /**
- *
  * @author BetaSteward_at_googlemail.com
  */
 public final class Preordain extends CardImpl {
@@ -18,11 +18,11 @@ public final class Preordain extends CardImpl {
 
         // Scry 2, then draw a card. (To scry 2, look at the top two cards of your library, then put any number of them on the bottom of your library and the rest on top in any order.)
         this.getSpellAbility().addEffect(
-                new ScryEffect(2).setText("Scry 2, ")
+                new ScryEffect(2, false)
         );
         this.getSpellAbility().addEffect(
                 new DrawCardSourceControllerEffect(1)
-                        .setText("then draw a card. <i>(To scry 2, "
+                        .setText(", then draw a card. <i>(To scry 2, "
                                 + "look at the top two cards of your library, "
                                 + "then put any number of them on the "
                                 + "bottom of your library and the rest on "
diff --git a/Mage.Sets/src/mage/cards/p/PsychicImpetus.java b/Mage.Sets/src/mage/cards/p/PsychicImpetus.java
index 8a0d6a96e8..bbbedc9098 100644
--- a/Mage.Sets/src/mage/cards/p/PsychicImpetus.java
+++ b/Mage.Sets/src/mage/cards/p/PsychicImpetus.java
@@ -40,7 +40,7 @@ public final class PsychicImpetus extends CardImpl {
 
         // Whenever enchanted creature attacks, you scry 2.
         this.addAbility(new AttacksAttachedTriggeredAbility(
-                new ScryEffect(2).setText("you scry 2"), AttachmentType.AURA, false
+                new ScryEffect(2, false).setText("you scry 2"), AttachmentType.AURA, false
         ));
     }
 
diff --git a/Mage.Sets/src/mage/cards/s/ScourAllPossibilities.java b/Mage.Sets/src/mage/cards/s/ScourAllPossibilities.java
index e3d67b370c..6ce1b3d0f2 100644
--- a/Mage.Sets/src/mage/cards/s/ScourAllPossibilities.java
+++ b/Mage.Sets/src/mage/cards/s/ScourAllPossibilities.java
@@ -20,8 +20,8 @@ public final class ScourAllPossibilities extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{U}");
 
         // Scry 2, then draw a card.
-        this.getSpellAbility().addEffect(new ScryEffect(2).setText("scry 2,"));
-        this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("then"));
+        this.getSpellAbility().addEffect(new ScryEffect(2, false));
+        this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy(", then"));
 
         // Flashback {4}{U}
         this.addAbility(new FlashbackAbility(new ManaCostsImpl("{4}{U}"), TimingRule.SORCERY));
diff --git a/Mage.Sets/src/mage/cards/t/TamiyosEpiphany.java b/Mage.Sets/src/mage/cards/t/TamiyosEpiphany.java
index bd626fa4f0..e924bb5021 100644
--- a/Mage.Sets/src/mage/cards/t/TamiyosEpiphany.java
+++ b/Mage.Sets/src/mage/cards/t/TamiyosEpiphany.java
@@ -17,8 +17,8 @@ public final class TamiyosEpiphany extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{U}");
 
         // Scry 4, then draw two cards.
-        this.getSpellAbility().addEffect(new ScryEffect(4).setText("scry 4,"));
-        this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2).setText("then draw two cards"));
+        this.getSpellAbility().addEffect(new ScryEffect(4, false));
+        this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2).concatBy(", then"));
     }
 
     private TamiyosEpiphany(final TamiyosEpiphany card) {
diff --git a/Mage.Sets/src/mage/cards/t/TenthDistrictLegionnaire.java b/Mage.Sets/src/mage/cards/t/TenthDistrictLegionnaire.java
index 3f1dd36652..b4e5220460 100644
--- a/Mage.Sets/src/mage/cards/t/TenthDistrictLegionnaire.java
+++ b/Mage.Sets/src/mage/cards/t/TenthDistrictLegionnaire.java
@@ -1,6 +1,5 @@
 package mage.cards.t;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.effects.common.counter.AddCountersSourceEffect;
@@ -13,6 +12,8 @@ import mage.constants.CardType;
 import mage.constants.SubType;
 import mage.counters.CounterType;
 
+import java.util.UUID;
+
 /**
  * @author TheElk801
  */
@@ -33,7 +34,7 @@ public final class TenthDistrictLegionnaire extends CardImpl {
         Ability ability = new HeroicAbility(new AddCountersSourceEffect(
                 CounterType.P1P1.createInstance()
         ), false, false);
-        ability.addEffect(new ScryEffect(1).setText(", then scry 1"));
+        ability.addEffect(new ScryEffect(1).concatBy(", then"));
         this.addAbility(ability);
     }
 
diff --git a/Mage.Sets/src/mage/cards/t/TrackDown.java b/Mage.Sets/src/mage/cards/t/TrackDown.java
index d4451d193e..25051d9ebb 100644
--- a/Mage.Sets/src/mage/cards/t/TrackDown.java
+++ b/Mage.Sets/src/mage/cards/t/TrackDown.java
@@ -24,8 +24,8 @@ public final class TrackDown extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{G}");
 
         // Scry 3, then reveal the top card of your library. If it's a creature or land card, draw a card.
-        this.getSpellAbility().addEffect(new ScryEffect(3).setText("Scry 3,"));
-        this.getSpellAbility().addEffect(new TrackDownEffect());
+        this.getSpellAbility().addEffect(new ScryEffect(3, false));
+        this.getSpellAbility().addEffect(new TrackDownEffect().concatBy(", then"));
     }
 
     public TrackDown(final TrackDown card) {
@@ -42,7 +42,7 @@ class TrackDownEffect extends OneShotEffect {
 
     public TrackDownEffect() {
         super(Outcome.DrawCard);
-        this.staticText = "then reveal the top card of your library. If it's a creature or land card, draw a card";
+        this.staticText = "reveal the top card of your library. If it’s a creature or land card, draw a card";
     }
 
     public TrackDownEffect(final TrackDownEffect effect) {
diff --git a/Mage/src/main/java/mage/abilities/effects/keyword/ScryEffect.java b/Mage/src/main/java/mage/abilities/effects/keyword/ScryEffect.java
index 99fd8e360a..d4ebabe2b3 100644
--- a/Mage/src/main/java/mage/abilities/effects/keyword/ScryEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/keyword/ScryEffect.java
@@ -8,22 +8,28 @@ import mage.players.Player;
 import mage.util.CardUtil;
 
 /**
- *
  * @author BetaSteward_at_googlemail.com
  */
 public class ScryEffect extends OneShotEffect {
 
     protected int scryNumber;
+    protected boolean showEffectHint;
 
     public ScryEffect(int scryNumber) {
+        this(scryNumber, true);
+    }
+
+    public ScryEffect(int scryNumber, boolean showEffectHint) {
         super(Outcome.Benefit);
         this.scryNumber = scryNumber;
+        this.showEffectHint = showEffectHint;
         this.setText();
     }
 
     public ScryEffect(final ScryEffect effect) {
         super(effect);
         this.scryNumber = effect.scryNumber;
+        this.showEffectHint = effect.showEffectHint;
     }
 
     @Override
@@ -42,12 +48,14 @@ public class ScryEffect extends OneShotEffect {
 
     private void setText() {
         StringBuilder sb = new StringBuilder("scry ").append(scryNumber);
-        if (scryNumber == 1) {
-            sb.append(". <i>(Look at the top card of your library. You may put that card on the bottom of your library.)</i>");
-        } else {
-            sb.append(". <i>(Look at the top ");
-            sb.append(CardUtil.numberToText(scryNumber));
-            sb.append(" cards of your library, then put any number of them on the bottom of your library and the rest on top in any order.)</i>");
+        if (showEffectHint) {
+            if (scryNumber == 1) {
+                sb.append(". <i>(Look at the top card of your library. You may put that card on the bottom of your library.)</i>");
+            } else {
+                sb.append(". <i>(Look at the top ");
+                sb.append(CardUtil.numberToText(scryNumber));
+                sb.append(" cards of your library, then put any number of them on the bottom of your library and the rest on top in any order.)</i>");
+            }
         }
         staticText = sb.toString();
     }

From f19c92922978cf647c3d7494971e497f668ede8f Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Fri, 26 Jun 2020 10:14:08 +0400
Subject: [PATCH 519/586] Verify tests: added checks for wrong ability/effect
 hint description in card text;

---
 Mage.Sets/src/mage/cards/t/TrackDown.java     |  2 +-
 .../java/mage/verify/VerifyCardDataTest.java  | 27 ++++++++++++++++++-
 2 files changed, 27 insertions(+), 2 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/t/TrackDown.java b/Mage.Sets/src/mage/cards/t/TrackDown.java
index 25051d9ebb..08506dd9d0 100644
--- a/Mage.Sets/src/mage/cards/t/TrackDown.java
+++ b/Mage.Sets/src/mage/cards/t/TrackDown.java
@@ -42,7 +42,7 @@ class TrackDownEffect extends OneShotEffect {
 
     public TrackDownEffect() {
         super(Outcome.DrawCard);
-        this.staticText = "reveal the top card of your library. If it’s a creature or land card, draw a card";
+        this.staticText = "reveal the top card of your library. If it's a creature or land card, draw a card";
     }
 
     public TrackDownEffect(final TrackDownEffect effect) {
diff --git a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java
index 7661bc9f8e..7aac72e181 100644
--- a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java
+++ b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java
@@ -3,6 +3,9 @@ package mage.verify;
 import mage.ObjectColor;
 import mage.abilities.Ability;
 import mage.abilities.effects.CostModificationEffect;
+import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect;
+import mage.abilities.effects.keyword.ScryEffect;
+import mage.abilities.keyword.MenaceAbility;
 import mage.abilities.keyword.MultikickerAbility;
 import mage.cards.*;
 import mage.cards.basiclands.BasicLand;
@@ -158,7 +161,7 @@ public class VerifyCardDataTest {
 
         printMessages(outputMessages);
         if (failed > 0) {
-            Assert.fail(failed + " errors in verify");
+            Assert.fail("found " + failed + " errors in cards verify (see errors list above)");
         }
     }
 
@@ -925,6 +928,28 @@ public class VerifyCardDataTest {
             fail(card, "abilities", "card have Multikicker ability, but missing it in rules text");
         }
 
+        // special check: missing or wrong ability/effect hints
+        Map<Class, String> hints = new HashMap<>();
+        hints.put(MenaceAbility.class, "can't be blocked except by two or more");
+        hints.put(ScryEffect.class, "Look at the top card of your library");
+        for (Class objectClass : hints.keySet()) {
+            String objectHint = hints.get(objectClass);
+            // ability/effect must have description or not
+            boolean mustCheck = card.getAbilities().containsClass(objectClass)
+                    || card.getAbilities().stream()
+                    .map(Ability::getAllEffects)
+                    .flatMap(Collection::stream)
+                    .anyMatch(effect -> effect.getClass().isAssignableFrom(objectClass));
+            mustCheck = false; // TODO: enable and fix all problems with effect and ability hints
+            if (mustCheck) {
+                boolean needHint = ref.text.contains(objectHint);
+                boolean haveHint = card.getRules().stream().anyMatch(rule -> rule.contains(objectHint));
+                if (needHint != haveHint) {
+                    fail(card, "abilities", "card have " + objectClass.getSimpleName() + " but hint is wrong (it must be " + (needHint ? "enabled" : "disabled") + ")");
+                }
+            }
+        }
+
         // spells have only 1 ability
         if (card.isSorcery() || card.isInstant()) {
             return;

From 99167fa50e28991b100a10ed554359c53147708a Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Fri, 26 Jun 2020 10:55:33 +0400
Subject: [PATCH 520/586] [M21] Terror of the Peaks - fixed missing copy method

---
 Mage.Sets/src/mage/cards/t/TerrorOfThePeaks.java | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/Mage.Sets/src/mage/cards/t/TerrorOfThePeaks.java b/Mage.Sets/src/mage/cards/t/TerrorOfThePeaks.java
index fc080a11e4..93eadfdf00 100644
--- a/Mage.Sets/src/mage/cards/t/TerrorOfThePeaks.java
+++ b/Mage.Sets/src/mage/cards/t/TerrorOfThePeaks.java
@@ -107,6 +107,11 @@ class TerrorOfThePeaksTriggeredAbility extends EntersBattlefieldAllTriggeredAbil
         super(ability);
     }
 
+    @Override
+    public TerrorOfThePeaksTriggeredAbility copy() {
+        return new TerrorOfThePeaksTriggeredAbility(this);
+    }
+
     @Override
     public boolean checkTrigger(GameEvent event, Game game) {
         if (!super.checkTrigger(event, game)) {
@@ -119,7 +124,7 @@ class TerrorOfThePeaksTriggeredAbility extends EntersBattlefieldAllTriggeredAbil
         this.getEffects().clear();
         this.getTargets().clear();
         this.addEffect(new DamageTargetEffect(permanent.getPower().getValue()));
-        this.addTarget(new TargetAnyTarget());
+        this.addTarget(new TargetAnyTarget().withChooseHint("gets " + permanent.getPower().getValue() + " damage"));
         return true;
     }
 

From 50c46e39a37751c3c0fffdbc664ba22ea826e73e Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Fri, 26 Jun 2020 10:59:22 +0400
Subject: [PATCH 521/586] Improved game logs: added target hints, fixed spaces
 in target amount;

---
 Mage.Sets/src/mage/cards/f/ForkedBolt.java    |  9 +++----
 .../main/java/mage/abilities/AbilityImpl.java |  2 ++
 .../CopySpellForEachItCouldTargetEffect.java  |  2 +-
 Mage/src/main/java/mage/target/Target.java    |  2 ++
 .../src/main/java/mage/target/TargetImpl.java |  9 +++++--
 .../main/java/mage/target/TargetObject.java   |  6 ++---
 .../main/java/mage/target/TargetPlayer.java   | 12 ++++-----
 .../target/common/TargetActivatedAbility.java | 13 ++++-----
 .../mage/target/common/TargetAnyTarget.java   | 12 ++++-----
 .../target/common/TargetCreatureOrPlayer.java |  8 +++---
 .../mage/target/common/TargetDefender.java    | 27 +++++++++----------
 .../target/common/TargetPermanentAmount.java  |  8 +++---
 .../common/TargetPermanentOrPlayer.java       |  2 +-
 .../common/TargetPermanentOrPlayerAmount.java |  6 ++---
 .../TargetPermanentOrSuspendedCard.java       | 22 +++++++--------
 .../target/common/TargetSpellOrPermanent.java | 12 ++++-----
 16 files changed, 76 insertions(+), 76 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/f/ForkedBolt.java b/Mage.Sets/src/mage/cards/f/ForkedBolt.java
index cf57dc2b75..83db5716c5 100644
--- a/Mage.Sets/src/mage/cards/f/ForkedBolt.java
+++ b/Mage.Sets/src/mage/cards/f/ForkedBolt.java
@@ -1,7 +1,5 @@
-
 package mage.cards.f;
 
-import java.util.UUID;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.DamageMultiEffect;
 import mage.cards.CardImpl;
@@ -9,22 +7,21 @@ import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.target.common.TargetAnyTargetAmount;
 
+import java.util.UUID;
+
 /**
- *
  * @author jeffwadsworth
  */
 public final class ForkedBolt extends CardImpl {
 
     public ForkedBolt(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{R}");
-
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{R}");
 
         // Forked Bolt deals 2 damage divided as you choose among one or two target creatures and/or players.
         Effect effect = new DamageMultiEffect(2);
         effect.setText("{this} deals 2 damage divided as you choose among one or two target creatures and/or players");
         this.getSpellAbility().addEffect(effect);
         this.getSpellAbility().addTarget(new TargetAnyTargetAmount(2));
-        
     }
 
     public ForkedBolt(final ForkedBolt card) {
diff --git a/Mage/src/main/java/mage/abilities/AbilityImpl.java b/Mage/src/main/java/mage/abilities/AbilityImpl.java
index 4a053e6a86..5f1512e321 100644
--- a/Mage/src/main/java/mage/abilities/AbilityImpl.java
+++ b/Mage/src/main/java/mage/abilities/AbilityImpl.java
@@ -1116,6 +1116,7 @@ public abstract class AbilityImpl implements Ability {
             String usedVerb = null;
             for (Target target : targets) {
                 if (!target.getTargets().isEmpty()) {
+                    String targetHintInfo = target.getChooseHint() == null ? "" : " (" + target.getChooseHint() + ")";
                     if (!target.isNotTarget()) {
                         if (usedVerb == null || usedVerb.equals(" choosing ")) {
                             usedVerb = " targeting ";
@@ -1126,6 +1127,7 @@ public abstract class AbilityImpl implements Ability {
                         sb.append(usedVerb);
                     }
                     sb.append(target.getTargetedName(game));
+                    sb.append(targetHintInfo);
                 }
             }
         }
diff --git a/Mage/src/main/java/mage/abilities/effects/common/CopySpellForEachItCouldTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CopySpellForEachItCouldTargetEffect.java
index dccf10bc17..3da5053047 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/CopySpellForEachItCouldTargetEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/CopySpellForEachItCouldTargetEffect.java
@@ -450,6 +450,6 @@ class TargetWithAdditionalFilter<T extends MageItem> extends TargetImpl {
                 }
             }
         }
-        return sb.toString();
+        return sb.toString().trim();
     }
 }
diff --git a/Mage/src/main/java/mage/target/Target.java b/Mage/src/main/java/mage/target/Target.java
index ec2a5aa573..267c97558b 100644
--- a/Mage/src/main/java/mage/target/Target.java
+++ b/Mage/src/main/java/mage/target/Target.java
@@ -137,5 +137,7 @@ public interface Target extends Serializable {
 
     Target withChooseHint(String chooseHint);
 
+    String getChooseHint();
+
     void setEventReporting(boolean shouldReport);
 }
diff --git a/Mage/src/main/java/mage/target/TargetImpl.java b/Mage/src/main/java/mage/target/TargetImpl.java
index 8c36e138c8..9d0eaebd0d 100644
--- a/Mage/src/main/java/mage/target/TargetImpl.java
+++ b/Mage/src/main/java/mage/target/TargetImpl.java
@@ -112,8 +112,8 @@ public abstract class TargetImpl implements Target {
             sb.append(suffix);
             return sb.toString();
         }
-        if (targetName.startsWith("another") 
-                || targetName.startsWith("a ") 
+        if (targetName.startsWith("another")
+                || targetName.startsWith("a ")
                 || targetName.startsWith("an ")
                 || targetName.startsWith("any ")) {
             return "Select " + targetName + suffix;
@@ -555,6 +555,11 @@ public abstract class TargetImpl implements Target {
         return this;
     }
 
+    @Override
+    public String getChooseHint() {
+        return chooseHint;
+    }
+
     @Override
     public void setEventReporting(boolean shouldReport) {
         this.shouldReportEvents = shouldReport;
diff --git a/Mage/src/main/java/mage/target/TargetObject.java b/Mage/src/main/java/mage/target/TargetObject.java
index 575ad44873..e5efde8b8b 100644
--- a/Mage/src/main/java/mage/target/TargetObject.java
+++ b/Mage/src/main/java/mage/target/TargetObject.java
@@ -1,13 +1,13 @@
 package mage.target;
 
-import java.util.UUID;
 import mage.MageObject;
 import mage.abilities.Ability;
 import mage.constants.Zone;
 import mage.game.Game;
 
+import java.util.UUID;
+
 /**
- *
  * @author BetaSteward_at_googlemail.com
  */
 public abstract class TargetObject extends TargetImpl {
@@ -44,7 +44,7 @@ public abstract class TargetObject extends TargetImpl {
                 sb.append(object.getLogName()).append(' ');
             }
         }
-        return sb.toString();
+        return sb.toString().trim();
     }
 
     @Override
diff --git a/Mage/src/main/java/mage/target/TargetPlayer.java b/Mage/src/main/java/mage/target/TargetPlayer.java
index 7b2d0bdf77..2b7e126551 100644
--- a/Mage/src/main/java/mage/target/TargetPlayer.java
+++ b/Mage/src/main/java/mage/target/TargetPlayer.java
@@ -1,16 +1,16 @@
 package mage.target;
 
-import java.util.HashSet;
-import java.util.Set;
-import java.util.UUID;
 import mage.MageObject;
 import mage.abilities.Ability;
 import mage.filter.FilterPlayer;
 import mage.game.Game;
 import mage.players.Player;
 
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
+
 /**
- *
  * @author BetaSteward_at_googlemail.com
  */
 public class TargetPlayer extends TargetImpl {
@@ -51,7 +51,7 @@ public class TargetPlayer extends TargetImpl {
      * Checks if there are enough {@link Player} that can be chosen. Should only
      * be used for Ability targets since this checks for protection, shroud etc.
      *
-     * @param sourceId - the target event source
+     * @param sourceId           - the target event source
      * @param sourceControllerId - controller of the target event source
      * @param game
      * @return - true if enough valid {@link Player} exist
@@ -170,7 +170,7 @@ public class TargetPlayer extends TargetImpl {
                 sb.append("[target missing]");
             }
         }
-        return sb.toString();
+        return sb.toString().trim();
     }
 
     @Override
diff --git a/Mage/src/main/java/mage/target/common/TargetActivatedAbility.java b/Mage/src/main/java/mage/target/common/TargetActivatedAbility.java
index 5e1ca2be78..d256885d4f 100644
--- a/Mage/src/main/java/mage/target/common/TargetActivatedAbility.java
+++ b/Mage/src/main/java/mage/target/common/TargetActivatedAbility.java
@@ -1,11 +1,6 @@
-
 package mage.target.common;
 
-import java.util.HashSet;
-import java.util.Set;
-import java.util.UUID;
 import mage.abilities.Ability;
-import mage.abilities.ActivatedAbility;
 import mage.constants.AbilityType;
 import mage.constants.Zone;
 import mage.filter.Filter;
@@ -16,8 +11,11 @@ import mage.game.stack.StackAbility;
 import mage.game.stack.StackObject;
 import mage.target.TargetObject;
 
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
+
 /**
- *
  * @author emerald000
  */
 public class TargetActivatedAbility extends TargetObject {
@@ -108,7 +106,6 @@ public class TargetActivatedAbility extends TargetObject {
                 sb.append(object.getRule()).append(' ');
             }
         }
-        sb.append(')');
-        return sb.toString();
+        return sb.toString().trim() + ")";
     }
 }
diff --git a/Mage/src/main/java/mage/target/common/TargetAnyTarget.java b/Mage/src/main/java/mage/target/common/TargetAnyTarget.java
index 8204246dc7..f2fa5459b3 100644
--- a/Mage/src/main/java/mage/target/common/TargetAnyTarget.java
+++ b/Mage/src/main/java/mage/target/common/TargetAnyTarget.java
@@ -1,9 +1,5 @@
 package mage.target.common;
 
-import java.util.HashSet;
-import java.util.Set;
-import java.util.UUID;
-
 import mage.MageObject;
 import mage.abilities.Ability;
 import mage.constants.Zone;
@@ -14,6 +10,10 @@ import mage.game.permanent.Permanent;
 import mage.players.Player;
 import mage.target.TargetImpl;
 
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
+
 /**
  * @author JRHerlehy Created on 4/8/18.
  */
@@ -96,7 +96,7 @@ public class TargetAnyTarget extends TargetImpl {
      * be chosen. Should only be used for Ability targets since this checks for
      * protection, shroud etc.
      *
-     * @param sourceId - the target event source
+     * @param sourceId           - the target event source
      * @param sourceControllerId - controller of the target event source
      * @param game
      * @return - true if enough valid {@link Permanent} or {@link Player} exist
@@ -252,7 +252,7 @@ public class TargetAnyTarget extends TargetImpl {
                 }
             }
         }
-        return sb.toString();
+        return sb.toString().trim();
     }
 
     @Override
diff --git a/Mage/src/main/java/mage/target/common/TargetCreatureOrPlayer.java b/Mage/src/main/java/mage/target/common/TargetCreatureOrPlayer.java
index 2c74ef554a..a8ef0efe33 100644
--- a/Mage/src/main/java/mage/target/common/TargetCreatureOrPlayer.java
+++ b/Mage/src/main/java/mage/target/common/TargetCreatureOrPlayer.java
@@ -1,9 +1,8 @@
-
 package mage.target.common;
 
-import mage.constants.Zone;
 import mage.MageObject;
 import mage.abilities.Ability;
+import mage.constants.Zone;
 import mage.filter.Filter;
 import mage.filter.common.FilterCreatureOrPlayer;
 import mage.game.Game;
@@ -16,7 +15,6 @@ import java.util.Set;
 import java.util.UUID;
 
 /**
- *
  * @author BetaSteward_at_googlemail.com
  */
 public class TargetCreatureOrPlayer extends TargetImpl {
@@ -98,7 +96,7 @@ public class TargetCreatureOrPlayer extends TargetImpl {
      * be chosen. Should only be used for Ability targets since this checks for
      * protection, shroud etc.
      *
-     * @param sourceId - the target event source
+     * @param sourceId           - the target event source
      * @param sourceControllerId - controller of the target event source
      * @param game
      * @return - true if enough valid {@link Permanent} or {@link Player} exist
@@ -211,7 +209,7 @@ public class TargetCreatureOrPlayer extends TargetImpl {
                 }
             }
         }
-        return sb.toString();
+        return sb.toString().trim();
     }
 
     @Override
diff --git a/Mage/src/main/java/mage/target/common/TargetDefender.java b/Mage/src/main/java/mage/target/common/TargetDefender.java
index 863c419b28..d83839e806 100644
--- a/Mage/src/main/java/mage/target/common/TargetDefender.java
+++ b/Mage/src/main/java/mage/target/common/TargetDefender.java
@@ -1,9 +1,5 @@
-
 package mage.target.common;
 
-import java.util.HashSet;
-import java.util.Set;
-import java.util.UUID;
 import mage.MageObject;
 import mage.abilities.Ability;
 import mage.constants.Zone;
@@ -15,8 +11,11 @@ import mage.game.permanent.Permanent;
 import mage.players.Player;
 import mage.target.TargetImpl;
 
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
+
 /**
- *
  * @author BetaSteward_at_googlemail.com
  */
 public class TargetDefender extends TargetImpl {
@@ -68,7 +67,7 @@ public class TargetDefender extends TargetImpl {
             }
         }
         for (Permanent permanent : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_PLANESWALKER, sourceControllerId, game)) {
-            if ((notTarget 
+            if ((notTarget
                     || permanent.canBeTargetedBy(targetSource, sourceControllerId, game))
                     && filter.match(permanent, game)) {
                 count++;
@@ -85,7 +84,7 @@ public class TargetDefender extends TargetImpl {
         int count = 0;
         for (UUID playerId : game.getState().getPlayersInRange(sourceControllerId, game)) {
             Player player = game.getPlayer(playerId);
-            if (player != null 
+            if (player != null
                     && filter.match(player, game)) {
                 count++;
                 if (count >= this.minNumberOfTargets) {
@@ -111,14 +110,14 @@ public class TargetDefender extends TargetImpl {
         for (UUID playerId : game.getState().getPlayersInRange(sourceControllerId, game)) {
             Player player = game.getPlayer(playerId);
             if (player != null
-                    && (notTarget 
+                    && (notTarget
                     || player.canBeTargetedBy(targetSource, sourceControllerId, game))
                     && filter.match(player, game)) {
                 possibleTargets.add(playerId);
             }
         }
         for (Permanent permanent : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_PLANESWALKER, sourceControllerId, game)) {
-            if ((notTarget 
+            if ((notTarget
                     || permanent.canBeTargetedBy(targetSource, sourceControllerId, game))
                     && filter.match(permanent, game)) {
                 possibleTargets.add(permanent.getId());
@@ -132,7 +131,7 @@ public class TargetDefender extends TargetImpl {
         Set<UUID> possibleTargets = new HashSet<>();
         for (UUID playerId : game.getState().getPlayersInRange(sourceControllerId, game)) {
             Player player = game.getPlayer(playerId);
-            if (player != null 
+            if (player != null
                     && filter.match(player, game)) {
                 possibleTargets.add(playerId);
             }
@@ -157,7 +156,7 @@ public class TargetDefender extends TargetImpl {
                 sb.append(player.getLogName()).append(' ');
             }
         }
-        return sb.toString();
+        return sb.toString().trim();
     }
 
     @Override
@@ -167,7 +166,7 @@ public class TargetDefender extends TargetImpl {
             return filter.match(player, game);
         }
         Permanent permanent = game.getPermanent(id);
-        return permanent != null 
+        return permanent != null
                 && filter.match(permanent, game);
     }
 
@@ -176,7 +175,7 @@ public class TargetDefender extends TargetImpl {
         Player player = game.getPlayer(id);
         MageObject targetSource = game.getObject(attackerId);
         if (player != null) {
-            return (notTarget 
+            return (notTarget
                     || player.canBeTargetedBy(targetSource, (source == null ? null : source.getControllerId()), game))
                     && filter.match(player, game);
         }
@@ -187,7 +186,7 @@ public class TargetDefender extends TargetImpl {
             if (source != null) {
                 controllerId = source.getControllerId();
             }
-            return (notTarget 
+            return (notTarget
                     || permanent.canBeTargetedBy(targetSource, controllerId, game))
                     && filter.match(permanent, game);
         }
diff --git a/Mage/src/main/java/mage/target/common/TargetPermanentAmount.java b/Mage/src/main/java/mage/target/common/TargetPermanentAmount.java
index ecfd696f5c..2556df419d 100644
--- a/Mage/src/main/java/mage/target/common/TargetPermanentAmount.java
+++ b/Mage/src/main/java/mage/target/common/TargetPermanentAmount.java
@@ -78,13 +78,13 @@ public abstract class TargetPermanentAmount extends TargetAmount {
 
     @Override
     public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) {
-        return game.getBattlefield().getActivePermanents(filter, sourceControllerId, sourceId, game).size() 
+        return game.getBattlefield().getActivePermanents(filter, sourceControllerId, sourceId, game).size()
                 >= this.minNumberOfTargets;
     }
 
     @Override
     public boolean canChoose(UUID sourceControllerId, Game game) {
-        return game.getBattlefield().getActivePermanents(filter, sourceControllerId, game).size() 
+        return game.getBattlefield().getActivePermanents(filter, sourceControllerId, game).size()
                 >= this.minNumberOfTargets;
     }
 
@@ -127,10 +127,10 @@ public abstract class TargetPermanentAmount extends TargetAmount {
         getTargets().forEach((targetId) -> {
             Permanent permanent = game.getPermanent(targetId);
             if (permanent != null) {
-                sb.append(permanent.getLogName()).append('(').append(getTargetAmount(targetId)).append(") ");
+                sb.append(permanent.getLogName()).append(" (").append(getTargetAmount(targetId)).append(") ");
             }
         });
-        return sb.toString();
+        return sb.toString().trim();
     }
 
     @Override
diff --git a/Mage/src/main/java/mage/target/common/TargetPermanentOrPlayer.java b/Mage/src/main/java/mage/target/common/TargetPermanentOrPlayer.java
index cee4973c4f..6a99354f10 100644
--- a/Mage/src/main/java/mage/target/common/TargetPermanentOrPlayer.java
+++ b/Mage/src/main/java/mage/target/common/TargetPermanentOrPlayer.java
@@ -220,7 +220,7 @@ public class TargetPermanentOrPlayer extends TargetImpl {
                 sb.append(player.getLogName()).append(' ');
             }
         }
-        return sb.toString();
+        return sb.toString().trim();
     }
 
     @Override
diff --git a/Mage/src/main/java/mage/target/common/TargetPermanentOrPlayerAmount.java b/Mage/src/main/java/mage/target/common/TargetPermanentOrPlayerAmount.java
index adc75fae2c..3971c1aa5c 100644
--- a/Mage/src/main/java/mage/target/common/TargetPermanentOrPlayerAmount.java
+++ b/Mage/src/main/java/mage/target/common/TargetPermanentOrPlayerAmount.java
@@ -211,12 +211,12 @@ public abstract class TargetPermanentOrPlayerAmount extends TargetAmount {
         for (UUID targetId : getTargets()) {
             Permanent permanent = game.getPermanent(targetId);
             if (permanent != null) {
-                sb.append(permanent.getLogName()).append('(').append(getTargetAmount(targetId)).append(") ");
+                sb.append(permanent.getLogName()).append(" (").append(getTargetAmount(targetId)).append(") ");
             } else {
                 Player player = game.getPlayer(targetId);
-                sb.append(player.getLogName()).append('(').append(getTargetAmount(targetId)).append(") ");
+                sb.append(player.getLogName()).append(" (").append(getTargetAmount(targetId)).append(") ");
             }
         }
-        return sb.toString();
+        return sb.toString().trim();
     }
 }
diff --git a/Mage/src/main/java/mage/target/common/TargetPermanentOrSuspendedCard.java b/Mage/src/main/java/mage/target/common/TargetPermanentOrSuspendedCard.java
index 1575dcc4ba..50e37b7300 100644
--- a/Mage/src/main/java/mage/target/common/TargetPermanentOrSuspendedCard.java
+++ b/Mage/src/main/java/mage/target/common/TargetPermanentOrSuspendedCard.java
@@ -29,9 +29,6 @@
  */
 package mage.target.common;
 
-import java.util.HashSet;
-import java.util.Set;
-import java.util.UUID;
 import mage.MageObject;
 import mage.abilities.Ability;
 import mage.cards.Card;
@@ -42,18 +39,21 @@ import mage.game.Game;
 import mage.game.permanent.Permanent;
 import mage.target.TargetImpl;
 
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
+
 /**
- *
  * @author emerald000
  */
 public class TargetPermanentOrSuspendedCard extends TargetImpl {
-    
+
     protected final FilterPermanentOrSuspendedCard filter;
-    
+
     public TargetPermanentOrSuspendedCard() {
         this(new FilterPermanentOrSuspendedCard(), false);
     }
-    
+
     public TargetPermanentOrSuspendedCard(FilterPermanentOrSuspendedCard filter, boolean notTarget) {
         super(notTarget);
         this.filter = filter;
@@ -62,17 +62,17 @@ public class TargetPermanentOrSuspendedCard extends TargetImpl {
         this.minNumberOfTargets = 1;
         this.maxNumberOfTargets = 1;
     }
-    
+
     public TargetPermanentOrSuspendedCard(final TargetPermanentOrSuspendedCard target) {
         super(target);
         this.filter = target.filter.copy();
     }
-    
+
     @Override
     public Filter<MageObject> getFilter() {
         return this.filter;
     }
-    
+
     @Override
     public TargetPermanentOrSuspendedCard copy() {
         return new TargetPermanentOrSuspendedCard(this);
@@ -166,6 +166,6 @@ public class TargetPermanentOrSuspendedCard extends TargetImpl {
                 }
             }
         }
-        return sb.toString();
+        return sb.toString().trim();
     }
 }
diff --git a/Mage/src/main/java/mage/target/common/TargetSpellOrPermanent.java b/Mage/src/main/java/mage/target/common/TargetSpellOrPermanent.java
index f2a846ba37..c8b7749875 100644
--- a/Mage/src/main/java/mage/target/common/TargetSpellOrPermanent.java
+++ b/Mage/src/main/java/mage/target/common/TargetSpellOrPermanent.java
@@ -29,9 +29,6 @@
  */
 package mage.target.common;
 
-import java.util.HashSet;
-import java.util.Set;
-import java.util.UUID;
 import mage.MageObject;
 import mage.abilities.Ability;
 import mage.constants.Zone;
@@ -45,8 +42,11 @@ import mage.game.stack.StackObject;
 import mage.target.TargetImpl;
 import mage.util.GameLog;
 
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
+
 /**
- *
  * @author LevelX
  */
 public class TargetSpellOrPermanent extends TargetImpl {
@@ -135,7 +135,7 @@ public class TargetSpellOrPermanent extends TargetImpl {
      * {@link mage.game.stack.Spell} that can be chosen. Should only be used for
      * Ability targets since this checks for protection, shroud etc.
      *
-     * @param sourceId - the target event source
+     * @param sourceId           - the target event source
      * @param sourceControllerId - controller of the target event source
      * @param game
      * @return - true if enough valid {@link mage.game.permanent.Permanent} or
@@ -257,7 +257,7 @@ public class TargetSpellOrPermanent extends TargetImpl {
                 }
             }
         }
-        return sb.toString();
+        return sb.toString().trim();
     }
 
     @Override

From 99a651876a10951eabdcb1fda516bb06c781c847 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Fri, 26 Jun 2020 12:22:35 +0200
Subject: [PATCH 522/586] * Fixed that Morph put face up ability was shown
 twice in the tooltip rule text (fixes  #6663).

---
 Mage.Common/src/main/java/mage/view/PermanentView.java | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/Mage.Common/src/main/java/mage/view/PermanentView.java b/Mage.Common/src/main/java/mage/view/PermanentView.java
index 840011f45c..312e1c9e8f 100644
--- a/Mage.Common/src/main/java/mage/view/PermanentView.java
+++ b/Mage.Common/src/main/java/mage/view/PermanentView.java
@@ -95,10 +95,9 @@ public class PermanentView extends CardView {
             if (controlled) {
                 // must be a morphed or manifested card
                 for (Ability permanentAbility : permanent.getAbilities()) {
-                    if (permanentAbility instanceof TurnFaceUpAbility && !permanentAbility.getRuleVisible()) {
-                        this.rules.add(permanentAbility.getRule(true));
-                    }
                     if (permanentAbility.getWorksFaceDown()) {
+                        this.rules.add(permanentAbility.getRule(true));
+                    } else if (permanentAbility instanceof TurnFaceUpAbility && !permanentAbility.getRuleVisible()) {
                         this.rules.add(permanentAbility.getRule());
                     }
                 }

From 6f3a6f1adbc9ac5241791c3f5a9342b146fecf67 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Fri, 26 Jun 2020 14:52:32 +0400
Subject: [PATCH 523/586] Test for #6720

---
 .../GainMenaceAbilityAsSingletonTest.java     | 59 +++++++++++++++++++
 1 file changed, 59 insertions(+)
 create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/GainMenaceAbilityAsSingletonTest.java

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/GainMenaceAbilityAsSingletonTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/GainMenaceAbilityAsSingletonTest.java
new file mode 100644
index 0000000000..473f9327a3
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/GainMenaceAbilityAsSingletonTest.java
@@ -0,0 +1,59 @@
+package org.mage.test.cards.abilities.other;
+
+import mage.abilities.keyword.MenaceAbility;
+import mage.constants.PhaseStep;
+import mage.constants.Zone;
+import mage.game.permanent.Permanent;
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestPlayerBase;
+
+/**
+ * @author JayDi85
+ */
+public class GainMenaceAbilityAsSingletonTest extends CardTestPlayerBase {
+
+    @Test
+    @Ignore // enable it after MenaceAbility will be singleton, see https://github.com/magefree/mage/issues/6720
+    public void test_SingletonNonMultiInstances() {
+        // bug: permanent shows multiple instances of one ability in card's text
+        // https://github.com/magefree/mage/issues/6720
+
+        // {3}{R}{R}
+        // Whenever Sethron, Hurloon General or another nontoken Minotaur enters the battlefield under your control, create a 2/3 red Minotaur creature token.
+        // {2}{B/R}: Minotaurs you control get +1/+0 and gain menace and haste until end of turn. ({B/R} can be paid with either {B} or {R}.)
+        addCard(Zone.HAND, playerA, "Sethron, Hurloon General", 7);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5 + 3 * 3);
+
+        // prepare minotaur token
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sethron, Hurloon General");
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+        checkPermanentCount("token", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Minotaur", 1);
+
+        // boost 1
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{B/R}");
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+        checkPT("boost 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Minotaur", 2 + 1, 3);
+
+        // boost 2
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{B/R}");
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+        checkPT("boost 2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Minotaur", 2 + 1 * 2, 3);
+
+        // boost 3
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{B/R}");
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+        checkPT("boost 3", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Minotaur", 2 + 1 * 3, 3);
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        Permanent permanent = getPermanent("Minotaur", playerA);
+        Assert.assertEquals("must have only 1 Menace instance", 1, permanent.getAbilities(currentGame).stream()
+                .filter(a -> a instanceof MenaceAbility).count());
+
+    }
+}

From e73a11cb1242fd7bb9299189ef8532c35ebc14f5 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Fri, 26 Jun 2020 09:49:20 -0400
Subject: [PATCH 524/586] fixed a small error with Inniaz, the Gale Force

---
 Mage.Sets/src/mage/cards/i/InniazTheGaleForce.java | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/i/InniazTheGaleForce.java b/Mage.Sets/src/mage/cards/i/InniazTheGaleForce.java
index 2b73d8c560..9d5d804cb0 100644
--- a/Mage.Sets/src/mage/cards/i/InniazTheGaleForce.java
+++ b/Mage.Sets/src/mage/cards/i/InniazTheGaleForce.java
@@ -84,7 +84,7 @@ class InniazTheGaleForceEffect extends OneShotEffect {
         private final FilterPermanent filter;
         private final TargetPermanent target;
 
-        private PlayerPair(Player leftPlayer, Player rightPlayer) {
+        private PlayerPair(Player rightPlayer, Player leftPlayer) {
             this.leftPlayer = leftPlayer;
             this.rightPlayer = rightPlayer;
             this.filter = this.makeFilter();
@@ -101,7 +101,7 @@ class InniazTheGaleForceEffect extends OneShotEffect {
         }
 
         private TargetPermanent makeTarget() {
-            return new TargetPermanent(1, this.filter);
+            return new TargetPermanent(1, 1, this.filter, true);
         }
 
         private void chooseTargets(Player controller, Game game, Ability source) {

From 329f7fd609e0a9532239ac1cacb71d66c860c31f Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Fri, 26 Jun 2020 18:58:13 +0400
Subject: [PATCH 525/586] Little improves for tests

---
 .../{abilitywords => keywords}/ConvergeTest.java     |  4 +---
 .../{abilitywords => keywords}/DomainTest.java       |  4 +---
 .../{abilitywords => keywords}/RevoltTest.java       |  4 +---
 .../serverside/base/impl/CardTestPlayerAPIImpl.java  |  5 +++--
 .../abilities/effects/ContinuousEffectsList.java     | 12 +++++++++---
 5 files changed, 15 insertions(+), 14 deletions(-)
 rename Mage.Tests/src/test/java/org/mage/test/cards/abilities/{abilitywords => keywords}/ConvergeTest.java (98%)
 rename Mage.Tests/src/test/java/org/mage/test/cards/abilities/{abilitywords => keywords}/DomainTest.java (96%)
 rename Mage.Tests/src/test/java/org/mage/test/cards/abilities/{abilitywords => keywords}/RevoltTest.java (97%)

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/abilitywords/ConvergeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ConvergeTest.java
similarity index 98%
rename from Mage.Tests/src/test/java/org/mage/test/cards/abilities/abilitywords/ConvergeTest.java
rename to Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ConvergeTest.java
index 5d79565a58..205da4bd24 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/abilitywords/ConvergeTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ConvergeTest.java
@@ -1,5 +1,4 @@
-
-package org.mage.test.cards.abilities.abilitywords;
+package org.mage.test.cards.abilities.keywords;
 
 import mage.constants.PhaseStep;
 import mage.constants.Zone;
@@ -7,7 +6,6 @@ import org.junit.Test;
 import org.mage.test.serverside.base.CardTestPlayerBase;
 
 /**
- *
  * @author LevelX2
  */
 public class ConvergeTest extends CardTestPlayerBase {
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/abilitywords/DomainTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DomainTest.java
similarity index 96%
rename from Mage.Tests/src/test/java/org/mage/test/cards/abilities/abilitywords/DomainTest.java
rename to Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DomainTest.java
index 5a79581708..81513456f3 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/abilitywords/DomainTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DomainTest.java
@@ -1,5 +1,4 @@
-
-package org.mage.test.cards.abilities.abilitywords;
+package org.mage.test.cards.abilities.keywords;
 
 import mage.constants.PhaseStep;
 import mage.constants.Zone;
@@ -7,7 +6,6 @@ import org.junit.Test;
 import org.mage.test.serverside.base.CardTestPlayerBase;
 
 /**
- *
  * @author LevelX2
  */
 public class DomainTest extends CardTestPlayerBase {
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/abilitywords/RevoltTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/RevoltTest.java
similarity index 97%
rename from Mage.Tests/src/test/java/org/mage/test/cards/abilities/abilitywords/RevoltTest.java
rename to Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/RevoltTest.java
index c25d6cad2d..151446d6c3 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/abilitywords/RevoltTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/RevoltTest.java
@@ -1,5 +1,4 @@
-
-package org.mage.test.cards.abilities.abilitywords;
+package org.mage.test.cards.abilities.keywords;
 
 import mage.constants.PhaseStep;
 import mage.constants.Zone;
@@ -7,7 +6,6 @@ import org.junit.Test;
 import org.mage.test.serverside.base.CardTestPlayerBase;
 
 /**
- *
  * @author LevelX2
  */
 public class RevoltTest extends CardTestPlayerBase {
diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
index 97940fff69..71ff178235 100644
--- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
+++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
@@ -51,7 +51,8 @@ import java.util.stream.Collectors;
  */
 public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implements CardTestAPI {
 
-    private static final boolean FAST_SCAN_WITHOUT_DATABASE_CREATE = false; // DEBUG only, enable it to fast startup tests without database create
+    // DEBUG only, enable it to fast startup tests without database create (delete \db\ folder to force db recreate)
+    private static final boolean FAST_SCAN_WITHOUT_DATABASE_CREATE = false;
 
     public static final String ALIAS_PREFIX = "@"; // don't change -- it uses in user's tests
     public static final String CHECK_PARAM_DELIMETER = "#";
@@ -137,7 +138,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     public CardTestPlayerAPIImpl() {
         // load all cards to db from class list
         ArrayList<String> errorsList = new ArrayList<>();
-        if (FAST_SCAN_WITHOUT_DATABASE_CREATE) {
+        if (FAST_SCAN_WITHOUT_DATABASE_CREATE && CardRepository.instance.findCard("Mountain") != null) {
             CardScanner.scanned = true;
         }
         CardScanner.scan(errorsList);
diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousEffectsList.java b/Mage/src/main/java/mage/abilities/effects/ContinuousEffectsList.java
index 6353df838a..c3e2cd3429 100644
--- a/Mage/src/main/java/mage/abilities/effects/ContinuousEffectsList.java
+++ b/Mage/src/main/java/mage/abilities/effects/ContinuousEffectsList.java
@@ -223,14 +223,20 @@ public class ContinuousEffectsList<T extends ContinuousEffect> extends ArrayList
         effectAbilityMap.clear();
     }
 
-    public boolean contains(Effect effect) {
+    @Override
+    public boolean contains(Object object) {
+        if (object == null || !(object instanceof ContinuousEffect)) {
+            return false;
+        }
+
         // search by id
+        ContinuousEffect need = (ContinuousEffect) object;
         for (Iterator<T> iterator = this.iterator(); iterator.hasNext(); ) {
             T test = iterator.next();
-            if (effect.getId().equals(test.getId())) {
+            if (need.equals(test)) {
                 return true;
             }
-            if (effect.equals(test)) {
+            if (need.getId().equals(test.getId())) {
                 return true;
             }
         }

From 166d898168f8622e1fed744e21b4e9f471d9985d Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Fri, 26 Jun 2020 17:41:10 +0200
Subject: [PATCH 526/586] * Added a test for #4659 - it's not reproducable
 (closes #4659).

---
 .../mage/test/rollback/DemonicPactTest.java   | 52 ++++++++++++++++++-
 .../delayed/PactDelayedTriggeredAbility.java  | 18 ++-----
 2 files changed, 56 insertions(+), 14 deletions(-)

diff --git a/Mage.Tests/src/test/java/org/mage/test/rollback/DemonicPactTest.java b/Mage.Tests/src/test/java/org/mage/test/rollback/DemonicPactTest.java
index 06e5b4791e..c741768a7a 100644
--- a/Mage.Tests/src/test/java/org/mage/test/rollback/DemonicPactTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/rollback/DemonicPactTest.java
@@ -1,8 +1,8 @@
-
 package org.mage.test.rollback;
 
 import mage.constants.PhaseStep;
 import mage.constants.Zone;
+import org.junit.Assert;
 import org.junit.Test;
 import org.mage.test.serverside.base.CardTestPlayerBase;
 
@@ -74,4 +74,54 @@ public class DemonicPactTest extends CardTestPlayerBase {
         assertLife(playerB, 16);
 
     }
+
+    /**
+     * Rollback problem with Pact of Negation etc [cards] #4659
+     *
+     * Potential bug here for [Pact of Negation] and similar cards with 0 cost
+     * and demand to pay or lose the next turn.
+     *
+     * So can you check the following scenario where I think the game is buggy:
+     * an opponent casts pact of negation on my turn, his next turn he requests
+     * a rollback to beginning of his turn -- bingo I'm a winner and he loses
+     * the game. The log says I'm the winner and the opponent lost and that is
+     * immediately after rollback request.
+     */
+    
+    @Test
+    public void testPactOfNegationRollback() {
+        addCard(Zone.HAND, playerA, "Silvercoat Lion", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
+
+
+        addCard(Zone.BATTLEFIELD, playerB, "Island", 5);
+        // Counter target spell.
+        // At the beginning of your next upkeep, pay {3}{U}{U}. If you don't, you lose the game.
+        addCard(Zone.HAND, playerB, "Pact of Negation"); // Instant {0}
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Silvercoat Lion");
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Pact of Negation", "Silvercoat Lion", "Silvercoat Lion");
+
+        setChoice(playerB, "Yes");
+
+        rollbackTurns(2, PhaseStep.PRECOMBAT_MAIN, playerB, 0);
+
+        setStrictChooseMode(true);
+        setStopAt(2, PhaseStep.BEGIN_COMBAT);
+        execute();
+        
+        assertAllCommandsUsed();
+
+
+        assertGraveyardCount(playerA, "Silvercoat Lion", 1);
+        assertGraveyardCount(playerB, "Pact of Negation", 1);
+        
+        Assert.assertTrue("Player A is still in game", playerA.isInGame());
+        Assert.assertTrue("Player B is still in game", playerB.isInGame());
+        
+        assertTappedCount("Island", true,  5); 
+
+
+    }
+    
 }
diff --git a/Mage/src/main/java/mage/abilities/common/delayed/PactDelayedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/delayed/PactDelayedTriggeredAbility.java
index 1bb565531a..5329f3f438 100644
--- a/Mage/src/main/java/mage/abilities/common/delayed/PactDelayedTriggeredAbility.java
+++ b/Mage/src/main/java/mage/abilities/common/delayed/PactDelayedTriggeredAbility.java
@@ -1,4 +1,3 @@
-
 package mage.abilities.common.delayed;
 
 import mage.constants.Outcome;
@@ -19,7 +18,6 @@ public class PactDelayedTriggeredAbility extends DelayedTriggeredAbility {
         super(new PactEffect(cost));
     }
 
-
     public PactDelayedTriggeredAbility(PactDelayedTriggeredAbility ability) {
         super(ability);
     }
@@ -39,8 +37,6 @@ public class PactDelayedTriggeredAbility extends DelayedTriggeredAbility {
         return game.isActivePlayer(this.getControllerId());
     }
 
-    
-
     @Override
     public String getRule() {
         return "At the beginning of your next upkeep " + modes.getText();
@@ -49,8 +45,7 @@ public class PactDelayedTriggeredAbility extends DelayedTriggeredAbility {
 
 class PactEffect extends OneShotEffect {
 
-    private ManaCosts cost;
-
+    private final ManaCosts cost;
 
     public PactEffect(ManaCosts cost) {
         super(Outcome.Neutral);
@@ -60,7 +55,7 @@ class PactEffect extends OneShotEffect {
 
     public PactEffect(final PactEffect effect) {
         super(effect);
-        this.cost = effect.cost;
+        this.cost = effect.cost.copy();
     }
 
     @Override
@@ -71,10 +66,10 @@ class PactEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player player = game.getPlayer(source.getControllerId());
-        if (player != null) { 
-            if (player.chooseUse(Outcome.Benefit, "Pay " + cost.getText()  + '?', source, game)) {
+        if (player != null) {
+            if (player.chooseUse(Outcome.Benefit, "Pay " + cost.getText() + '?', source, game)) {
                 cost.clearPaid();
-                if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), false, null)){
+                if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), false, null)) {
                     return true;
                 }
             }
@@ -83,7 +78,4 @@ class PactEffect extends OneShotEffect {
         }
         return false;
     }
-
-    
-
 }

From 029f47ec32447efda1f235a2b11d52a23064e187 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Fri, 26 Jun 2020 17:50:02 +0200
Subject: [PATCH 527/586] * Phage the Untouchable bug is not reproducable
 (closes #3693).

---
 .../org/mage/test/game/ends/PhageTheUntouchableTest.java  | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/Mage.Tests/src/test/java/org/mage/test/game/ends/PhageTheUntouchableTest.java b/Mage.Tests/src/test/java/org/mage/test/game/ends/PhageTheUntouchableTest.java
index 8679e5c112..e56534cb3e 100644
--- a/Mage.Tests/src/test/java/org/mage/test/game/ends/PhageTheUntouchableTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/game/ends/PhageTheUntouchableTest.java
@@ -10,7 +10,7 @@ import org.mage.test.serverside.base.CardTestPlayerBase;
 
 /**
  *
- * @author LeveöX
+ * @author LevelX2
  */
 public class PhageTheUntouchableTest extends CardTestPlayerBase {
 
@@ -32,10 +32,16 @@ public class PhageTheUntouchableTest extends CardTestPlayerBase {
 
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Phage the Untouchable");
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Hero's Downfall", "Phage the Untouchable");
+        addTarget(playerA, playerB);
 
         setStopAt(2, PhaseStep.BEGIN_COMBAT);
+        
+        setStrictChooseMode(true);
+        
         execute();
 
+        assertAllCommandsUsed();
+        
         assertLife(playerA, 20);
         assertLife(playerB, 20);
 

From 2f5a4e0cafa3384587e4a13f0ead4e50f4ffb849 Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Fri, 26 Jun 2020 11:10:54 -0500
Subject: [PATCH 528/586] - Fixed text related to bug 6675

---
 Mage.Sets/src/mage/cards/b/BlessedSanctuary.java       |  4 ++--
 Mage.Sets/src/mage/cards/i/InniazTheGaleForce.java     | 10 ++++++----
 Mage.Sets/src/mage/cards/o/OrmosArchiveKeeper.java     |  6 +++---
 .../common/PreventAllNonCombatDamageToAllEffect.java   |  4 +---
 4 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/b/BlessedSanctuary.java b/Mage.Sets/src/mage/cards/b/BlessedSanctuary.java
index 8b0099dc4d..4cf9a6409d 100644
--- a/Mage.Sets/src/mage/cards/b/BlessedSanctuary.java
+++ b/Mage.Sets/src/mage/cards/b/BlessedSanctuary.java
@@ -20,14 +20,14 @@ import java.util.UUID;
 public class BlessedSanctuary extends CardImpl {
 
     private static final FilterPermanent filterYourCreatures = new FilterControlledCreaturePermanent("creatures you control");
-    private static final FilterControlledCreaturePermanent filterNontoken = new FilterControlledCreaturePermanent("nontoken creature");
+    private static final FilterControlledCreaturePermanent filterNontoken = new FilterControlledCreaturePermanent("a nontoken creature");
 
     static {
         filterNontoken.add(Predicates.not(TokenPredicate.instance));
     }
 
     public BlessedSanctuary(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{W}{W}");
+        super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{W}{W}");
 
         //Prevent all noncombat damage that would be dealt to you and creatures you control.
         this.addAbility(new SimpleStaticAbility(new PreventAllNonCombatDamageToAllEffect(
diff --git a/Mage.Sets/src/mage/cards/i/InniazTheGaleForce.java b/Mage.Sets/src/mage/cards/i/InniazTheGaleForce.java
index 9d5d804cb0..6e917cd794 100644
--- a/Mage.Sets/src/mage/cards/i/InniazTheGaleForce.java
+++ b/Mage.Sets/src/mage/cards/i/InniazTheGaleForce.java
@@ -34,9 +34,9 @@ import java.util.UUID;
 public final class InniazTheGaleForce extends CardImpl {
 
     private static final FilterCreaturePermanent filter
-            = new FilterCreaturePermanent("attacking creatures with flying");
+            = new FilterCreaturePermanent("Attacking creatures with flying");
     private static final FilterCreaturePermanent filter2
-            = new FilterCreaturePermanent("creatures you control with flying");
+            = new FilterCreaturePermanent("three or more creatures you control with flying");
 
     static {
         filter.add(AttackingPredicate.instance);
@@ -60,7 +60,8 @@ public final class InniazTheGaleForce extends CardImpl {
                 1, 1, Duration.EndOfTurn, filter, false
         ), new ManaCostsImpl("{2}{W/U}")));
 
-        // Whenever three or more creatures you control with flying attack, each player gains control of a nonland permanent of your choice controlled by the player to their right.
+        // Whenever three or more creatures you control with flying attack, each player gains control 
+        // of a nonland permanent of your choice controlled by the player to their right.
         this.addAbility(new AttacksWithCreaturesTriggeredAbility(
                 new InniazTheGaleForceEffect(), 3, filter2
         ));
@@ -79,6 +80,7 @@ public final class InniazTheGaleForce extends CardImpl {
 class InniazTheGaleForceEffect extends OneShotEffect {
 
     private static final class PlayerPair {
+
         private final Player leftPlayer;
         private final Player rightPlayer;
         private final FilterPermanent filter;
@@ -94,7 +96,7 @@ class InniazTheGaleForceEffect extends OneShotEffect {
         private FilterPermanent makeFilter() {
             FilterPermanent filter = new FilterNonlandPermanent(
                     "nonland permanent controlled by " + rightPlayer.getName()
-                            + " to give to " + leftPlayer.getName()
+                    + " to give to " + leftPlayer.getName()
             );
             filter.add(new ControllerIdPredicate(rightPlayer.getId()));
             return filter;
diff --git a/Mage.Sets/src/mage/cards/o/OrmosArchiveKeeper.java b/Mage.Sets/src/mage/cards/o/OrmosArchiveKeeper.java
index 23d40fbc68..e1a74d19ea 100644
--- a/Mage.Sets/src/mage/cards/o/OrmosArchiveKeeper.java
+++ b/Mage.Sets/src/mage/cards/o/OrmosArchiveKeeper.java
@@ -65,8 +65,8 @@ class OrmosArchiveKeeperEffect extends ReplacementEffectImpl {
 
     OrmosArchiveKeeperEffect() {
         super(Duration.WhileOnBattlefield, Outcome.Benefit);
-        staticText = "If you would draw a card while your library has no cards in it, " +
-                "instead put five +1/+1 counters on {this}";
+        staticText = "If you would draw a card while your library has no cards in it, "
+                + "instead put five +1/+1 counters on {this}";
     }
 
     private OrmosArchiveKeeperEffect(final OrmosArchiveKeeperEffect effect) {
@@ -111,7 +111,7 @@ class OrmosArchiveKeeperEffect extends ReplacementEffectImpl {
 
 class OrmosArchiveKeeperTarget extends TargetCardInHand {
 
-    private static final FilterCard filter = new FilterCard("3 cards with different names");
+    private static final FilterCard filter = new FilterCard("three cards with different names");
 
     OrmosArchiveKeeperTarget() {
         super(3, filter);
diff --git a/Mage/src/main/java/mage/abilities/effects/common/PreventAllNonCombatDamageToAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PreventAllNonCombatDamageToAllEffect.java
index 49aee45b7f..8edf712f0c 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/PreventAllNonCombatDamageToAllEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/PreventAllNonCombatDamageToAllEffect.java
@@ -1,5 +1,3 @@
-
-
 package mage.abilities.effects.common;
 
 import mage.abilities.Ability;
@@ -27,7 +25,7 @@ public class PreventAllNonCombatDamageToAllEffect extends PreventionEffectImpl {
         super(duration, Integer.MAX_VALUE, false);
         this.filter = filter;
         this.andToYou = andToYou;
-        staticText = "Prevent all non combat damage that would be dealt to " + (andToYou ? "you and " : "") + filter.getMessage();
+        staticText = "Prevent all noncombat damage that would be dealt to " + (andToYou ? "you and " : "") + filter.getMessage();
 
         if (duration != Duration.WhileOnBattlefield) {
             staticText += ' ' + duration.toString();

From 0cd5ab12f76f788b48691f2b609c2107795bf734 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Fri, 26 Jun 2020 18:16:11 +0200
Subject: [PATCH 529/586] * Mycosynth Lattice - Added hint text for the use of
 floating mana (closes #1544).

---
 Mage.Sets/src/mage/cards/m/MycosynthLattice.java | 5 ++++-
 Mage.Sets/src/mage/cards/s/SunderingStroke.java  | 2 +-
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/m/MycosynthLattice.java b/Mage.Sets/src/mage/cards/m/MycosynthLattice.java
index 61ac8b0fae..7053ad1eab 100644
--- a/Mage.Sets/src/mage/cards/m/MycosynthLattice.java
+++ b/Mage.Sets/src/mage/cards/m/MycosynthLattice.java
@@ -8,6 +8,7 @@ import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.AsThoughEffectImpl;
 import mage.abilities.effects.AsThoughManaEffect;
 import mage.abilities.effects.ContinuousEffectImpl;
+import mage.abilities.hint.StaticHint;
 import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -34,7 +35,9 @@ public final class MycosynthLattice extends CardImpl {
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new EverythingIsColorlessEffect()));
 
         // Players may spend mana as though it were mana of any color.
-        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ManaCanBeSpentAsAnyColorEffect()));
+        Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new ManaCanBeSpentAsAnyColorEffect());
+        ability.addHint(new StaticHint("(XMage hint: You can use floating mana by clicking on the related symbol of the needed mana type in your mana pool player area.)"));
+        this.addAbility(ability);
     }
 
     public MycosynthLattice(final MycosynthLattice card) {
diff --git a/Mage.Sets/src/mage/cards/s/SunderingStroke.java b/Mage.Sets/src/mage/cards/s/SunderingStroke.java
index fb3feb97f9..da329bc299 100644
--- a/Mage.Sets/src/mage/cards/s/SunderingStroke.java
+++ b/Mage.Sets/src/mage/cards/s/SunderingStroke.java
@@ -32,7 +32,7 @@ public final class SunderingStroke extends CardImpl {
         ));
         this.getSpellAbility().addTarget(new TargetAnyTargetAmount(7, 3));
         this.getSpellAbility().addHint(new StaticHint(
-                "(You have to choose how 7 damage is divided even if you spend seven red mana)"
+                "(You have to choose how 7 damage is divided even if you spend seven red mana.)"
         ));
     }
 

From 8e5b72b8c7dc0dc28c810caa5acbc737eeed3247 Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Fri, 26 Jun 2020 14:39:24 -0500
Subject: [PATCH 530/586] - Fixed text for Inniaz, the Gale Force.

---
 Mage.Sets/src/mage/cards/i/InniazTheGaleForce.java | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/i/InniazTheGaleForce.java b/Mage.Sets/src/mage/cards/i/InniazTheGaleForce.java
index 6e917cd794..dcd62665a2 100644
--- a/Mage.Sets/src/mage/cards/i/InniazTheGaleForce.java
+++ b/Mage.Sets/src/mage/cards/i/InniazTheGaleForce.java
@@ -23,7 +23,6 @@ import mage.players.Player;
 import mage.players.PlayerList;
 import mage.target.TargetPermanent;
 import mage.target.targetpointer.FixedTarget;
-
 import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
@@ -36,7 +35,7 @@ public final class InniazTheGaleForce extends CardImpl {
     private static final FilterCreaturePermanent filter
             = new FilterCreaturePermanent("Attacking creatures with flying");
     private static final FilterCreaturePermanent filter2
-            = new FilterCreaturePermanent("three or more creatures you control with flying");
+            = new FilterCreaturePermanent("creatures you control with flying");
 
     static {
         filter.add(AttackingPredicate.instance);

From d02d8a4dfed61d35deb483d9c41d615bbb9cd041 Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Fri, 26 Jun 2020 16:51:17 -0500
Subject: [PATCH 531/586] - Fixed #6732

---
 ...omesBlockedByCreatureTriggeredAbility.java |  4 +++-
 .../effects/common/ExileTargetEffect.java     | 23 ++++++++++++++-----
 .../common/ExileUntilSourceLeavesEffect.java  |  6 ++++-
 3 files changed, 25 insertions(+), 8 deletions(-)

diff --git a/Mage/src/main/java/mage/abilities/common/BecomesBlockedByCreatureTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesBlockedByCreatureTriggeredAbility.java
index bc4d9626b7..0045bb8292 100644
--- a/Mage/src/main/java/mage/abilities/common/BecomesBlockedByCreatureTriggeredAbility.java
+++ b/Mage/src/main/java/mage/abilities/common/BecomesBlockedByCreatureTriggeredAbility.java
@@ -45,7 +45,9 @@ public class BecomesBlockedByCreatureTriggeredAbility extends TriggeredAbilityIm
         if (!filter.match(blocker, game)) {
             return false;
         }
-        this.getEffects().setTargetPointer(new FixedTarget(blocker, game));
+        for (Effect effect : this.getEffects()) {
+            effect.setTargetPointer(new FixedTarget(event.getSourceId()));
+        }
         return true;
     }
 
diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileTargetEffect.java
index 7978af7f73..f7dc493b35 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/ExileTargetEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/ExileTargetEffect.java
@@ -43,7 +43,8 @@ public class ExileTargetEffect extends OneShotEffect {
     }
 
     /**
-     * Exile cards to normal exile window (but it can exile to source's exile window after toSourceExileZone change)
+     * Exile cards to normal exile window (but it can exile to source's exile
+     * window after toSourceExileZone change)
      */
     public ExileTargetEffect() {
         this(null, "");
@@ -91,21 +92,27 @@ public class ExileTargetEffect extends OneShotEffect {
             Set<Card> toExile = new LinkedHashSet<>();
             if (multitargetHandling
                     && targetPointer instanceof FirstTargetPointer
-                    && (source.getTargets().size() > 1 || (source.getTargets().size() > 0 && source.getTargets().get(0).getTargets().size() > 1))) {
+                    && (source.getTargets().size() > 1
+                    || (source.getTargets().size() > 0
+                    && source.getTargets().get(0).getTargets().size() > 1))) {
                 for (Target target : source.getTargets()) {
                     for (UUID targetId : target.getTargets()) {
                         Permanent permanent = game.getPermanent(targetId);
                         if (permanent != null
                                 && permanent.isPhasedIn()) {
                             Zone currentZone = game.getState().getZone(permanent.getId());
-                            if (currentZone != Zone.EXILED && (onlyFromZone == null || onlyFromZone == Zone.BATTLEFIELD)) {
+                            if (currentZone != Zone.EXILED
+                                    && (onlyFromZone == null
+                                    || onlyFromZone == Zone.BATTLEFIELD)) {
                                 toExile.add(permanent);
                             }
                         } else {
                             Card card = game.getCard(targetId);
                             if (card != null) {
                                 Zone currentZone = game.getState().getZone(card.getId());
-                                if (currentZone != Zone.EXILED && (onlyFromZone == null || onlyFromZone == currentZone)) {
+                                if (currentZone != Zone.EXILED
+                                        && (onlyFromZone == null
+                                        || onlyFromZone == currentZone)) {
                                     toExile.add(card);
                                 }
                             } else {
@@ -123,14 +130,18 @@ public class ExileTargetEffect extends OneShotEffect {
                     if (permanent != null
                             && permanent.isPhasedIn()) {
                         Zone currentZone = game.getState().getZone(permanent.getId());
-                        if (currentZone != Zone.EXILED && (onlyFromZone == null || onlyFromZone == Zone.BATTLEFIELD)) {
+                        if (currentZone != Zone.EXILED
+                                && (onlyFromZone == null
+                                || onlyFromZone == Zone.BATTLEFIELD)) {
                             toExile.add(permanent);
                         }
                     } else {
                         Card card = game.getCard(targetId);
                         if (card != null) {
                             Zone currentZone = game.getState().getZone(card.getId());
-                            if (currentZone != Zone.EXILED && (onlyFromZone == null || onlyFromZone == currentZone)) {
+                            if (currentZone != Zone.EXILED
+                                    && (onlyFromZone == null
+                                    || onlyFromZone == currentZone)) {
                                 toExile.add(card);
                             }
                         } else {
diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileUntilSourceLeavesEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileUntilSourceLeavesEffect.java
index bd54104237..433a7c97cf 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/ExileUntilSourceLeavesEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/ExileUntilSourceLeavesEffect.java
@@ -36,7 +36,11 @@ public class ExileUntilSourceLeavesEffect extends OneShotEffect {
     public boolean apply(Game game, Ability source) {
         Permanent permanent = game.getPermanent(source.getSourceId());
         if (permanent != null) {
-            return new ExileTargetEffect(CardUtil.getCardExileZoneId(game, source), permanent.getIdName()).apply(game, source);
+            ExileTargetEffect effect = new ExileTargetEffect(CardUtil.getCardExileZoneId(game, source), permanent.getIdName());
+            if (targetPointer != null) {  // Grasping Giant
+                effect.setTargetPointer(targetPointer);
+            }
+            return effect.apply(game, source);
         }
         return false;
     }

From 8906f3be7b9fc27990c9b6ecc545cf2c057b0825 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Sat, 27 Jun 2020 01:01:55 +0200
Subject: [PATCH 532/586] * Cloning a face-down creature should produce a plain
 2/2 creature (fixes #3582).

---
 .../org/mage/test/cards/copy/CloneTest.java   | 58 +++++++++++++++++++
 Mage/src/main/java/mage/game/GameImpl.java    |  5 +-
 2 files changed, 61 insertions(+), 2 deletions(-)

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/CloneTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/CloneTest.java
index 0d4a005c3c..d38e19f92a 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/CloneTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/CloneTest.java
@@ -3,9 +3,11 @@ package org.mage.test.cards.copy;
 import mage.abilities.effects.ContinuousEffect;
 import mage.abilities.effects.ContinuousEffectsList;
 import mage.cards.Card;
+import mage.constants.EmptyNames;
 import mage.constants.PhaseStep;
 import mage.constants.SubType;
 import mage.constants.Zone;
+import mage.filter.Filter;
 import mage.filter.FilterCard;
 import mage.filter.StaticFilters;
 import mage.filter.predicate.mageobject.NamePredicate;
@@ -231,4 +233,60 @@ public class CloneTest extends CardTestPlayerBase {
         Assert.assertTrue("The cloned Adaptive Automaton should be a Goblin", clone.hasSubtype(SubType.GOBLIN, currentGame));
     }
 
+    /**
+     * Cloning a face-down creature should produce a plain 2/2 creature #3582
+     *
+     * What I expected: Using a clone effect on a face-down creature should
+     * create another 2/2 creature.
+     *
+     * What I observed: The resulting token appears face-up as a copy of the
+     * face-down creature's front face, copying its power/toughness but not
+     * abilities.
+     *
+     * To reproduce: Play a Terastodon, then an Ixidron. Terastodon is turned
+     * face-down. Then play a clone creature (such as Progenitor Mimic). It
+     * appears as a face-up 9/9 Terastodon token, but without any ETB triggers.
+     * The next turn, when it copies itself, it creates a Terastodon that does
+     * have its ETB ability.
+     *
+     * https://blogs.magicjudges.org/articles/2014/09/16/morph-rules-problems-face-down-in-a-face-up-world/
+     * explains that cloning a face-down 2/2 creature should create another 2/2
+     * creature with no abilities (see Face-down Creatures and Clone).
+     */
+
+    @Test
+    public void testCloningFaceDownCreature() {
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 5);
+        // As Ixidron enters the battlefield, turn all other nontoken creatures face down.
+        // Ixidron's power and toughness are each equal to the number of face-down creatures on the battlefield.        
+        addCard(Zone.HAND, playerA, "Ixidron"); // Creature {3}{U}{U}
+
+        // When Terastodon enters the battlefield, you may destroy up to three target noncreature permanents. 
+        // For each permanent put into a graveyard this way, its controller creates a 3/3 green Elephant creature token.
+        addCard(Zone.BATTLEFIELD, playerA, "Terastodon", 1); // Creature 9/9  {6}{G}{G}
+
+        // You may have Clone enter the battlefield as a copy of any creature on the battlefield.        
+        addCard(Zone.HAND, playerA, "Clone"); // Creature {3}{U}
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ixidron");
+
+        castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Clone");
+        setChoice(playerA, "Yes");
+        setChoice(playerA, EmptyNames.FACE_DOWN_CREATURE.toString());
+
+        setStrictChooseMode(true);
+        setStopAt(3, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        
+        assertPermanentCount(playerA, "Terastodon", 0);
+        
+        assertPermanentCount(playerA, "Ixidron", 1);
+        assertPowerToughness(playerA, "Ixidron", 1, 1);
+
+        assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2);
+        assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 2, Filter.ComparisonScope.All);
+    }
+    
 }
diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java
index 435772ef44..673c2f056f 100644
--- a/Mage/src/main/java/mage/game/GameImpl.java
+++ b/Mage/src/main/java/mage/game/GameImpl.java
@@ -562,7 +562,7 @@ public abstract class GameImpl implements Game, Serializable {
 
     @Override
     public void saveState(boolean bookmark) {
-        if (!simulation && gameStates != null) {
+        if (!simulation && gameStates != null) {            
             if (bookmark || saveGame) {
                 gameStates.save(state);
             }
@@ -1642,7 +1642,8 @@ public abstract class GameImpl implements Game, Serializable {
             newBluePrint.reset(this);
 
             //getState().addCard(permanent);
-            if (copyFromPermanent.isMorphed() || copyFromPermanent.isManifested()) {
+            if (copyFromPermanent.isMorphed() || copyFromPermanent.isManifested() 
+                    || copyFromPermanent.isFaceDown(this)) {
                 MorphAbility.setPermanentToFaceDownCreature(newBluePrint);
             }
             newBluePrint.assignNewId();

From 6fa1ad3aaaf6ef5eeea6b8be0e0262467a2cc154 Mon Sep 17 00:00:00 2001
From: htrajan <htrajan2002@yahoo.com>
Date: Fri, 26 Jun 2020 17:55:43 -0700
Subject: [PATCH 533/586] [M21] Implement more cards (#6730)

* EnthrallingHold

* ArchfiendsVessel

* ConspicuousSnoop

* HoodedBlightfang

* commit set updates

* fix text

* fix Archfiend's Vessel cast from graveyard trigger and Enthralling Hold text
---
 .../src/mage/cards/a/ArchfiendsVessel.java    | 131 ++++++++++++++++++
 .../src/mage/cards/c/ConspicuousSnoop.java    |  54 ++++++++
 .../src/mage/cards/e/EnthrallingHold.java     |  64 +++++++++
 .../src/mage/cards/h/HoodedBlightfang.java    |  58 ++++++++
 Mage.Sets/src/mage/cards/s/SkillBorrower.java |  67 ++-------
 Mage.Sets/src/mage/sets/CoreSet2021.java      |   4 +
 ...cksCreatureYouControlTriggeredAbility.java |   4 +-
 ...aneswalkerWhenDamagedTriggeredAbility.java |  62 +++++++++
 ...GainActivatedAbilitiesOfTopCardEffect.java |  56 ++++++++
 .../game/permanent/token/AssassinToken2.java  |  47 +------
 .../watchers/common/SpellsCastWatcher.java    |  22 ++-
 11 files changed, 465 insertions(+), 104 deletions(-)
 create mode 100644 Mage.Sets/src/mage/cards/a/ArchfiendsVessel.java
 create mode 100644 Mage.Sets/src/mage/cards/c/ConspicuousSnoop.java
 create mode 100644 Mage.Sets/src/mage/cards/e/EnthrallingHold.java
 create mode 100644 Mage.Sets/src/mage/cards/h/HoodedBlightfang.java
 create mode 100644 Mage/src/main/java/mage/abilities/common/DestroyPlaneswalkerWhenDamagedTriggeredAbility.java
 create mode 100644 Mage/src/main/java/mage/abilities/effects/common/GainActivatedAbilitiesOfTopCardEffect.java

diff --git a/Mage.Sets/src/mage/cards/a/ArchfiendsVessel.java b/Mage.Sets/src/mage/cards/a/ArchfiendsVessel.java
new file mode 100644
index 0000000000..95d0680393
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/a/ArchfiendsVessel.java
@@ -0,0 +1,131 @@
+package mage.cards.a;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.keyword.LifelinkAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Outcome;
+import mage.constants.SubType;
+import mage.constants.Zone;
+import mage.game.Game;
+import mage.game.events.EntersTheBattlefieldEvent;
+import mage.game.events.GameEvent;
+import mage.game.permanent.Permanent;
+import mage.game.permanent.token.DemonToken;
+import mage.game.permanent.token.Token;
+import mage.game.stack.Spell;
+import mage.players.Player;
+import mage.watchers.common.SpellsCastWatcher;
+
+import java.util.List;
+import java.util.UUID;
+
+/**
+ *
+ * @author htrajan
+ */
+public final class ArchfiendsVessel extends CardImpl {
+
+    public ArchfiendsVessel(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}");
+        
+        this.subtype.add(SubType.HUMAN);
+        this.subtype.add(SubType.CLERIC);
+        this.power = new MageInt(1);
+        this.toughness = new MageInt(1);
+
+        // Lifelink
+        this.addAbility(LifelinkAbility.getInstance());
+
+        // When Archfiend's Vessel enters the battlefield, if it entered from your graveyard or you cast it from your graveyard, exile it. If you do, create a 5/5 black Demon creature token with flying.
+        this.addAbility(new ArchfiendsVesselAbility(), new SpellsCastWatcher());
+    }
+
+    private ArchfiendsVessel(final ArchfiendsVessel card) {
+        super(card);
+    }
+
+    @Override
+    public ArchfiendsVessel copy() {
+        return new ArchfiendsVessel(this);
+    }
+}
+
+class ArchfiendsVesselAbility extends EntersBattlefieldTriggeredAbility {
+
+    ArchfiendsVesselAbility() {
+        super(new ArchfiendsVesselEffect());
+    }
+
+    private ArchfiendsVesselAbility(ArchfiendsVesselAbility ability) {
+        super(ability);
+    }
+
+    @Override
+    public boolean checkEventType(GameEvent event, Game game) {
+        return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD;
+    }
+
+    @Override
+    public boolean checkTrigger(GameEvent event, Game game) {
+        if (super.checkTrigger(event, game)) {
+            if (event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD) {
+                EntersTheBattlefieldEvent entersTheBattlefieldEvent = (EntersTheBattlefieldEvent) event;
+                if (entersTheBattlefieldEvent.getTargetId().equals(getSourceId()) && entersTheBattlefieldEvent.getFromZone() == Zone.GRAVEYARD) {
+                    return true;
+                } else {
+                    SpellsCastWatcher watcher = game.getState().getWatcher(SpellsCastWatcher.class);
+                    List<Spell> spellsCastFromGraveyard = watcher.getSpellsCastFromGraveyardThisTurn(getControllerId());
+                    if (spellsCastFromGraveyard != null) {
+                        return spellsCastFromGraveyard.stream()
+                            .anyMatch(spell -> spell.getMainCard().getId().equals((entersTheBattlefieldEvent.getTarget().getMainCard().getId())));
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public ArchfiendsVesselAbility copy() {
+        return new ArchfiendsVesselAbility(this);
+    }
+}
+
+class ArchfiendsVesselEffect extends OneShotEffect {
+
+    ArchfiendsVesselEffect() {
+        super(Outcome.Benefit);
+        staticText = "if it entered from your graveyard or you cast it from your graveyard, exile it. If you do, create a 5/5 black Demon creature token with flying";
+    }
+
+    private ArchfiendsVesselEffect(ArchfiendsVesselEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Permanent archfiendsVessel = source.getSourcePermanentIfItStillExists(game);
+        if (archfiendsVessel != null) {
+            Player controller = game.getPlayer(source.getControllerId());
+            if (controller != null) {
+                boolean moved = controller.moveCards(archfiendsVessel.getMainCard(), Zone.EXILED, source, game);
+                if (moved) {
+                    Token token = new DemonToken();
+                    token.putOntoBattlefield(1, game, source.getSourceId(), controller.getId());
+                }
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public ArchfiendsVesselEffect copy() {
+        return new ArchfiendsVesselEffect(this);
+    }
+}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/c/ConspicuousSnoop.java b/Mage.Sets/src/mage/cards/c/ConspicuousSnoop.java
new file mode 100644
index 0000000000..1edb15357c
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/c/ConspicuousSnoop.java
@@ -0,0 +1,54 @@
+package mage.cards.c;
+
+import mage.MageInt;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.effects.common.GainActivatedAbilitiesOfTopCardEffect;
+import mage.abilities.effects.common.continuous.PlayTheTopCardEffect;
+import mage.abilities.effects.common.continuous.PlayWithTheTopCardRevealedEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.filter.FilterCard;
+
+import java.util.UUID;
+
+/**
+ *
+ * @author htrajan
+ */
+public final class ConspicuousSnoop extends CardImpl {
+
+    private static final FilterCard filter = new FilterCard("cast Goblin spells");
+
+    static {
+        filter.add(SubType.GOBLIN.getPredicate());
+    }
+
+    public ConspicuousSnoop(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}{R}");
+        
+        this.subtype.add(SubType.GOBLIN);
+        this.subtype.add(SubType.ROGUE);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(2);
+
+        // Play with the top card of your library revealed.
+        this.addAbility(new SimpleStaticAbility(new PlayWithTheTopCardRevealedEffect()));
+
+        // You may cast Goblin spells from the top of your library.
+        this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter)));
+
+        // As long as the top card of your library is a Goblin card, Conspicuous Snoop has all activated abilities of that card.
+        this.addAbility(new SimpleStaticAbility(new GainActivatedAbilitiesOfTopCardEffect(filter.copy().withMessage("a Goblin card"))));
+    }
+
+    private ConspicuousSnoop(final ConspicuousSnoop card) {
+        super(card);
+    }
+
+    @Override
+    public ConspicuousSnoop copy() {
+        return new ConspicuousSnoop(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/cards/e/EnthrallingHold.java b/Mage.Sets/src/mage/cards/e/EnthrallingHold.java
new file mode 100644
index 0000000000..9060709592
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/e/EnthrallingHold.java
@@ -0,0 +1,64 @@
+package mage.cards.e;
+
+import mage.abilities.Ability;
+import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.effects.Effect;
+import mage.abilities.effects.common.AttachEffect;
+import mage.abilities.effects.common.continuous.ControlEnchantedEffect;
+import mage.abilities.keyword.EnchantAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Outcome;
+import mage.constants.SubType;
+import mage.constants.Zone;
+import mage.filter.FilterPermanent;
+import mage.filter.common.FilterCreaturePermanent;
+import mage.filter.predicate.permanent.TappedPredicate;
+import mage.target.TargetPermanent;
+import mage.target.common.TargetCreaturePermanent;
+
+import java.util.UUID;
+
+/**
+ *
+ * @author htrajan
+ */
+public final class EnthrallingHold extends CardImpl {
+
+    private static final FilterPermanent filter = new FilterCreaturePermanent();
+
+    static {
+        filter.add(TappedPredicate.instance);
+    }
+
+    public EnthrallingHold(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}{U}");
+        
+        this.subtype.add(SubType.AURA);
+
+        // Enchant creature
+        TargetPermanent auraTarget = new TargetCreaturePermanent();
+        this.getSpellAbility().addTarget(auraTarget);
+        this.getSpellAbility().addEffect(new AttachEffect(Outcome.GainControl));
+        Ability ability = new EnchantAbility(auraTarget.getTargetName());
+        this.addAbility(ability);
+
+        // You can't choose an untapped creature as this spell's target as you cast it.
+        auraTarget.replaceFilter(filter);
+        Effect controlEnchantedEffect = new ControlEnchantedEffect();
+        controlEnchantedEffect.setText("You can't choose an untapped creature as this spell's target as you cast it.<br>" + controlEnchantedEffect.getText(null));
+
+        // You control enchanted creature.
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, controlEnchantedEffect));
+    }
+
+    private EnthrallingHold(final EnthrallingHold card) {
+        super(card);
+    }
+
+    @Override
+    public EnthrallingHold copy() {
+        return new EnthrallingHold(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/cards/h/HoodedBlightfang.java b/Mage.Sets/src/mage/cards/h/HoodedBlightfang.java
new file mode 100644
index 0000000000..f3eb71472d
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/h/HoodedBlightfang.java
@@ -0,0 +1,58 @@
+package mage.cards.h;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.AttacksCreatureYouControlTriggeredAbility;
+import mage.abilities.common.DestroyPlaneswalkerWhenDamagedTriggeredAbility;
+import mage.abilities.effects.common.GainLifeEffect;
+import mage.abilities.effects.common.LoseLifeOpponentsEffect;
+import mage.abilities.keyword.DeathtouchAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.filter.common.FilterControlledCreaturePermanent;
+import mage.filter.predicate.mageobject.AbilityPredicate;
+
+import java.util.UUID;
+
+/**
+ *
+ * @author htrajan
+ */
+public final class HoodedBlightfang extends CardImpl {
+
+    private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("a creature you control with deathtouch");
+
+    static {
+        filter.add(new AbilityPredicate(DeathtouchAbility.class));
+    }
+
+    public HoodedBlightfang(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}");
+        
+        this.subtype.add(SubType.SNAKE);
+        this.power = new MageInt(1);
+        this.toughness = new MageInt(4);
+
+        // Deathtouch
+        this.addAbility(DeathtouchAbility.getInstance());
+
+        // Whenever a creature you control with deathtouch attacks, each opponent loses 1 life and you gain 1 life.
+        Ability ability = new AttacksCreatureYouControlTriggeredAbility(new LoseLifeOpponentsEffect(1), false, filter);
+        ability.addEffect(new GainLifeEffect(1).setText("and you gain 1 life"));
+        this.addAbility(ability);
+
+        // Whenever a creature you control with deathtouch deals damage to a planeswalker, destroy that planeswalker.
+        this.addAbility(new DestroyPlaneswalkerWhenDamagedTriggeredAbility(filter));
+    }
+
+    private HoodedBlightfang(final HoodedBlightfang card) {
+        super(card);
+    }
+
+    @Override
+    public HoodedBlightfang copy() {
+        return new HoodedBlightfang(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/cards/s/SkillBorrower.java b/Mage.Sets/src/mage/cards/s/SkillBorrower.java
index 60f9d87363..0fefe34ec9 100644
--- a/Mage.Sets/src/mage/cards/s/SkillBorrower.java
+++ b/Mage.Sets/src/mage/cards/s/SkillBorrower.java
@@ -1,26 +1,19 @@
 package mage.cards.s;
 
-import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.Ability;
-import mage.abilities.ActivatedAbility;
 import mage.abilities.StaticAbility;
 import mage.abilities.common.SimpleStaticAbility;
-import mage.abilities.effects.ContinuousEffectImpl;
+import mage.abilities.effects.common.GainActivatedAbilitiesOfTopCardEffect;
 import mage.abilities.effects.common.continuous.PlayWithTheTopCardRevealedEffect;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.Duration;
-import mage.constants.Layer;
-import mage.constants.Outcome;
-import mage.constants.SubLayer;
 import mage.constants.SubType;
 import mage.constants.Zone;
-import mage.game.Game;
-import mage.game.permanent.Permanent;
-import mage.players.Player;
+import mage.filter.FilterCard;
+import mage.filter.predicate.Predicates;
+
+import java.util.UUID;
 
 /**
  *
@@ -54,8 +47,14 @@ public final class SkillBorrower extends CardImpl {
 
 class SkillBorrowerAbility extends StaticAbility {
 
+    private static final FilterCard filter = new FilterCard("an artifact or creature card");
+
+    static {
+        filter.add(Predicates.or(CardType.CREATURE.getPredicate(), CardType.ARTIFACT.getPredicate()));
+    }
+
     public SkillBorrowerAbility() {
-        super(Zone.BATTLEFIELD, new SkillBorrowerEffect());
+        super(Zone.BATTLEFIELD, new GainActivatedAbilitiesOfTopCardEffect(filter));
     }
 
     public SkillBorrowerAbility(SkillBorrowerAbility ability) {
@@ -66,47 +65,5 @@ class SkillBorrowerAbility extends StaticAbility {
     public SkillBorrowerAbility copy() {
         return new SkillBorrowerAbility(this);
     }
-
-    @Override
-    public String getRule() {
-        return "As long as the top card of your library is an artifact or creature card, {this} has all activated abilities of that card";
-    }
 }
 
-class SkillBorrowerEffect extends ContinuousEffectImpl {
-
-    public SkillBorrowerEffect() {
-        super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility);
-        staticText = "As long as the top card of your library is an artifact or creature card, {this} has all activated abilities of that card";
-    }
-
-    public SkillBorrowerEffect(final SkillBorrowerEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public SkillBorrowerEffect copy() {
-        return new SkillBorrowerEffect(this);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        Player player = game.getPlayer(source.getControllerId());
-        if (player != null) {
-            Card card = player.getLibrary().getFromTop(game);
-            if (card != null && (card.isCreature() || card.isArtifact())) {
-                Permanent permanent = game.getPermanent(source.getSourceId());
-                if (permanent != null) {
-                    for (Ability ability : card.getAbilities(game)) {
-                        if (ability instanceof ActivatedAbility) {
-                            permanent.addAbility(ability, source.getSourceId(), game);
-                        }
-                    }
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 8bb6310bfc..1c7be8b046 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -40,6 +40,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Angelic Ascension", 3, Rarity.UNCOMMON, mage.cards.a.AngelicAscension.class));
         cards.add(new SetCardInfo("Animal Sanctuary", 242, Rarity.RARE, mage.cards.a.AnimalSanctuary.class));
         cards.add(new SetCardInfo("Anointed Chorister", 4, Rarity.COMMON, mage.cards.a.AnointedChorister.class));
+        cards.add(new SetCardInfo("Archfiend's Vessel", 88, Rarity.UNCOMMON, mage.cards.a.ArchfiendsVessel.class));
         cards.add(new SetCardInfo("Aven Gagglemaster", 5, Rarity.UNCOMMON, mage.cards.a.AvenGagglemaster.class));
         cards.add(new SetCardInfo("Azusa, Lost but Seeking", 173, Rarity.RARE, mage.cards.a.AzusaLostButSeeking.class));
         cards.add(new SetCardInfo("Bad Deal", 89, Rarity.UNCOMMON, mage.cards.b.BadDeal.class));
@@ -77,6 +78,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Colossal Dreadmaw", 176, Rarity.COMMON, mage.cards.c.ColossalDreadmaw.class));
         cards.add(new SetCardInfo("Conclave Mentor", 216, Rarity.UNCOMMON, mage.cards.c.ConclaveMentor.class));
         cards.add(new SetCardInfo("Concordia Pegasus", 12, Rarity.COMMON, mage.cards.c.ConcordiaPegasus.class));
+        cards.add(new SetCardInfo("Conspicuous Snoop", 139, Rarity.RARE, mage.cards.c.ConspicuousSnoop.class));
         cards.add(new SetCardInfo("Containment Priest", 13, Rarity.RARE, mage.cards.c.ContainmentPriest.class));
         cards.add(new SetCardInfo("Crash Through", 140, Rarity.COMMON, mage.cards.c.CrashThrough.class));
         cards.add(new SetCardInfo("Crypt Lurker", 93, Rarity.COMMON, mage.cards.c.CryptLurker.class));
@@ -95,6 +97,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Duress", 96, Rarity.COMMON, mage.cards.d.Duress.class));
         cards.add(new SetCardInfo("Elder Gargaroth", 179, Rarity.MYTHIC, mage.cards.e.ElderGargaroth.class));
         cards.add(new SetCardInfo("Eliminate", 97, Rarity.UNCOMMON, mage.cards.e.Eliminate.class));
+        cards.add(new SetCardInfo("Enthralling Hold", 49, Rarity.UNCOMMON, mage.cards.e.EnthrallingHold.class));
         cards.add(new SetCardInfo("Epitaph Golem", 230, Rarity.UNCOMMON, mage.cards.e.EpitaphGolem.class));
         cards.add(new SetCardInfo("Experimental Overload", 218, Rarity.UNCOMMON, mage.cards.e.ExperimentalOverload.class));
         cards.add(new SetCardInfo("Fabled Passage", 246, Rarity.RARE, mage.cards.f.FabledPassage.class));
@@ -137,6 +140,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Heroic Intervention", 188, Rarity.RARE, mage.cards.h.HeroicIntervention.class));
         cards.add(new SetCardInfo("Historian of Zhalfir", 325, Rarity.UNCOMMON, mage.cards.h.HistorianOfZhalfir.class));
         cards.add(new SetCardInfo("Hobblefiend", 152, Rarity.COMMON, mage.cards.h.Hobblefiend.class));
+        cards.add(new SetCardInfo("Hooded Blightfang", 104, Rarity.RARE, mage.cards.h.HoodedBlightfang.class));
         cards.add(new SetCardInfo("Hunter's Edge", 189, Rarity.COMMON, mage.cards.h.HuntersEdge.class));
         cards.add(new SetCardInfo("Igneous Cur", 153, Rarity.COMMON, mage.cards.i.IgneousCur.class));
         cards.add(new SetCardInfo("Indulging Patrician", 219, Rarity.UNCOMMON, mage.cards.i.IndulgingPatrician.class));
diff --git a/Mage/src/main/java/mage/abilities/common/AttacksCreatureYouControlTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/AttacksCreatureYouControlTriggeredAbility.java
index 36aa72e858..6f67313da9 100644
--- a/Mage/src/main/java/mage/abilities/common/AttacksCreatureYouControlTriggeredAbility.java
+++ b/Mage/src/main/java/mage/abilities/common/AttacksCreatureYouControlTriggeredAbility.java
@@ -76,9 +76,9 @@ public class AttacksCreatureYouControlTriggeredAbility extends TriggeredAbilityI
     public String getRule() {
         String an;
         String who = filter.getMessage();
-        if (who.startsWith("another")) {
+        if (who.startsWith("another") || who.startsWith("a ")) {
             an = "";
-        } else if (who.startsWith("a")) {
+        } else if (who.length() > 0 && "aeiou".contains(who.charAt(0) + "")) {
             an = "an ";
         } else {
             an = "a ";
diff --git a/Mage/src/main/java/mage/abilities/common/DestroyPlaneswalkerWhenDamagedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DestroyPlaneswalkerWhenDamagedTriggeredAbility.java
new file mode 100644
index 0000000000..1d8eabc2bd
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/common/DestroyPlaneswalkerWhenDamagedTriggeredAbility.java
@@ -0,0 +1,62 @@
+package mage.abilities.common;
+
+import mage.abilities.TriggeredAbilityImpl;
+import mage.abilities.effects.Effect;
+import mage.abilities.effects.common.DestroyTargetEffect;
+import mage.constants.Zone;
+import mage.filter.FilterPermanent;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+import mage.game.permanent.Permanent;
+import mage.target.targetpointer.FixedTarget;
+
+public class DestroyPlaneswalkerWhenDamagedTriggeredAbility extends TriggeredAbilityImpl {
+
+    private final FilterPermanent filter;
+
+    public DestroyPlaneswalkerWhenDamagedTriggeredAbility() {
+        this((FilterPermanent) null);
+    }
+
+    public DestroyPlaneswalkerWhenDamagedTriggeredAbility(FilterPermanent filter) {
+        super(Zone.BATTLEFIELD, null);
+        this.filter = filter;
+    }
+
+    private DestroyPlaneswalkerWhenDamagedTriggeredAbility(final DestroyPlaneswalkerWhenDamagedTriggeredAbility effect) {
+        super(effect);
+        this.filter = effect.filter;
+    }
+
+    @Override
+    public DestroyPlaneswalkerWhenDamagedTriggeredAbility copy() {
+        return new DestroyPlaneswalkerWhenDamagedTriggeredAbility(this);
+    }
+
+    @Override
+    public boolean checkEventType(GameEvent event, Game game) {
+        return event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER;
+    }
+
+    @Override
+    public boolean checkTrigger(GameEvent event, Game game) {
+        Permanent permanent = getSourcePermanentIfItStillExists(game);
+        if (permanent != null) {
+            boolean applies = filter != null ?
+                filter.match(permanent, game) : event.getSourceId().equals(getSourceId());
+            if (applies) {
+                Effect effect = new DestroyTargetEffect();
+                effect.setTargetPointer(new FixedTarget(event.getTargetId(), game));
+                this.getEffects().clear();
+                this.addEffect(effect);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public String getRule() {
+        return "Whenever " + (filter != null ? filter.getMessage() : "this creature") + " deals damage to a planeswalker, destroy that planeswalker.";
+    }
+}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/GainActivatedAbilitiesOfTopCardEffect.java b/Mage/src/main/java/mage/abilities/effects/common/GainActivatedAbilitiesOfTopCardEffect.java
new file mode 100644
index 0000000000..2ea7fe132c
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/effects/common/GainActivatedAbilitiesOfTopCardEffect.java
@@ -0,0 +1,56 @@
+package mage.abilities.effects.common;
+
+import mage.abilities.Ability;
+import mage.abilities.ActivatedAbility;
+import mage.abilities.effects.ContinuousEffectImpl;
+import mage.cards.Card;
+import mage.constants.Duration;
+import mage.constants.Layer;
+import mage.constants.Outcome;
+import mage.constants.SubLayer;
+import mage.filter.FilterCard;
+import mage.game.Game;
+import mage.game.permanent.Permanent;
+import mage.players.Player;
+
+public class GainActivatedAbilitiesOfTopCardEffect extends ContinuousEffectImpl {
+
+    private final FilterCard filter;
+
+    public GainActivatedAbilitiesOfTopCardEffect(FilterCard filter) {
+        super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility);
+        staticText = "As long as the top card of your library is " + filter.getMessage() + ", {this} has all activated abilities of that card";
+        this.filter = filter;
+    }
+
+    public GainActivatedAbilitiesOfTopCardEffect(final GainActivatedAbilitiesOfTopCardEffect effect) {
+        super(effect);
+        this.filter = effect.filter;
+    }
+
+    @Override
+    public GainActivatedAbilitiesOfTopCardEffect copy() {
+        return new GainActivatedAbilitiesOfTopCardEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Player player = game.getPlayer(source.getControllerId());
+        if (player != null) {
+            Card card = player.getLibrary().getFromTop(game);
+            if (card != null && filter.match(card, game)) {
+                Permanent permanent = game.getPermanent(source.getSourceId());
+                if (permanent != null) {
+                    for (Ability ability : card.getAbilities(game)) {
+                        if (ability instanceof ActivatedAbility) {
+                            permanent.addAbility(ability, source.getSourceId(), game);
+                        }
+                    }
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+}
diff --git a/Mage/src/main/java/mage/game/permanent/token/AssassinToken2.java b/Mage/src/main/java/mage/game/permanent/token/AssassinToken2.java
index 5f7b407718..2ddde6e5b4 100644
--- a/Mage/src/main/java/mage/game/permanent/token/AssassinToken2.java
+++ b/Mage/src/main/java/mage/game/permanent/token/AssassinToken2.java
@@ -1,16 +1,10 @@
 package mage.game.permanent.token;
 
 import mage.MageInt;
-import mage.abilities.TriggeredAbilityImpl;
-import mage.abilities.effects.Effect;
-import mage.abilities.effects.common.DestroyTargetEffect;
+import mage.abilities.common.DestroyPlaneswalkerWhenDamagedTriggeredAbility;
 import mage.abilities.keyword.DeathtouchAbility;
 import mage.constants.CardType;
 import mage.constants.SubType;
-import mage.constants.Zone;
-import mage.game.Game;
-import mage.game.events.GameEvent;
-import mage.target.targetpointer.FixedTarget;
 
 /**
  * @author TheElk801
@@ -25,7 +19,7 @@ public final class AssassinToken2 extends TokenImpl {
         power = new MageInt(1);
         toughness = new MageInt(1);
         addAbility(DeathtouchAbility.getInstance());
-        addAbility(new AssassinToken2TriggeredAbility());
+        addAbility(new DestroyPlaneswalkerWhenDamagedTriggeredAbility());
 
         setOriginalExpansionSetCode("WAR");
     }
@@ -39,40 +33,3 @@ public final class AssassinToken2 extends TokenImpl {
     }
 }
 
-class AssassinToken2TriggeredAbility extends TriggeredAbilityImpl {
-
-    AssassinToken2TriggeredAbility() {
-        super(Zone.BATTLEFIELD, null);
-    }
-
-    private AssassinToken2TriggeredAbility(final AssassinToken2TriggeredAbility effect) {
-        super(effect);
-    }
-
-    @Override
-    public AssassinToken2TriggeredAbility copy() {
-        return new AssassinToken2TriggeredAbility(this);
-    }
-
-    @Override
-    public boolean checkEventType(GameEvent event, Game game) {
-        return event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER;
-    }
-
-    @Override
-    public boolean checkTrigger(GameEvent event, Game game) {
-        if (event.getSourceId().equals(getSourceId())) {
-            Effect effect = new DestroyTargetEffect();
-            effect.setTargetPointer(new FixedTarget(event.getTargetId(), game));
-            this.getEffects().clear();
-            this.addEffect(effect);
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    public String getRule() {
-        return "Whenever this creature deals damage to a planeswalker, destroy that planeswalker.";
-    }
-}
\ No newline at end of file
diff --git a/Mage/src/main/java/mage/watchers/common/SpellsCastWatcher.java b/Mage/src/main/java/mage/watchers/common/SpellsCastWatcher.java
index 883ab6f9ef..46a1a7fcc0 100644
--- a/Mage/src/main/java/mage/watchers/common/SpellsCastWatcher.java
+++ b/Mage/src/main/java/mage/watchers/common/SpellsCastWatcher.java
@@ -5,8 +5,6 @@
  */
 package mage.watchers.common;
 
-import java.util.*;
-
 import mage.MageObject;
 import mage.constants.WatcherScope;
 import mage.constants.Zone;
@@ -17,6 +15,12 @@ import mage.game.events.GameEvent.EventType;
 import mage.game.stack.Spell;
 import mage.watchers.Watcher;
 
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
 /**
  *
  * @author LevelX2
@@ -24,6 +28,7 @@ import mage.watchers.Watcher;
 public class SpellsCastWatcher extends Watcher {
 
     private final Map<UUID, List<Spell>> spellsCast = new HashMap<>();
+    private final Map<UUID, List<Spell>> spellsCastFromGraveyard = new HashMap<>();
     private int nonCreatureSpells;
 
     public SpellsCastWatcher() {
@@ -42,13 +47,21 @@ public class SpellsCastWatcher extends Watcher {
             }
             if (spell != null) {
                 List<Spell> spells;
+                List<Spell> graveyardSpells;
                 if (!spellsCast.containsKey(spell.getControllerId())) {
                     spells = new ArrayList<>();
                     spellsCast.put(spell.getControllerId(), spells);
+                    graveyardSpells = new ArrayList<>();
+                    spellsCastFromGraveyard.put(spell.getControllerId(), graveyardSpells);
+
                 } else {
                     spells = spellsCast.get(spell.getControllerId());
+                    graveyardSpells = spellsCastFromGraveyard.get(spell.getControllerId());
                 }
                 spells.add(spell.copy()); // copy needed because attributes like color could be changed later
+                if (event.getZone() == Zone.GRAVEYARD) {
+                    graveyardSpells.add(spell.copy());
+                }
                 if (StaticFilters.FILTER_SPELL_NON_CREATURE.match(spell, game)) {
                     nonCreatureSpells++;
                 }
@@ -61,12 +74,17 @@ public class SpellsCastWatcher extends Watcher {
         super.reset();
         nonCreatureSpells = 0;
         spellsCast.clear();
+        spellsCastFromGraveyard.clear();
     }
 
     public List<Spell> getSpellsCastThisTurn(UUID playerId) {
         return spellsCast.get(playerId);
     }
 
+    public List<Spell> getSpellsCastFromGraveyardThisTurn(UUID playerId) {
+        return spellsCastFromGraveyard.get(playerId);
+    }
+
     public int getNumberOfNonCreatureSpells() {
         return nonCreatureSpells;
     }

From c46e5d2399d4f88d5bfe3ee11c2901a6bc918307 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Fri, 26 Jun 2020 20:56:34 -0400
Subject: [PATCH 534/586] Implemented Zurzoth, Chaos Rider

---
 .../src/mage/cards/z/ZurzothChaosRider.java   | 185 ++++++++++++++++++
 Mage.Sets/src/mage/sets/Jumpstart.java        |   1 +
 2 files changed, 186 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/z/ZurzothChaosRider.java

diff --git a/Mage.Sets/src/mage/cards/z/ZurzothChaosRider.java b/Mage.Sets/src/mage/cards/z/ZurzothChaosRider.java
new file mode 100644
index 0000000000..62ec419371
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/z/ZurzothChaosRider.java
@@ -0,0 +1,185 @@
+package mage.cards.z;
+
+import mage.MageInt;
+import mage.MageItem;
+import mage.abilities.Ability;
+import mage.abilities.TriggeredAbilityImpl;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.CreateTokenEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.*;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+import mage.game.permanent.token.DevilToken;
+import mage.players.Player;
+
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class ZurzothChaosRider extends CardImpl {
+
+    public ZurzothChaosRider(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}");
+
+        this.addSuperType(SuperType.LEGENDARY);
+        this.subtype.add(SubType.DEVIL);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(3);
+
+        // Whenever an opponent draws their first card each turn, if it's not their turn, you create a 1/1 red Devil creature token with "When this creature dies, it deals 1 damage to any target."
+        this.addAbility(new ZurzothChaosRiderDrawAbility());
+
+        // Whenever one or more Devils you control attack one or more players, you and those players each draw a card, then discard a card at random.
+        this.addAbility(new ZurzothChaosRiderAttackAbility());
+    }
+
+    private ZurzothChaosRider(final ZurzothChaosRider card) {
+        super(card);
+    }
+
+    @Override
+    public ZurzothChaosRider copy() {
+        return new ZurzothChaosRider(this);
+    }
+}
+
+class ZurzothChaosRiderDrawAbility extends TriggeredAbilityImpl {
+
+    private final Set<UUID> playerIds = new HashSet<>();
+
+    ZurzothChaosRiderDrawAbility() {
+        super(Zone.BATTLEFIELD, new CreateTokenEffect(new DevilToken()));
+    }
+
+    private ZurzothChaosRiderDrawAbility(final ZurzothChaosRiderDrawAbility ability) {
+        super(ability);
+        this.playerIds.addAll(ability.playerIds);
+    }
+
+    @Override
+    public boolean checkEventType(GameEvent event, Game game) {
+        return event.getType() == GameEvent.EventType.DREW_CARD
+                || event.getType() == GameEvent.EventType.END_PHASE_POST;
+    }
+
+    @Override
+    public boolean checkTrigger(GameEvent event, Game game) {
+        switch (event.getType()) {
+            case END_PHASE_POST:
+                playerIds.clear();
+                return false;
+            case DREW_CARD:
+                if (playerIds.contains(event.getPlayerId())) {
+                    return false;
+                }
+                playerIds.add(event.getPlayerId());
+                return !game.isActivePlayer(event.getPlayerId())
+                        && game.getOpponents(this.getControllerId()).contains(event.getPlayerId());
+            default:
+                return false;
+        }
+    }
+
+    @Override
+    public ZurzothChaosRiderDrawAbility copy() {
+        return new ZurzothChaosRiderDrawAbility(this);
+    }
+
+    @Override
+    public String getRule() {
+        return "Whenever an opponent draws their first card each turn, if it's not their turn, " +
+                "you create a 1/1 red Devil creature token with \"When this creature dies, it deals 1 damage to any target.\"";
+    }
+}
+
+class ZurzothChaosRiderAttackAbility extends TriggeredAbilityImpl {
+
+    ZurzothChaosRiderAttackAbility() {
+        super(Zone.BATTLEFIELD, null);
+    }
+
+    private ZurzothChaosRiderAttackAbility(final ZurzothChaosRiderAttackAbility ability) {
+        super(ability);
+    }
+
+    @Override
+    public boolean checkEventType(GameEvent event, Game game) {
+        return event.getType() == GameEvent.EventType.DECLARED_ATTACKERS;
+    }
+
+    @Override
+    public boolean checkTrigger(GameEvent event, Game game) {
+        Set<UUID> playerIds = new HashSet<>();
+        game.getCombat()
+                .getAttackers()
+                .stream()
+                .map(game::getPermanent)
+                .filter(Objects::nonNull)
+                .filter(permanent -> permanent.hasSubtype(SubType.DEVIL, game))
+                .map(MageItem::getId)
+                .map(game.getCombat()::getDefenderId)
+                .map(game::getPlayer)
+                .filter(Objects::nonNull)
+                .map(MageItem::getId)
+                .forEach(playerIds::add);
+        if (playerIds.isEmpty()) {
+            return false;
+        }
+        playerIds.add(getControllerId());
+        this.getEffects().clear();
+        this.addEffect(new ZurzothChaosRiderEffect(playerIds));
+        return true;
+    }
+
+    @Override
+    public ZurzothChaosRiderAttackAbility copy() {
+        return new ZurzothChaosRiderAttackAbility(this);
+    }
+
+    @Override
+    public String getRule() {
+        return "Whenever one or more Devils you control attack one or more players, " +
+                "you and those players each draw a card, then discard a card at random.";
+    }
+}
+
+class ZurzothChaosRiderEffect extends OneShotEffect {
+
+    private final Set<UUID> playerIds = new HashSet<>();
+
+    ZurzothChaosRiderEffect(Set<UUID> playerIds) {
+        super(Outcome.Benefit);
+        this.playerIds.addAll(playerIds);
+    }
+
+    private ZurzothChaosRiderEffect(final ZurzothChaosRiderEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public ZurzothChaosRiderEffect copy() {
+        return new ZurzothChaosRiderEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) {
+            if (!playerIds.contains(playerId)) {
+                continue;
+            }
+            Player player = game.getPlayer(playerId);
+            if (player == null) {
+                continue;
+            }
+            player.drawCards(1, source.getSourceId(), game);
+            player.discard(1, true, source, game);
+        }
+        return true;
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index 1017a848b8..1c1eeb8667 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -478,5 +478,6 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Young Pyromancer", 372, Rarity.UNCOMMON, mage.cards.y.YoungPyromancer.class));
         cards.add(new SetCardInfo("Zendikar's Roil", 448, Rarity.UNCOMMON, mage.cards.z.ZendikarsRoil.class));
         cards.add(new SetCardInfo("Zombie Infestation", 288, Rarity.UNCOMMON, mage.cards.z.ZombieInfestation.class));
+        cards.add(new SetCardInfo("Zurzoth, Chaos Rider", 27, Rarity.RARE, mage.cards.z.ZurzothChaosRider.class));
     }
 }

From 2252648f010c51727fb6bc0d7527c8049eb40b78 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Sat, 27 Jun 2020 05:36:04 +0400
Subject: [PATCH 535/586] * Added card hints to Avatar of Might, Avatar of
 Will,  Avatar of Woe, Dusk Feaster, Rekindled Flame; * Refactor: removed
 custom spell cost reduction effects;

---
 Mage.Sets/src/mage/cards/a/AvatarOfMight.java | 68 +++++----------
 Mage.Sets/src/mage/cards/a/AvatarOfWill.java  | 69 +++-------------
 Mage.Sets/src/mage/cards/a/AvatarOfWoe.java   | 60 +++++---------
 .../src/mage/cards/d/DaybreakChimera.java     | 51 ++----------
 .../src/mage/cards/d/DragToTheUnderworld.java | 50 ++---------
 Mage.Sets/src/mage/cards/d/DuskFeaster.java   | 82 ++++---------------
 .../src/mage/cards/g/GearseekerSerpent.java   |  8 +-
 .../src/mage/cards/m/MarshmistTitan.java      | 54 ++----------
 .../src/mage/cards/r/RekindledFlame.java      | 39 ++-------
 .../OpponentHasNoCardsInHandCondition.java    | 36 ++++++++
 .../effects/common/AffinityEffect.java        | 22 ++---
 .../cost/SpellCostReductionSourceEffect.java  | 21 ++++-
 ...CostReductionSourceForOpponentsEffect.java | 18 ++--
 13 files changed, 167 insertions(+), 411 deletions(-)
 create mode 100644 Mage/src/main/java/mage/abilities/condition/common/OpponentHasNoCardsInHandCondition.java

diff --git a/Mage.Sets/src/mage/cards/a/AvatarOfMight.java b/Mage.Sets/src/mage/cards/a/AvatarOfMight.java
index c35a03c79b..d051a3174d 100644
--- a/Mage.Sets/src/mage/cards/a/AvatarOfMight.java
+++ b/Mage.Sets/src/mage/cards/a/AvatarOfMight.java
@@ -1,27 +1,30 @@
-
 package mage.cards.a;
 
-import java.util.UUID;
 import mage.MageInt;
-import mage.Mana;
 import mage.abilities.Ability;
-import mage.abilities.SpellAbility;
 import mage.abilities.common.SimpleStaticAbility;
-import mage.abilities.effects.common.cost.CostModificationEffectImpl;
+import mage.abilities.condition.Condition;
+import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect;
+import mage.abilities.hint.ConditionHint;
 import mage.abilities.keyword.TrampleAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.*;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.Zone;
 import mage.filter.StaticFilters;
 import mage.game.Game;
 import mage.players.Player;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class AvatarOfMight extends CardImpl {
 
+    private static final Condition condition = new AvatarOfMightCondition();
+
     public AvatarOfMight(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{G}{G}");
         this.subtype.add(SubType.AVATAR);
@@ -29,7 +32,10 @@ public final class AvatarOfMight extends CardImpl {
         this.toughness = new MageInt(8);
 
         // If an opponent controls at least four more creatures than you, Avatar of Might costs {6} less to cast.
-        this.addAbility(new SimpleStaticAbility(Zone.ALL, new AvatarOfMightCostReductionEffect()));
+        this.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(6, condition)
+                .setText("If an opponent controls at least four more creatures than you, {this} costs {6} less to cast"))
+                .addHint(new ConditionHint(condition, "Opponent controls at least four more creatures than you"))
+        );
 
         // Trample
         this.addAbility(TrampleAbility.getInstance());
@@ -45,49 +51,17 @@ public final class AvatarOfMight extends CardImpl {
     }
 }
 
-class AvatarOfMightCostReductionEffect extends CostModificationEffectImpl {
-
-    AvatarOfMightCostReductionEffect() {
-        super(Duration.Custom, Outcome.Benefit, CostModificationType.REDUCE_COST);
-        staticText = "If an opponent controls at least four more creatures than you, {this} costs {6} less to cast";
-    }
-
-    AvatarOfMightCostReductionEffect(final AvatarOfMightCostReductionEffect effect) {
-        super(effect);
-    }
+class AvatarOfMightCondition implements Condition {
 
     @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        SpellAbility spellAbility = (SpellAbility) abilityToModify;
-        Mana mana = spellAbility.getManaCostsToPay().getMana();
-        if (mana.getGeneric() > 0) {
-            int newCount = mana.getGeneric() - 6;
-            if (newCount < 0) {
-                newCount = 0;
-            }
-            mana.setGeneric(newCount);
-            spellAbility.getManaCostsToPay().load(mana.toString());
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        if (abilityToModify.getSourceId().equals(source.getSourceId()) && (abilityToModify instanceof SpellAbility)) {
-            int creatures = game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), game);
-            for (UUID playerId : game.getOpponents(source.getControllerId())) {
-                Player opponent = game.getPlayer(playerId);
-                if (opponent != null && game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_CREATURE, opponent.getId(), game) >= creatures + 4) {
-                    return true;
-                }
+    public boolean apply(Game game, Ability source) {
+        int creatures = game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), game);
+        for (UUID playerId : game.getOpponents(source.getControllerId())) {
+            Player opponent = game.getPlayer(playerId);
+            if (opponent != null && game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_CREATURE, opponent.getId(), game) >= creatures + 4) {
+                return true;
             }
         }
         return false;
     }
-
-    @Override
-    public AvatarOfMightCostReductionEffect copy() {
-        return new AvatarOfMightCostReductionEffect(this);
-    }
 }
diff --git a/Mage.Sets/src/mage/cards/a/AvatarOfWill.java b/Mage.Sets/src/mage/cards/a/AvatarOfWill.java
index c57877756a..d67fb66059 100644
--- a/Mage.Sets/src/mage/cards/a/AvatarOfWill.java
+++ b/Mage.Sets/src/mage/cards/a/AvatarOfWill.java
@@ -1,22 +1,20 @@
-
 package mage.cards.a;
 
-import java.util.UUID;
 import mage.MageInt;
-import mage.Mana;
-import mage.abilities.Ability;
-import mage.abilities.SpellAbility;
 import mage.abilities.common.SimpleStaticAbility;
-import mage.abilities.effects.common.cost.CostModificationEffectImpl;
+import mage.abilities.condition.common.OpponentHasNoCardsInHandCondition;
+import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect;
+import mage.abilities.hint.ConditionHint;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.*;
-import mage.game.Game;
-import mage.players.Player;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.Zone;
+
+import java.util.UUID;
 
 /**
- *
  * @author LevelX2
  */
 public final class AvatarOfWill extends CardImpl {
@@ -28,7 +26,10 @@ public final class AvatarOfWill extends CardImpl {
         this.toughness = new MageInt(6);
 
         // If an opponent has no cards in hand, Avatar of Will costs {6} less to cast.
-        this.addAbility(new SimpleStaticAbility(Zone.ALL, new AvatarOfWillCostReductionEffect()));
+        this.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(6, OpponentHasNoCardsInHandCondition.instance)
+                        .setText("If an opponent has no cards in hand, Avatar of Will costs {6} less to cast")
+                ).addHint(new ConditionHint(OpponentHasNoCardsInHandCondition.instance, "Opponent has no cards in hand"))
+        );
 
         // Flying
         this.addAbility(FlyingAbility.getInstance());
@@ -43,49 +44,3 @@ public final class AvatarOfWill extends CardImpl {
         return new AvatarOfWill(this);
     }
 }
-
-class AvatarOfWillCostReductionEffect extends CostModificationEffectImpl {
-
-    AvatarOfWillCostReductionEffect() {
-        super(Duration.Custom, Outcome.Benefit, CostModificationType.REDUCE_COST);
-        staticText = "If an opponent has no cards in hand, {this} costs {6} less to cast";
-    }
-
-    AvatarOfWillCostReductionEffect(final AvatarOfWillCostReductionEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        SpellAbility spellAbility = (SpellAbility) abilityToModify;
-        Mana mana = spellAbility.getManaCostsToPay().getMana();
-        if (mana.getGeneric() > 0) {
-            int newCount = mana.getGeneric() - 6;
-            if (newCount < 0) {
-                newCount = 0;
-            }
-            mana.setGeneric(newCount);
-            spellAbility.getManaCostsToPay().load(mana.toString());
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        if (abilityToModify.getSourceId().equals(source.getSourceId())) {
-            for (UUID playerId : game.getOpponents(source.getControllerId())) {
-                Player opponent = game.getPlayer(playerId);
-                if (opponent != null && opponent.getHand().isEmpty()) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public AvatarOfWillCostReductionEffect copy() {
-        return new AvatarOfWillCostReductionEffect(this);
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/a/AvatarOfWoe.java b/Mage.Sets/src/mage/cards/a/AvatarOfWoe.java
index 02115afba8..70911593e4 100644
--- a/Mage.Sets/src/mage/cards/a/AvatarOfWoe.java
+++ b/Mage.Sets/src/mage/cards/a/AvatarOfWoe.java
@@ -1,30 +1,36 @@
 package mage.cards.a;
 
-import java.util.UUID;
 import mage.MageInt;
-import mage.Mana;
 import mage.abilities.Ability;
-import mage.abilities.SpellAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.condition.Condition;
 import mage.abilities.costs.common.TapSourceCost;
+import mage.abilities.dynamicvalue.DynamicValue;
 import mage.abilities.dynamicvalue.common.CardsInAllGraveyardsCount;
 import mage.abilities.effects.common.DestroyTargetEffect;
-import mage.abilities.effects.common.cost.CostModificationEffectImpl;
+import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect;
+import mage.abilities.hint.ValueHint;
 import mage.abilities.keyword.FearAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.*;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.Zone;
 import mage.filter.StaticFilters;
 import mage.game.Game;
 import mage.target.common.TargetCreaturePermanent;
 
+import java.util.UUID;
+
 /**
- *
  * @author emerald000
  */
 public final class AvatarOfWoe extends CardImpl {
 
+    protected static final DynamicValue graveyardCount = new CardsInAllGraveyardsCount(StaticFilters.FILTER_CARD_CREATURE);
+    private static final Condition condition = new AvatarOfWoeCondition();
+
     public AvatarOfWoe(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{B}{B}");
         this.subtype.add(SubType.AVATAR);
@@ -32,12 +38,15 @@ public final class AvatarOfWoe extends CardImpl {
         this.toughness = new MageInt(5);
 
         // If there are ten or more creature cards total in all graveyards, Avatar of Woe costs {6} less to cast.
-        this.addAbility(new SimpleStaticAbility(Zone.ALL, new AvatarOfWoeCostReductionEffect()));
+        this.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(6, condition)
+                .setText("If there are ten or more creature cards total in all graveyards, {this} costs {6} less to cast"))
+                .addHint(new ValueHint("Creature cards in all graveyards", graveyardCount))
+        );
 
         // Fear
         this.addAbility(FearAbility.getInstance());
 
-        // {tap}: Destroy target creature. It can't be regenerated.
+        // {T}: Destroy target creature. It can't be regenerated.
         Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(true), new TapSourceCost());
         ability.addTarget(new TargetCreaturePermanent());
         this.addAbility(ability);
@@ -53,42 +62,13 @@ public final class AvatarOfWoe extends CardImpl {
     }
 }
 
-class AvatarOfWoeCostReductionEffect extends CostModificationEffectImpl {
-
-    AvatarOfWoeCostReductionEffect() {
-        super(Duration.Custom, Outcome.Benefit, CostModificationType.REDUCE_COST);
-        staticText = "If there are ten or more creature cards total in all graveyards, {this} costs {6} less to cast";
-    }
-
-    AvatarOfWoeCostReductionEffect(final AvatarOfWoeCostReductionEffect effect) {
-        super(effect);
-    }
+class AvatarOfWoeCondition implements Condition {
 
     @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        SpellAbility spellAbility = (SpellAbility) abilityToModify;
-        Mana mana = spellAbility.getManaCostsToPay().getMana();
-        if (mana.getGeneric() > 0) {
-            int newCount = mana.getGeneric() - 6;
-            if (newCount < 0) {
-                newCount = 0;
-            }
-            mana.setGeneric(newCount);
-            spellAbility.getManaCostsToPay().load(mana.toString());
+    public boolean apply(Game game, Ability source) {
+        if (AvatarOfWoe.graveyardCount.calculate(game, source, null) >= 10) {
             return true;
         }
         return false;
     }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        return abilityToModify.getSourceId().equals(source.getSourceId())
-                && (abilityToModify instanceof SpellAbility)
-                && new CardsInAllGraveyardsCount(StaticFilters.FILTER_CARD_CREATURE).calculate(game, source, this) >= 10;
-    }
-
-    @Override
-    public AvatarOfWoeCostReductionEffect copy() {
-        return new AvatarOfWoeCostReductionEffect(this);
-    }
 }
diff --git a/Mage.Sets/src/mage/cards/d/DaybreakChimera.java b/Mage.Sets/src/mage/cards/d/DaybreakChimera.java
index 7b6d0c1bb0..b8035bf7db 100644
--- a/Mage.Sets/src/mage/cards/d/DaybreakChimera.java
+++ b/Mage.Sets/src/mage/cards/d/DaybreakChimera.java
@@ -1,17 +1,15 @@
 package mage.cards.d;
 
 import mage.MageInt;
-import mage.Mana;
-import mage.abilities.Ability;
-import mage.abilities.SpellAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.dynamicvalue.common.DevotionCount;
-import mage.abilities.effects.common.cost.CostModificationEffectImpl;
+import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.*;
-import mage.game.Game;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.Zone;
 
 import java.util.UUID;
 
@@ -28,7 +26,9 @@ public final class DaybreakChimera extends CardImpl {
         this.toughness = new MageInt(3);
 
         // This spell costs {X} less to cast, where X is your devotion to white.
-        this.addAbility(new SimpleStaticAbility(Zone.ALL, new DaybreakChimeraEffect()).addHint(DevotionCount.W.getHint()));
+        this.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(DevotionCount.W))
+                .addHint(DevotionCount.W.getHint())
+        );
 
         // Flying
         this.addAbility(FlyingAbility.getInstance());
@@ -43,40 +43,3 @@ public final class DaybreakChimera extends CardImpl {
         return new DaybreakChimera(this);
     }
 }
-
-class DaybreakChimeraEffect extends CostModificationEffectImpl {
-
-    DaybreakChimeraEffect() {
-        super(Duration.Custom, Outcome.Benefit, CostModificationType.REDUCE_COST);
-        staticText = "This spell costs {X} less to cast, where X is your devotion to white. " +
-                "<i>(Each {W} in the mana costs of permanents you control counts toward your devotion to white.)</i>";
-    }
-
-    private DaybreakChimeraEffect(final DaybreakChimeraEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        SpellAbility spellAbility = (SpellAbility) abilityToModify;
-        Mana mana = spellAbility.getManaCostsToPay().getMana();
-        if (mana.getGeneric() == 0) {
-            return false;
-        }
-        int count = DevotionCount.W.calculate(game, source, this);
-        mana.setGeneric(Math.max(mana.getGeneric() - count, 0));
-        spellAbility.getManaCostsToPay().load(mana.toString());
-        return true;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        return abilityToModify instanceof SpellAbility
-                && abilityToModify.getSourceId().equals(source.getSourceId());
-    }
-
-    @Override
-    public DaybreakChimeraEffect copy() {
-        return new DaybreakChimeraEffect(this);
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/d/DragToTheUnderworld.java b/Mage.Sets/src/mage/cards/d/DragToTheUnderworld.java
index a569e15e87..bc91ad64c3 100644
--- a/Mage.Sets/src/mage/cards/d/DragToTheUnderworld.java
+++ b/Mage.Sets/src/mage/cards/d/DragToTheUnderworld.java
@@ -1,16 +1,13 @@
 package mage.cards.d;
 
-import mage.Mana;
-import mage.abilities.Ability;
-import mage.abilities.SpellAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.dynamicvalue.common.DevotionCount;
 import mage.abilities.effects.common.DestroyTargetEffect;
-import mage.abilities.effects.common.cost.CostModificationEffectImpl;
+import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.*;
-import mage.game.Game;
+import mage.constants.CardType;
+import mage.constants.Zone;
 import mage.target.common.TargetCreaturePermanent;
 
 import java.util.UUID;
@@ -24,7 +21,7 @@ public final class DragToTheUnderworld extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{B}{B}");
 
         // This spell costs {X} less to cast, where X is your devotion to black.
-        this.addAbility(new SimpleStaticAbility(Zone.ALL, new DragToTheUnderworldEffect())
+        this.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(DevotionCount.B))
                 .addHint(DevotionCount.B.getHint())
                 .setRuleAtTheTop(true));
 
@@ -41,41 +38,4 @@ public final class DragToTheUnderworld extends CardImpl {
     public DragToTheUnderworld copy() {
         return new DragToTheUnderworld(this);
     }
-}
-
-class DragToTheUnderworldEffect extends CostModificationEffectImpl {
-
-    DragToTheUnderworldEffect() {
-        super(Duration.Custom, Outcome.Benefit, CostModificationType.REDUCE_COST);
-        staticText = "This spell costs {X} less to cast, where X is your devotion to black. " +
-                "<i>(Each {B} in the mana costs of permanents you control counts toward your devotion to black.)</i> ";
-    }
-
-    private DragToTheUnderworldEffect(final DragToTheUnderworldEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        SpellAbility spellAbility = (SpellAbility) abilityToModify;
-        Mana mana = spellAbility.getManaCostsToPay().getMana();
-        if (mana.getGeneric() == 0) {
-            return false;
-        }
-        int count = DevotionCount.B.calculate(game, source, this);
-        mana.setGeneric(Math.max(mana.getGeneric() - count, 0));
-        spellAbility.getManaCostsToPay().load(mana.toString());
-        return true;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        return abilityToModify instanceof SpellAbility
-                && abilityToModify.getSourceId().equals(source.getSourceId());
-    }
-
-    @Override
-    public DragToTheUnderworldEffect copy() {
-        return new DragToTheUnderworldEffect(this);
-    }
-}
+}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/d/DuskFeaster.java b/Mage.Sets/src/mage/cards/d/DuskFeaster.java
index 8d586d0640..4075027397 100644
--- a/Mage.Sets/src/mage/cards/d/DuskFeaster.java
+++ b/Mage.Sets/src/mage/cards/d/DuskFeaster.java
@@ -1,36 +1,38 @@
-
 package mage.cards.d;
 
-import java.util.EnumSet;
-import java.util.UUID;
 import mage.MageInt;
-import mage.Mana;
 import mage.abilities.Ability;
-import mage.abilities.SpellAbility;
 import mage.abilities.common.SimpleStaticAbility;
-import mage.abilities.effects.common.cost.CostModificationEffectImpl;
+import mage.abilities.condition.common.DeliriumCondition;
+import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect;
+import mage.abilities.hint.common.DeliriumHint;
 import mage.abilities.keyword.FlyingAbility;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.*;
-import mage.game.Game;
-import mage.players.Player;
+import mage.constants.AbilityWord;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.Zone;
+
+import java.util.UUID;
 
 /**
- *
  * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com)
  */
 public final class DuskFeaster extends CardImpl {
 
     public DuskFeaster(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{5}{B}{B}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{B}{B}");
         this.subtype.add(SubType.VAMPIRE);
         this.power = new MageInt(4);
         this.toughness = new MageInt(5);
 
-        // Delirium - Dusk Feaster costs {2} less to cast if there are four or more card types among cards in your graveyard.
-        this.addAbility(new SimpleStaticAbility(Zone.ALL, new DuskFeasterCostReductionEffect()));
+        // <i>Delirium</i> &mdash; This spell costs {2} less to cast if there are four or more card types among cards in your graveyard.
+        Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(2, DeliriumCondition.instance));
+        ability.setRuleAtTheTop(true);
+        ability.setAbilityWord(AbilityWord.DELIRIUM);
+        ability.addHint(DeliriumHint.instance);
+        this.addAbility(ability);
 
         // Flying
         this.addAbility(FlyingAbility.getInstance());
@@ -44,56 +46,4 @@ public final class DuskFeaster extends CardImpl {
     public DuskFeaster copy() {
         return new DuskFeaster(this);
     }
-}
-
-class DuskFeasterCostReductionEffect extends CostModificationEffectImpl {
-
-    DuskFeasterCostReductionEffect() {
-        super(Duration.Custom, Outcome.Benefit, CostModificationType.REDUCE_COST);
-        staticText = "<i>Delirium</i> &mdash; {this} costs {2} less to cast if there are four or more card types among cards in your graveyard";
-    }
-
-    DuskFeasterCostReductionEffect(final DuskFeasterCostReductionEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        SpellAbility spellAbility = (SpellAbility) abilityToModify;
-        Mana mana = spellAbility.getManaCostsToPay().getMana();
-        if (mana.getGeneric() > 0) {
-            int newCount = mana.getGeneric() - 2;
-            if (newCount < 0) {
-                newCount = 0;
-            }
-            mana.setGeneric(newCount);
-            spellAbility.getManaCostsToPay().load(mana.toString());
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-
-        boolean hasDelirium = false;
-        Player controller = game.getPlayer(source.getControllerId());
-        if (controller != null) {
-            EnumSet<CardType> foundCardTypes = EnumSet.noneOf(CardType.class);
-            for (Card card : controller.getGraveyard().getCards(game)) {
-                foundCardTypes.addAll(card.getCardType());
-            }
-            int number = foundCardTypes.size();
-            hasDelirium = number > 3;
-        }
-
-        return abilityToModify.getSourceId().equals(source.getSourceId())
-                && (abilityToModify instanceof SpellAbility)
-                && hasDelirium;
-    }
-
-    @Override
-    public DuskFeasterCostReductionEffect copy() {
-        return new DuskFeasterCostReductionEffect(this);
-    }
 }
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/g/GearseekerSerpent.java b/Mage.Sets/src/mage/cards/g/GearseekerSerpent.java
index b350bc7107..30468d663c 100644
--- a/Mage.Sets/src/mage/cards/g/GearseekerSerpent.java
+++ b/Mage.Sets/src/mage/cards/g/GearseekerSerpent.java
@@ -1,7 +1,5 @@
-
 package mage.cards.g;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleActivatedAbility;
@@ -16,14 +14,15 @@ import mage.filter.common.FilterControlledPermanent;
 import mage.game.Game;
 import mage.util.CardUtil;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class GearseekerSerpent extends CardImpl {
 
     public GearseekerSerpent(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{5}{U}{U}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{U}{U}");
         this.subtype.add(SubType.SERPENT);
         this.power = new MageInt(5);
         this.toughness = new MageInt(6);
@@ -70,7 +69,6 @@ class GearseekerSerpentCostReductionEffect extends CostModificationEffectImpl {
         if (count > 0) {
             CardUtil.reduceCost(abilityToModify, count);
         }
-
         return true;
     }
 
diff --git a/Mage.Sets/src/mage/cards/m/MarshmistTitan.java b/Mage.Sets/src/mage/cards/m/MarshmistTitan.java
index a3c86710c0..8c380f606e 100644
--- a/Mage.Sets/src/mage/cards/m/MarshmistTitan.java
+++ b/Mage.Sets/src/mage/cards/m/MarshmistTitan.java
@@ -1,16 +1,15 @@
 package mage.cards.m;
 
 import mage.MageInt;
-import mage.Mana;
 import mage.abilities.Ability;
-import mage.abilities.SpellAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.dynamicvalue.common.DevotionCount;
-import mage.abilities.effects.common.cost.CostModificationEffectImpl;
+import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.*;
-import mage.game.Game;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.Zone;
 
 import java.util.UUID;
 
@@ -26,9 +25,11 @@ public final class MarshmistTitan extends CardImpl {
         this.power = new MageInt(4);
         this.toughness = new MageInt(5);
 
-        // Marshmist Titan costs {X} less to cast, where X is your devotion to black.
-        this.addAbility(new SimpleStaticAbility(Zone.ALL, new MarshmistTitanCostReductionEffect())
-                .addHint(DevotionCount.B.getHint()));
+        // This spell costs {X} less to cast, where X is your devotion to black.
+        Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(DevotionCount.B));
+        ability.addHint(DevotionCount.B.getHint());
+        this.addAbility(ability);
+
     }
 
     private MarshmistTitan(final MarshmistTitan card) {
@@ -40,40 +41,3 @@ public final class MarshmistTitan extends CardImpl {
         return new MarshmistTitan(this);
     }
 }
-
-class MarshmistTitanCostReductionEffect extends CostModificationEffectImpl {
-
-    MarshmistTitanCostReductionEffect() {
-        super(Duration.Custom, Outcome.Benefit, CostModificationType.REDUCE_COST);
-        staticText = "This spell costs {X} less to cast, where X is your devotion to black. " +
-                "<i>(Each {B} in the mana costs of permanents you control counts toward your devotion to black.)</i> ";
-    }
-
-    private MarshmistTitanCostReductionEffect(final MarshmistTitanCostReductionEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        SpellAbility spellAbility = (SpellAbility) abilityToModify;
-        Mana mana = spellAbility.getManaCostsToPay().getMana();
-        if (mana.getGeneric() == 0) {
-            return false;
-        }
-        int count = DevotionCount.B.calculate(game, source, this);
-        mana.setGeneric(Math.max(mana.getGeneric() - count, 0));
-        spellAbility.getManaCostsToPay().load(mana.toString());
-        return true;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        return abilityToModify instanceof SpellAbility
-                && abilityToModify.getSourceId().equals(source.getSourceId());
-    }
-
-    @Override
-    public MarshmistTitanCostReductionEffect copy() {
-        return new MarshmistTitanCostReductionEffect(this);
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/r/RekindledFlame.java b/Mage.Sets/src/mage/cards/r/RekindledFlame.java
index b294ef6821..a5592bde47 100644
--- a/Mage.Sets/src/mage/cards/r/RekindledFlame.java
+++ b/Mage.Sets/src/mage/cards/r/RekindledFlame.java
@@ -1,30 +1,26 @@
-
 package mage.cards.r;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
-import mage.abilities.condition.Condition;
+import mage.abilities.condition.common.OpponentHasNoCardsInHandCondition;
 import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
 import mage.abilities.effects.common.DamageTargetEffect;
 import mage.abilities.effects.common.ReturnSourceFromGraveyardToHandEffect;
+import mage.abilities.hint.ConditionHint;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.TargetController;
 import mage.constants.Zone;
-import mage.game.Game;
-import mage.players.Player;
 import mage.target.common.TargetAnyTarget;
 
+import java.util.UUID;
+
 /**
- *
  * @author jeffwadsworth
  */
 public final class RekindledFlame extends CardImpl {
 
-    static final String rule = "if an opponent has no cards in hand, you may return Rekindled Flame from your graveyard to your hand";
-
     public RekindledFlame(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}{R}");
 
@@ -34,10 +30,10 @@ public final class RekindledFlame extends CardImpl {
 
         // At the beginning of your upkeep, if an opponent has no cards in hand, you may return Rekindled Flame from your graveyard to your hand.
         Ability ability = new ConditionalInterveningIfTriggeredAbility(
-                new BeginningOfUpkeepTriggeredAbility(
-                        Zone.GRAVEYARD, new ReturnSourceFromGraveyardToHandEffect(), TargetController.YOU, true
-                ),
-                new OpponentHasNoCardsInHandCondition(), rule);
+                new BeginningOfUpkeepTriggeredAbility(Zone.GRAVEYARD, new ReturnSourceFromGraveyardToHandEffect(), TargetController.YOU, true),
+                OpponentHasNoCardsInHandCondition.instance,
+                "If an opponent has no cards in hand, you may return Rekindled Flame from your graveyard to your hand.");
+        ability.addHint(new ConditionHint(OpponentHasNoCardsInHandCondition.instance, "Opponent has no cards in hand"));
         ability.setRuleVisible(true);
         this.addAbility(ability);
 
@@ -51,21 +47,4 @@ public final class RekindledFlame extends CardImpl {
     public RekindledFlame copy() {
         return new RekindledFlame(this);
     }
-}
-
-class OpponentHasNoCardsInHandCondition implements Condition {
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        Player player = game.getPlayer(source.getControllerId());
-        if (player != null) {
-            for (UUID playerId : game.getOpponents(source.getControllerId())) {
-                Player opponent = game.getPlayer(playerId);
-                if (opponent != null && opponent.getHand().isEmpty()) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-}
+}
\ No newline at end of file
diff --git a/Mage/src/main/java/mage/abilities/condition/common/OpponentHasNoCardsInHandCondition.java b/Mage/src/main/java/mage/abilities/condition/common/OpponentHasNoCardsInHandCondition.java
new file mode 100644
index 0000000000..3eaed97bd9
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/condition/common/OpponentHasNoCardsInHandCondition.java
@@ -0,0 +1,36 @@
+package mage.abilities.condition.common;
+
+import mage.abilities.Ability;
+import mage.abilities.condition.Condition;
+import mage.game.Game;
+import mage.players.Player;
+
+import java.util.UUID;
+
+/**
+ * @author JayDi85
+ */
+
+public enum OpponentHasNoCardsInHandCondition implements Condition {
+
+    instance;
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Player player = game.getPlayer(source.getControllerId());
+        if (player != null) {
+            for (UUID playerId : game.getOpponents(source.getControllerId())) {
+                Player opponent = game.getPlayer(playerId);
+                if (opponent != null && opponent.getHand().isEmpty()) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return "an opponent has no cards in hand";
+    }
+}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/AffinityEffect.java b/Mage/src/main/java/mage/abilities/effects/common/AffinityEffect.java
index f3d6e14290..617a18934e 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/AffinityEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/AffinityEffect.java
@@ -1,6 +1,5 @@
 package mage.abilities.effects.common;
 
-import mage.Mana;
 import mage.abilities.Ability;
 import mage.abilities.SpellAbility;
 import mage.abilities.effects.common.cost.CostModificationEffectImpl;
@@ -9,9 +8,10 @@ import mage.constants.Duration;
 import mage.constants.Outcome;
 import mage.filter.common.FilterControlledPermanent;
 import mage.game.Game;
+import mage.util.CardUtil;
 
 public class AffinityEffect extends CostModificationEffectImpl {
-    
+
     private final FilterControlledPermanent filter;
 
     public AffinityEffect(FilterControlledPermanent affinityFilter) {
@@ -27,20 +27,12 @@ public class AffinityEffect extends CostModificationEffectImpl {
 
     @Override
     public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        SpellAbility spellAbility = (SpellAbility)abilityToModify;
-        Mana mana = spellAbility.getManaCostsToPay().getMana();
-        if (mana.getGeneric() > 0) {
-            // the following works with Sen Triplets and in multiplayer games
-            int count = game.getBattlefield().getActivePermanents(filter, abilityToModify.getControllerId(), source.getId(), game).size();
-            int newCount = mana.getGeneric() - count;
-            if (newCount < 0) {
-                newCount = 0;
-            }
-            mana.setGeneric(newCount);
-            spellAbility.getManaCostsToPay().load(mana.toString());
-            return true;
+        // abilityToModify.getControllerId() works with Sen Triplets and in multiplayer games, see https://github.com/magefree/mage/issues/5931
+        int count = game.getBattlefield().getActivePermanents(filter, abilityToModify.getControllerId(), source.getId(), game).size();
+        if (count > 0) {
+            CardUtil.reduceCost(abilityToModify, count);
         }
-        return false;
+        return true;
     }
 
     @Override
diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionSourceEffect.java
index e280df5a06..0f535c1a7f 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionSourceEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionSourceEffect.java
@@ -5,6 +5,8 @@ import mage.abilities.SpellAbility;
 import mage.abilities.condition.Condition;
 import mage.abilities.costs.mana.ManaCost;
 import mage.abilities.costs.mana.ManaCosts;
+import mage.abilities.dynamicvalue.DynamicValue;
+import mage.abilities.dynamicvalue.common.StaticValue;
 import mage.constants.CostModificationType;
 import mage.constants.Duration;
 import mage.constants.Outcome;
@@ -16,7 +18,7 @@ import mage.util.CardUtil;
  */
 public class SpellCostReductionSourceEffect extends CostModificationEffectImpl {
 
-    private final int amount;
+    private final DynamicValue amount;
     private ManaCosts<ManaCost> manaCostsToReduce = null;
     private Condition condition;
 
@@ -26,7 +28,7 @@ public class SpellCostReductionSourceEffect extends CostModificationEffectImpl {
 
     public SpellCostReductionSourceEffect(ManaCosts<ManaCost> manaCostsToReduce, Condition condition) {
         super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST);
-        this.amount = 0;
+        this.amount = StaticValue.get(0);
         this.manaCostsToReduce = manaCostsToReduce;
         this.condition = condition;
 
@@ -44,19 +46,30 @@ public class SpellCostReductionSourceEffect extends CostModificationEffectImpl {
     }
 
     public SpellCostReductionSourceEffect(int amount) {
+        this(StaticValue.get(amount), null);
+    }
+
+    public SpellCostReductionSourceEffect(DynamicValue amount) {
         this(amount, null);
     }
 
     public SpellCostReductionSourceEffect(int amount, Condition condition) {
+        this(StaticValue.get(amount), condition);
+    }
+
+    public SpellCostReductionSourceEffect(DynamicValue amount, Condition condition) {
         super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST);
         this.amount = amount;
         this.condition = condition;
         StringBuilder sb = new StringBuilder();
-        sb.append("this spell costs {").append(amount).append("} less to cast");
+        sb.append("this spell costs {").append(this.amount).append("} less to cast");
         if (this.condition != null) {
             sb.append(" ").append(this.condition.toString().startsWith("if ") ? "" : "if ");
             sb.append(this.condition.toString());
         }
+        if (this.amount.toString().equals("X")) {
+            sb.append(", where {X} is ").append(this.amount.getMessage());
+        }
         this.staticText = sb.toString();
     }
 
@@ -72,7 +85,7 @@ public class SpellCostReductionSourceEffect extends CostModificationEffectImpl {
         if (manaCostsToReduce != null) {
             CardUtil.adjustCost((SpellAbility) abilityToModify, manaCostsToReduce, false);
         } else {
-            CardUtil.reduceCost(abilityToModify, this.amount);
+            CardUtil.reduceCost(abilityToModify, this.amount.calculate(game, source, this));
         }
         return true;
     }
diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionSourceForOpponentsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionSourceForOpponentsEffect.java
index 4391755fa3..25b3ce931f 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionSourceForOpponentsEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionSourceForOpponentsEffect.java
@@ -1,12 +1,12 @@
 package mage.abilities.effects.common.cost;
 
-import mage.Mana;
 import mage.abilities.Ability;
 import mage.abilities.SpellAbility;
 import mage.constants.CostModificationType;
 import mage.constants.Duration;
 import mage.constants.Outcome;
 import mage.game.Game;
+import mage.util.CardUtil;
 
 public class SpellCostReductionSourceForOpponentsEffect extends CostModificationEffectImpl {
 
@@ -25,19 +25,11 @@ public class SpellCostReductionSourceForOpponentsEffect extends CostModification
 
     @Override
     public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        SpellAbility spellAbility = (SpellAbility) abilityToModify;
-        Mana mana = spellAbility.getManaCostsToPay().getMana();
-        if (mana.getGeneric() > 0) {
-            int count = game.getOpponents(source.getControllerId()).size();
-            int newCount = mana.getGeneric() - count;
-            if (newCount < 0) {
-                newCount = 0;
-            }
-            mana.setGeneric(newCount);
-            spellAbility.getManaCostsToPay().load(mana.toString());
-            return true;
+        int count = game.getOpponents(source.getControllerId()).size();
+        if (count > 0) {
+            CardUtil.reduceCost(abilityToModify, count);
         }
-        return false;
+        return true;
     }
 
     @Override

From 520d75dba9cddc814a737dd5670257e3a2f9cd57 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Sat, 27 Jun 2020 05:40:45 +0400
Subject: [PATCH 536/586] * Cost reduction effects - fixed that some cards with
 cost reduction can't be played (example: Price of Fame, see #6685, #6684);

---
 .../src/mage/cards/m/MysticalDispute.java     |  7 ++--
 .../src/mage/cards/n/NotOfThisWorld.java      | 11 +++---
 Mage.Sets/src/mage/cards/p/PriceOfFame.java   |  5 ++-
 Mage.Sets/src/mage/cards/s/SavageStomp.java   |  5 ++-
 .../CostReduceWithConditionTest.java          | 33 ++++++++++++++---
 .../cost/CostModificationEffectImpl.java      | 37 ++++++++++++-------
 .../cost/SpellCostReductionSourceEffect.java  |  4 +-
 7 files changed, 70 insertions(+), 32 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/m/MysticalDispute.java b/Mage.Sets/src/mage/cards/m/MysticalDispute.java
index e290974dad..61c8d08775 100644
--- a/Mage.Sets/src/mage/cards/m/MysticalDispute.java
+++ b/Mage.Sets/src/mage/cards/m/MysticalDispute.java
@@ -1,7 +1,5 @@
 package mage.cards.m;
 
-import java.util.Collection;
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.condition.Condition;
@@ -17,6 +15,9 @@ import mage.game.stack.StackObject;
 import mage.target.Target;
 import mage.target.TargetSpell;
 
+import java.util.Collection;
+import java.util.UUID;
+
 /**
  * @author TheElk801
  */
@@ -27,7 +28,7 @@ public final class MysticalDispute extends CardImpl {
 
         // This spell costs {2} less to cast if it targets a blue spell.
         this.addAbility(new SimpleStaticAbility(
-                Zone.ALL, new SpellCostReductionSourceEffect(2, MysticalDisputeCondition.instance)
+                Zone.ALL, new SpellCostReductionSourceEffect(2, MysticalDisputeCondition.instance).setCanWorksOnStackOnly(true)
         ).setRuleAtTheTop(true));
 
         // Counter target spell unless its controller pays {3}.
diff --git a/Mage.Sets/src/mage/cards/n/NotOfThisWorld.java b/Mage.Sets/src/mage/cards/n/NotOfThisWorld.java
index aa0fe9ebdb..df8dde261f 100644
--- a/Mage.Sets/src/mage/cards/n/NotOfThisWorld.java
+++ b/Mage.Sets/src/mage/cards/n/NotOfThisWorld.java
@@ -1,7 +1,5 @@
 package mage.cards.n;
 
-import java.util.Collection;
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.Mode;
 import mage.abilities.common.SimpleStaticAbility;
@@ -21,6 +19,9 @@ import mage.game.stack.StackObject;
 import mage.target.Target;
 import mage.target.TargetStackObject;
 
+import java.util.Collection;
+import java.util.UUID;
+
 /**
  * @author Rafbill
  */
@@ -41,7 +42,7 @@ public final class NotOfThisWorld extends CardImpl {
         this.getSpellAbility().addTarget(new TargetStackObject(filter));
 
         // Not of This World costs {7} less to cast if it targets a spell or ability that targets a creature you control with power 7 or greater.
-        this.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(7, NotOfThisWorldCondition.instance)));
+        this.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(7, NotOfThisWorldCondition.instance).setCanWorksOnStackOnly(true)));
     }
 
     private NotOfThisWorld(final NotOfThisWorld card) {
@@ -86,8 +87,8 @@ enum NotOfThisWorldCondition implements Condition {
                 .flatMap(Collection::stream)
                 .map(game::getPermanentOrLKIBattlefield)
                 .anyMatch(permanent -> permanent != null && filter.match(
-                permanent, sourceSpell.getSourceId(), sourceSpell.getControllerId(), game
-        ));
+                        permanent, sourceSpell.getSourceId(), sourceSpell.getControllerId(), game
+                ));
     }
 
     @Override
diff --git a/Mage.Sets/src/mage/cards/p/PriceOfFame.java b/Mage.Sets/src/mage/cards/p/PriceOfFame.java
index 68c180beaa..be37a6e773 100644
--- a/Mage.Sets/src/mage/cards/p/PriceOfFame.java
+++ b/Mage.Sets/src/mage/cards/p/PriceOfFame.java
@@ -1,6 +1,5 @@
 package mage.cards.p;
 
-import java.util.UUID;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.condition.Condition;
 import mage.abilities.condition.common.SourceTargetsPermanentCondition;
@@ -16,6 +15,8 @@ import mage.filter.FilterPermanent;
 import mage.filter.common.FilterCreaturePermanent;
 import mage.target.common.TargetCreaturePermanent;
 
+import java.util.UUID;
+
 /**
  * @author TheElk801
  */
@@ -35,7 +36,7 @@ public final class PriceOfFame extends CardImpl {
 
         // This spell costs {2} less to cast if it targets a legendary creature.
         this.addAbility(new SimpleStaticAbility(
-                Zone.ALL, new SpellCostReductionSourceEffect(2, condition)
+                Zone.ALL, new SpellCostReductionSourceEffect(2, condition).setCanWorksOnStackOnly(true)
         ).setRuleAtTheTop(true));
 
         // Destroy target creature.
diff --git a/Mage.Sets/src/mage/cards/s/SavageStomp.java b/Mage.Sets/src/mage/cards/s/SavageStomp.java
index 26db6aba9a..d2a8f044d1 100644
--- a/Mage.Sets/src/mage/cards/s/SavageStomp.java
+++ b/Mage.Sets/src/mage/cards/s/SavageStomp.java
@@ -1,6 +1,5 @@
 package mage.cards.s;
 
-import java.util.UUID;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.condition.Condition;
 import mage.abilities.condition.common.SourceTargetsPermanentCondition;
@@ -20,6 +19,8 @@ import mage.filter.common.FilterControlledCreaturePermanent;
 import mage.target.common.TargetControlledCreaturePermanent;
 import mage.target.common.TargetCreaturePermanent;
 
+import java.util.UUID;
+
 /**
  * @author TheElk801
  */
@@ -35,7 +36,7 @@ public final class SavageStomp extends CardImpl {
 
         // Savage Stomp costs {2} less to cast if it targets a Dinosaur you control.
         this.addAbility(new SimpleStaticAbility(
-                Zone.ALL, new SpellCostReductionSourceEffect(2, condition)
+                Zone.ALL, new SpellCostReductionSourceEffect(2, condition).setCanWorksOnStackOnly(true)
         ).setRuleAtTheTop(true));
 
         // Put a +1/+1 counter on target creature you control. Then that creature fights target creature you don't control.
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostReduceWithConditionTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostReduceWithConditionTest.java
index cd733ed0f8..7ab2978d83 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostReduceWithConditionTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostReduceWithConditionTest.java
@@ -2,14 +2,13 @@ package org.mage.test.cards.cost.modification;
 
 import mage.constants.PhaseStep;
 import mage.constants.Zone;
-import org.junit.Ignore;
 import org.junit.Test;
-import org.mage.test.serverside.base.CardTestPlayerBase;
+import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps;
 
 /**
  * @author JayDi85
  */
-public class CostReduceWithConditionTest extends CardTestPlayerBase {
+public class CostReduceWithConditionTest extends CardTestPlayerBaseWithAIHelps {
 
     @Test
     public void test_PriceOfFame_Normal() {
@@ -31,9 +30,9 @@ public class CostReduceWithConditionTest extends CardTestPlayerBase {
     }
 
     @Test
-    @Ignore
-    // TODO: implement workaround like putToStackAsNonPlayable for abilities, see https://github.com/magefree/mage/issues/6685
-    public void test_PriceOfFame_Reduce() {
+    public void test_PriceOfFame_Reduce_Manual() {
+        // https://github.com/magefree/mage/issues/6685
+
         // {3}{B}
         // This spell costs {2} less to cast if it targets a legendary creature.
         // Destroy target creature.
@@ -50,4 +49,26 @@ public class CostReduceWithConditionTest extends CardTestPlayerBase {
 
         assertGraveyardCount(playerB, "Anje Falkenrath", 1);
     }
+
+    @Test
+    public void test_PriceOfFame_Reduce_AI() {
+        // https://github.com/magefree/mage/issues/6685
+
+        // {3}{B}
+        // This spell costs {2} less to cast if it targets a legendary creature.
+        // Destroy target creature.
+        addCard(Zone.HAND, playerA, "Price of Fame", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4 - 2);
+        addCard(Zone.BATTLEFIELD, playerB, "Anje Falkenrath", 1);
+
+        // AI must see and play that cards too
+        aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA);
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        assertGraveyardCount(playerB, "Anje Falkenrath", 1);
+    }
 }
diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/CostModificationEffectImpl.java b/Mage/src/main/java/mage/abilities/effects/common/cost/CostModificationEffectImpl.java
index c043b741f9..53af0aca6b 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/cost/CostModificationEffectImpl.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/cost/CostModificationEffectImpl.java
@@ -1,27 +1,28 @@
-
-
 package mage.abilities.effects.common.cost;
 
-import mage.constants.Duration;
-import mage.constants.EffectType;
-import mage.constants.Outcome;
 import mage.abilities.Ability;
 import mage.abilities.effects.ContinuousEffectImpl;
 import mage.abilities.effects.CostModificationEffect;
 import mage.constants.CostModificationType;
+import mage.constants.Duration;
+import mage.constants.EffectType;
+import mage.constants.Outcome;
 import mage.game.Game;
 
 /**
  * Simple implementation of a {@link CostModificationEffect} offering simplified
  * construction to setup the object for use by the mage framework.
-
+ *
  * @author maurer.it_at_gmail.com
  */
 public abstract class CostModificationEffectImpl extends ContinuousEffectImpl implements CostModificationEffect {
 
     private final CostModificationType modificationType;
-    
-    public CostModificationEffectImpl ( Duration duration, Outcome outcome, CostModificationType type) {
+
+    // if effect need real stack object to check then mark it as stack only (example: apply cost reduction if you target human creature)
+    private boolean worksOnStackOnly = false;
+
+    public CostModificationEffectImpl(Duration duration, Outcome outcome, CostModificationType type) {
         super(duration, outcome);
         this.effectType = EffectType.COSTMODIFICATION;
         this.modificationType = type;
@@ -30,23 +31,33 @@ public abstract class CostModificationEffectImpl extends ContinuousEffectImpl im
     public CostModificationEffectImpl(final CostModificationEffectImpl effect) {
         super(effect);
         this.modificationType = effect.modificationType;
+        this.worksOnStackOnly = effect.worksOnStackOnly;
     }
 
     /**
      * Overridden and 'no-op' implementation put in place.
      *
-     * @see #apply(mage.game.Game, mage.abilities.Ability, mage.abilities.Ability)
-     *
      * @param game
      * @param source
      * @return
+     * @see #apply(mage.game.Game, mage.abilities.Ability, mage.abilities.Ability)
      */
     @Override
-    public final boolean apply ( Game game, Ability source ) { return false; }
-       
+    public final boolean apply(Game game, Ability source) {
+        return false;
+    }
+
     @Override
-    public CostModificationType getModificationType(){
+    public CostModificationType getModificationType() {
         return this.modificationType;
     }
 
+    public CostModificationEffectImpl setCanWorksOnStackOnly(boolean worksOnStackOnly) {
+        this.worksOnStackOnly = worksOnStackOnly;
+        return this;
+    }
+
+    public boolean canWorksOnStackOnly() {
+        return this.worksOnStackOnly;
+    }
 }
diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionSourceEffect.java
index 0f535c1a7f..a1910b5116 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionSourceEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionSourceEffect.java
@@ -93,7 +93,9 @@ public class SpellCostReductionSourceEffect extends CostModificationEffectImpl {
     @Override
     public boolean applies(Ability abilityToModify, Ability source, Game game) {
         if (abilityToModify.getSourceId().equals(source.getSourceId()) && (abilityToModify instanceof SpellAbility)) {
-            return condition == null || condition.apply(game, source);
+            // some conditions can works after put on stack, so skip it in get playable (allows user to put card on stack anyway)
+            boolean skipCondition = game.inCheckPlayableState() && canWorksOnStackOnly();
+            return condition == null || skipCondition || condition.apply(game, source);
         }
         return false;
     }

From aeec6a4e8d4ad8ae3c58575f19804949419e9ca6 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Sat, 27 Jun 2020 06:00:48 +0400
Subject: [PATCH 537/586] Additional fix for #6698, card hint for Of One Mind;

---
 Mage.Sets/src/mage/cards/m/MoxAmber.java     | 15 +++++++--------
 Mage.Sets/src/mage/cards/o/OfOneMind.java    | 11 +++++++----
 Mage.Sets/src/mage/cards/t/TitanicBrawl.java |  5 +++--
 3 files changed, 17 insertions(+), 14 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/m/MoxAmber.java b/Mage.Sets/src/mage/cards/m/MoxAmber.java
index ae6d86bd9d..83093f734d 100644
--- a/Mage.Sets/src/mage/cards/m/MoxAmber.java
+++ b/Mage.Sets/src/mage/cards/m/MoxAmber.java
@@ -1,7 +1,5 @@
-
 package mage.cards.m;
 
-import java.util.UUID;
 import mage.abilities.mana.AnyColorPermanentTypesManaAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -11,24 +9,25 @@ import mage.constants.TargetController;
 import mage.filter.FilterPermanent;
 import mage.filter.predicate.Predicates;
 
+import java.util.UUID;
+
 /**
- *
  * @author CountAndromalius
  */
 public final class MoxAmber extends CardImpl {
 
     public MoxAmber(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{0}");
+        super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{0}");
         addSuperType(SuperType.LEGENDARY);
 
-        // {tap}: Add one mana pool of any color among legendary creatures and planeswalkers you control.
+        // {T}: Add one mana pool of any color among legendary creatures and planeswalkers you control.
         FilterPermanent filter = new FilterPermanent("legendary creatures and planeswalkers");
         filter.add(Predicates.or(
                 Predicates.and(
-                    CardType.CREATURE.getPredicate(),
-                    SuperType.LEGENDARY.getPredicate()
+                        CardType.CREATURE.getPredicate(),
+                        SuperType.LEGENDARY.getPredicate()
                 ),
-            CardType.PLANESWALKER.getPredicate())
+                CardType.PLANESWALKER.getPredicate())
         );
         this.addAbility(new AnyColorPermanentTypesManaAbility(TargetController.YOU, filter));
     }
diff --git a/Mage.Sets/src/mage/cards/o/OfOneMind.java b/Mage.Sets/src/mage/cards/o/OfOneMind.java
index f81bb90277..a38184af0d 100644
--- a/Mage.Sets/src/mage/cards/o/OfOneMind.java
+++ b/Mage.Sets/src/mage/cards/o/OfOneMind.java
@@ -1,12 +1,12 @@
 package mage.cards.o;
 
-import java.util.UUID;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.condition.CompoundCondition;
 import mage.abilities.condition.Condition;
 import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect;
+import mage.abilities.hint.ConditionHint;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
@@ -16,6 +16,8 @@ import mage.filter.FilterPermanent;
 import mage.filter.common.FilterControlledCreaturePermanent;
 import mage.filter.predicate.Predicates;
 
+import java.util.UUID;
+
 /**
  * @author TheElk801
  */
@@ -38,9 +40,10 @@ public final class OfOneMind extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}");
 
         // This spell costs {2} less to cast if you control a Human creature and a non-Human creature.
-        this.addAbility(new SimpleStaticAbility(
-                Zone.ALL, new SpellCostReductionSourceEffect(2, condition)
-        ).setRuleAtTheTop(true));
+        this.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(2, condition))
+                .setRuleAtTheTop(true)
+                .addHint(new ConditionHint(condition, "You control a Human creature and a non-Human creature"))
+        );
 
         // Draw two cards.
         this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2));
diff --git a/Mage.Sets/src/mage/cards/t/TitanicBrawl.java b/Mage.Sets/src/mage/cards/t/TitanicBrawl.java
index 54f079db20..c23972f76e 100644
--- a/Mage.Sets/src/mage/cards/t/TitanicBrawl.java
+++ b/Mage.Sets/src/mage/cards/t/TitanicBrawl.java
@@ -1,6 +1,5 @@
 package mage.cards.t;
 
-import java.util.UUID;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.condition.Condition;
 import mage.abilities.condition.common.SourceTargetsPermanentCondition;
@@ -18,6 +17,8 @@ import mage.filter.predicate.permanent.CounterPredicate;
 import mage.target.common.TargetControlledCreaturePermanent;
 import mage.target.common.TargetCreaturePermanent;
 
+import java.util.UUID;
+
 /**
  * @author TheElk801
  */
@@ -37,7 +38,7 @@ public final class TitanicBrawl extends CardImpl {
 
         // This spell costs {1} less to cast if it targets a creature you control with a +1/+1 counter on it.
         this.addAbility(new SimpleStaticAbility(
-                Zone.ALL, new SpellCostReductionSourceEffect(1, condition)
+                Zone.ALL, new SpellCostReductionSourceEffect(1, condition).setCanWorksOnStackOnly(true)
         ).setRuleAtTheTop(true));
 
         // Target creature you control fights target creature you don't control.

From 81d0442ba135a0293d5466f39db17d0e0403a145 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Sat, 27 Jun 2020 10:47:27 +0200
Subject: [PATCH 538/586] * Added some tests for restoring objects after
 rollback.

---
 .../mage/test/rollback/StateValuesTest.java   | 80 ++++++++++++++++++-
 1 file changed, 79 insertions(+), 1 deletion(-)

diff --git a/Mage.Tests/src/test/java/org/mage/test/rollback/StateValuesTest.java b/Mage.Tests/src/test/java/org/mage/test/rollback/StateValuesTest.java
index 0ec79277b6..c751d8799e 100644
--- a/Mage.Tests/src/test/java/org/mage/test/rollback/StateValuesTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/rollback/StateValuesTest.java
@@ -1,6 +1,7 @@
-
 package org.mage.test.rollback;
 
+import mage.abilities.keyword.FirstStrikeAbility;
+import mage.abilities.keyword.FlyingAbility;
 import mage.constants.PhaseStep;
 import mage.constants.Zone;
 import org.junit.Test;
@@ -65,4 +66,81 @@ public class StateValuesTest extends CardTestPlayerBase {
         assertPermanentCount(playerA, "Clue", 2);
 
     }
+
+    @Test
+    public void rollbackTokenCreationTest() {
+        // Create two 1/1 white Bird creature tokens with flying.
+        // Flashback—Tap three untapped white creatures you control. (You may cast this card from your graveyard for its flashback cost. Then exile it.)
+        addCard(Zone.LIBRARY, playerA, "Battle Screech", 1); // Sorcery {2}{W}{W}
+        skipInitShuffling();
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 4);
+        addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1);
+
+        castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Battle Screech");
+
+        activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Flashback");
+        setChoice(playerA, "Bird");
+        setChoice(playerA, "Bird");
+        setChoice(playerA, "Silvercoat Lion");
+
+        setStrictChooseMode(true);
+        setStopAt(3, PhaseStep.POSTCOMBAT_MAIN);
+
+        execute();
+        assertAllCommandsUsed();
+
+        // Before rollback
+        assertTappedCount("Plains", true, 4);
+        assertTappedCount("Bird", true, 2);
+        assertTappedCount("Silvercoat Lion", true, 1);
+        assertPermanentCount(playerA, "Bird", 4);
+        assertHandCount(playerA, 0);
+        assertExileCount(playerA, "Battle Screech", 1);
+
+        currentGame.rollbackTurns(2);
+
+        // After rollback to turn 1
+        assertTappedCount("Plains", true, 0);
+        assertTappedCount("Silvercoat Lion", true, 0);
+        assertPermanentCount(playerA, "Bird", 0);
+        assertLibraryCount(playerA, "Battle Screech", 1);
+        assertHandCount(playerA, 0);
+    }
+
+    @Test
+    public void rollbackBuffUntilEndOfTurnTest() {
+        // Target creature gets +1/+1 and gains flying and first strike until end of turn.
+        addCard(Zone.LIBRARY, playerA, "Aerial Maneuver", 1); // Instant {1}{W}
+        skipInitShuffling();
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
+        addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1);
+
+        castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Aerial Maneuver", "Silvercoat Lion");
+
+        setStrictChooseMode(true);
+        setStopAt(3, PhaseStep.BEGIN_COMBAT);
+
+        execute();
+        assertAllCommandsUsed();
+
+        // Before rollback
+        assertTappedCount("Plains", true, 2);
+        assertPermanentCount(playerA, "Silvercoat Lion", 1);
+        assertPowerToughness(playerA, "Silvercoat Lion", 3, 3);
+        assertAbility(playerA, "Silvercoat Lion", FlyingAbility.getInstance(), true);
+        assertAbility(playerA, "Silvercoat Lion", FirstStrikeAbility.getInstance(), true);
+        assertHandCount(playerA, 0);
+        assertGraveyardCount(playerA, "Aerial Maneuver", 1);
+
+        currentGame.rollbackTurns(0);
+
+        // After rollback to begin turn 3
+        assertTappedCount("Plains", true, 0);
+        assertPermanentCount(playerA, "Silvercoat Lion", 1);
+        assertPowerToughness(playerA, "Silvercoat Lion", 2, 2);
+        assertAbility(playerA, "Silvercoat Lion", FlyingAbility.getInstance(), false);
+        assertAbility(playerA, "Silvercoat Lion", FirstStrikeAbility.getInstance(), false);
+        assertLibraryCount(playerA, "Aerial Maneuver", 1);
+        assertHandCount(playerA, 0);
+    }
 }

From 2c745109e4e110771710f005fe0385894d03b7b0 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Sat, 27 Jun 2020 10:48:39 +0200
Subject: [PATCH 539/586] * Added test for Reyhan, Last of the Abzan related to
 (#6688).

---
 Mage.Tests/ReyhanCommanderDeck.dck            | 65 +++++++++++++
 .../duel/CastBGPartnerCommanderTest.java      | 97 +++++++++++++++++++
 2 files changed, 162 insertions(+)
 create mode 100644 Mage.Tests/ReyhanCommanderDeck.dck
 create mode 100644 Mage.Tests/src/test/java/org/mage/test/commander/duel/CastBGPartnerCommanderTest.java

diff --git a/Mage.Tests/ReyhanCommanderDeck.dck b/Mage.Tests/ReyhanCommanderDeck.dck
new file mode 100644
index 0000000000..2da832153e
--- /dev/null
+++ b/Mage.Tests/ReyhanCommanderDeck.dck
@@ -0,0 +1,65 @@
+2 [C18:134] Budoka Gardener
+2 [C18:136] Consign to Dust
+1 [C18:129] Avenger of Zendikar
+1 [C18:29] Crash of Rhino Beetles
+1 [C18:119] Stitch Together
+1 [C18:116] Retreat to Hagra
+1 [C18:16] Loyal Subordinate
+1 [C18:15] Entreat the Dead
+1 [C18:117] Ruinous Path
+1 [C18:114] Moonlight Bargain
+1 [C18:18] Skull Storm
+1 [C18:115] Phyrexian Delver
+1 [C18:14] Bloodtracker
+1 [C18:19] Sower of Discord
+1 [C18:17] Night Incarnate
+1 [C18:113] Army of the Damned
+1 [C18:118] Soul of Innistrad
+1 [C18:197] Blinkmoth Urn
+1 [C18:54] Coveted Jewel
+1 [C18:199] Chief of the Foundry
+1 [C18:217] Psychosis Crawler
+1 [C18:202] Darksteel Juggernaut
+1 [C18:226] Thopter Assembly
+1 [C18:224] Steel Hellkite
+1 [C18:222] Sol Ring
+1 [C18:219] Scuttling Doom Engine
+1 [C18:205] Duplicant
+1 [C18:212] Myr Battlesphere
+1 [C18:53] Ancient Stone Idol
+1 [C18:223] Soul of New Phyrexia
+1 [C18:160] Sakura-Tribe Elder
+1 [C18:133] Borderland Explorer
+1 [C18:146] Farhaven Elf
+1 [C18:128] Aura Gnarlid
+1 [C18:166] Yavimaya Elder
+1 [C18:167] Yavimaya Enchantress
+1 [C18:161] Scute Mob
+1 [C18:151] Herald of the Pantheon
+1 [C18:35] Turntimber Sower
+1 [C18:34] Ravenous Slime
+1 [C18:140] Eidolon of Blossoms
+1 [C18:135] Centaur Vinecrasher
+1 [C18:156] Moldgraf Monstrosity
+1 [C18:149] Ground Seal
+1 [C18:147] Fertile Ground
+1 [C18:162] Snake Umbra
+1 [C18:157] Overgrowth
+1 [C18:150] Harrow
+1 [C18:138] Cultivate
+1 [C18:164] Vow of Wildness
+1 [C18:152] Hunting Wilds
+1 [C18:144] Explosive Vegetation
+1 [C18:131] Bear Umbra
+1 [C18:32] Myth Unbound
+1 [C18:141] Enchantress's Presence
+1 [C18:142] Epic Proportions
+1 [C18:137] Creeping Renaissance
+1 [C18:30] Genesis Storm
+1 [C18:163] Spawning Grounds
+25 [PPP1:5] Forest
+12 [PPP1:3] Swamp
+SB: 1 [CM2:13] Reyhan, Last of the Abzan
+SB: 1 [CM2:11] Ikra Shidiqi, the Usurper
+LAYOUT MAIN:(2,11)(CMC,true,29)|()()([C18:134])([C18:16],[C18:199])([C18:14])([C18:29],[C18:115],[C18:17],[C18:217],[C18:202],[C18:161],[C18:151],[C18:134],[C18:35],[C18:140],[C18:135],[C18:156])([C18:19],[C18:118],[C18:226],[C18:224],[C18:219],[C18:205],[C18:223],[C18:149],[C18:147],[C18:162],[C18:34],[C18:157],[C18:150],[C18:138],[C18:164],[C18:152],[C18:144],[C18:136],[C18:131],[C18:32],[C18:141],[C18:142],[C18:137],[C18:30],[C18:163])([C18:129],[C18:212],[C18:160],[C18:133],[C18:146],[C18:128],[C18:166],[C18:167])()()([C18:53])([PPP1:5],[PPP1:5],[PPP1:5],[PPP1:5],[PPP1:5],[PPP1:5],[PPP1:5],[PPP1:5],[PPP1:5],[PPP1:5],[PPP1:5],[PPP1:5],[PPP1:5],[PPP1:5],[PPP1:5],[PPP1:5],[PPP1:5],[PPP1:5],[PPP1:5],[PPP1:5],[PPP1:5],[PPP1:5],[PPP1:5],[PPP1:5],[PPP1:5],[PPP1:3],[PPP1:3],[PPP1:3],[PPP1:3],[PPP1:3],[PPP1:3],[PPP1:3],[PPP1:3],[PPP1:3],[PPP1:3],[PPP1:3],[PPP1:3])([C18:222])([C18:119])([C18:136],[C18:116],[C18:15],[C18:117])()([C18:114],[C18:197])([C18:54])()([C18:113])([C18:18])()
+LAYOUT SIDEBOARD:(1,2)(RARITY,true,0)|([CM2:13])([CM2:11])
diff --git a/Mage.Tests/src/test/java/org/mage/test/commander/duel/CastBGPartnerCommanderTest.java b/Mage.Tests/src/test/java/org/mage/test/commander/duel/CastBGPartnerCommanderTest.java
new file mode 100644
index 0000000000..29dca3ceef
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/commander/duel/CastBGPartnerCommanderTest.java
@@ -0,0 +1,97 @@
+package org.mage.test.commander.duel;
+
+import java.io.FileNotFoundException;
+import mage.constants.PhaseStep;
+import mage.constants.Zone;
+import mage.game.Game;
+import mage.game.GameException;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestCommanderDuelBase;
+
+/**
+ *
+ * @author LevelX2 ReyhanCommanderDeck
+ */
+public class CastBGPartnerCommanderTest extends CardTestCommanderDuelBase {
+
+    @Override
+    protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
+        setDecknamePlayerA("ReyhanCommanderDeck.dck");  // Commander Reyhan, Last of the Abzan {1}{B}{G}
+        //                                                         + Ikra Shidiqi, the Usurper
+        setDecknamePlayerB("CommanderDuel_UW.dck"); // Commander = Daxos of Meletis
+        return super.createNewGameAndPlayers();
+    }
+
+    /**
+     * With commander rule changes 6/2020 Reyhan goes to exile first before it
+     * goes to command zone. So it's triggerd ability does no longer move the
+     * counters on it on the battelfield
+     */
+    @Test
+    public void testExileReyhan() {
+        addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion");
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
+
+        // As an additional cost to cast this spell, reveal a colorless creature card from your hand.
+        // Exile target creature if its power is less than or equal to the revealed card's power.
+        addCard(Zone.HAND, playerB, "Titan's Presence", 1);
+        addCard(Zone.HAND, playerB, "Ancient Stone Idol", 1); // Artifact Creature 12/12
+        addCard(Zone.BATTLEFIELD, playerB, "Plains", 3);
+
+        // Reyhan, Last of the Abzan enters the battlefield with three +1/+1 counters on it.
+        // Whenever a creature you control dies or is put into the command zone,
+        // if it had one or more +1/+1 counters on it, you may put that many +1/+1 counters on target creature.
+        // Partner (You can have two commanders if both have partner.)
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Reyhan, Last of the Abzan");
+
+        castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Titan's Presence", "Reyhan, Last of the Abzan");
+        setChoice(playerB, "Ancient Stone Idol");
+
+        setChoice(playerA, "Yes"); // Commander goes to command zone
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertGraveyardCount(playerB, "Titan's Presence", 1);
+        assertCommandZoneCount(playerA, "Reyhan, Last of the Abzan", 1);
+        assertPermanentCount(playerA, "Silvercoat Lion", 1);
+        assertPowerToughness(playerA, "Silvercoat Lion", 2, 2);
+    }
+
+    @Test
+    public void testCastBothPartnerCommanders() {
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 2);
+        addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
+
+        // Reyhan, Last of the Abzan enters the battlefield with three +1/+1 counters on it.
+        // Whenever a creature you control dies or is put into the command zone,
+        // if it had one or more +1/+1 counters on it, you may put that many +1/+1 counters on target creature.
+        // Partner (You can have two commanders if both have partner.)
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Reyhan, Last of the Abzan"); // Creature 0/0
+
+        // Menace
+        // Whenever a creature you control deals combat damage to a player, you gain life equal to that creature's toughness.
+        // Partner
+        castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Ikra Shidiqi, the Usurper"); // Creature 3/7 {3}{B}{G}
+
+        attack(3, playerA, "Reyhan, Last of the Abzan");
+
+        setStrictChooseMode(true);
+        setStopAt(3, PhaseStep.POSTCOMBAT_MAIN);
+        execute();
+
+        assertAllCommandsUsed();
+
+        assertCommandZoneCount(playerA, "Reyhan, Last of the Abzan", 0);
+        assertCommandZoneCount(playerA, "Ikra Shidiqi, the Usurper", 0);
+
+        assertPowerToughness(playerA, "Reyhan, Last of the Abzan", 3, 3);
+        assertPowerToughness(playerA, "Ikra Shidiqi, the Usurper", 3, 7);
+
+        assertLife(playerA, 43);
+        assertLife(playerB, 37);
+    }
+}

From e8dbd2a3c81a60eae1d2c06c417a3fc51b13d45a Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Sat, 27 Jun 2020 19:10:04 +0400
Subject: [PATCH 540/586] Refactor some cost modification effects

---
 .../mage/cards/a/AnimarSoulOfElements.java    | 11 ++--
 .../mage/cards/b/BrighthearthBanneret.java    |  8 +--
 Mage.Sets/src/mage/cards/b/BrineGiant.java    |  4 +-
 Mage.Sets/src/mage/cards/d/DaruWarchief.java  |  8 +--
 .../src/mage/cards/e/EmbalmersTools.java      | 11 ++--
 Mage.Sets/src/mage/cards/e/EyeOfUgin.java     |  6 +--
 Mage.Sets/src/mage/cards/f/Fluctuator.java    | 10 ++--
 .../src/mage/cards/f/FrogtosserBanneret.java  |  8 +--
 Mage.Sets/src/mage/cards/g/GateColossus.java  |  4 +-
 .../src/mage/cards/g/GearseekerSerpent.java   |  4 +-
 .../src/mage/cards/g/GoblinWarchief.java      |  6 +--
 .../mage/cards/g/GrandArbiterAugustinIV.java  | 10 ++--
 Mage.Sets/src/mage/cards/h/Heartstone.java    | 20 ++-----
 Mage.Sets/src/mage/cards/h/HeroOfIroas.java   |  9 ++--
 Mage.Sets/src/mage/cards/h/HumOfTheRadix.java | 15 ++----
 Mage.Sets/src/mage/cards/m/MythUnbound.java   |  6 +--
 .../src/mage/cards/n/NightscapeFamiliar.java  |  8 +--
 .../src/mage/cards/r/RakdosLordOfRiots.java   |  6 +--
 .../src/mage/cards/s/StonybrookBanneret.java  |  8 +--
 .../src/mage/cards/s/StormscapeFamiliar.java  |  8 +--
 .../src/mage/cards/s/SunscapeFamiliar.java    |  8 +--
 .../src/mage/cards/t/TheImmortalSun.java      | 17 +++---
 .../mage/cards/t/ThunderscapeFamiliar.java    |  8 +--
 .../src/mage/cards/u/UndeadWarchief.java      |  8 +--
 .../src/mage/cards/w/WardenOfEvosIsle.java    |  9 ++--
 .../effects/common/AffinityEffect.java        |  4 +-
 .../PlanarDieRollCostIncreasingEffect.java    | 53 +++++++++++++++++++
 ...CostReductionSourceForOpponentsEffect.java |  4 +-
 .../planes/AcademyAtTolariaWestPlane.java     |  1 +
 .../mage/game/command/planes/AgyremPlane.java |  3 +-
 .../mage/game/command/planes/AkoumPlane.java  |  1 +
 .../game/command/planes/AstralArenaPlane.java |  1 +
 .../mage/game/command/planes/BantPlane.java   | 51 ++----------------
 .../command/planes/EdgeOfMalacolPlane.java    |  1 +
 .../command/planes/FeedingGroundsPlane.java   |  1 +
 .../command/planes/FieldsOfSummerPlane.java   |  1 +
 .../planes/HedronFieldsOfAgadeemPlane.java    |  1 +
 .../game/command/planes/LetheLakePlane.java   |  1 +
 .../mage/game/command/planes/NayaPlane.java   |  1 +
 .../game/command/planes/PanopticonPlane.java  |  1 +
 .../mage/game/command/planes/TazeemPlane.java |  1 +
 .../command/planes/TheDarkBaronyPlane.java    |  1 +
 .../game/command/planes/TheEonFogPlane.java   |  1 +
 .../command/planes/TheGreatForestPlane.java   |  1 +
 .../command/planes/TheZephyrMazePlane.java    |  1 +
 .../planes/TrailOfTheMageRingsPlane.java      |  1 +
 .../game/command/planes/TrugaJunglePlane.java |  1 +
 .../game/command/planes/TurriIslandPlane.java |  1 +
 .../command/planes/UndercityReachesPlane.java |  1 +
 49 files changed, 174 insertions(+), 180 deletions(-)
 create mode 100644 Mage/src/main/java/mage/abilities/effects/common/cost/PlanarDieRollCostIncreasingEffect.java

diff --git a/Mage.Sets/src/mage/cards/a/AnimarSoulOfElements.java b/Mage.Sets/src/mage/cards/a/AnimarSoulOfElements.java
index 94e44b7c8e..e47b01a468 100644
--- a/Mage.Sets/src/mage/cards/a/AnimarSoulOfElements.java
+++ b/Mage.Sets/src/mage/cards/a/AnimarSoulOfElements.java
@@ -1,7 +1,5 @@
-
 package mage.cards.a;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.ObjectColor;
 import mage.abilities.Ability;
@@ -21,8 +19,9 @@ import mage.game.Game;
 import mage.game.permanent.Permanent;
 import mage.util.CardUtil;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class AnimarSoulOfElements extends CardImpl {
@@ -73,10 +72,8 @@ class AnimarCostReductionEffect extends CostModificationEffectImpl {
         Permanent sourcePermanent = game.getPermanent(source.getSourceId());
         if (sourcePermanent != null && spellAbility != null) {
             int amount = sourcePermanent.getCounters(game).getCount(CounterType.P1P1);
-            if (amount > 0) {
-                CardUtil.reduceCost(spellAbility, amount);
-                return true;
-            }
+            CardUtil.reduceCost(spellAbility, amount);
+            return true;
         }
         return false;
     }
diff --git a/Mage.Sets/src/mage/cards/b/BrighthearthBanneret.java b/Mage.Sets/src/mage/cards/b/BrighthearthBanneret.java
index 1d6c96aa68..24a0646982 100644
--- a/Mage.Sets/src/mage/cards/b/BrighthearthBanneret.java
+++ b/Mage.Sets/src/mage/cards/b/BrighthearthBanneret.java
@@ -1,7 +1,5 @@
-
 package mage.cards.b;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
@@ -15,8 +13,9 @@ import mage.constants.Zone;
 import mage.filter.FilterCard;
 import mage.filter.predicate.Predicates;
 
+import java.util.UUID;
+
 /**
- *
  * @author North
  */
 public final class BrighthearthBanneret extends CardImpl {
@@ -30,7 +29,7 @@ public final class BrighthearthBanneret extends CardImpl {
     }
 
     public BrighthearthBanneret(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{R}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}");
         this.subtype.add(SubType.ELEMENTAL, SubType.WARRIOR);
 
         this.power = new MageInt(1);
@@ -38,6 +37,7 @@ public final class BrighthearthBanneret extends CardImpl {
 
         // Elemental spells and Warrior spells you cast cost {1} less to cast.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filter, 1)));
+
         // Reinforce 1-{1}{R}
         this.addAbility(new ReinforceAbility(1, new ManaCostsImpl("{1}{R}")));
     }
diff --git a/Mage.Sets/src/mage/cards/b/BrineGiant.java b/Mage.Sets/src/mage/cards/b/BrineGiant.java
index 09942e2ab3..9b02061df9 100644
--- a/Mage.Sets/src/mage/cards/b/BrineGiant.java
+++ b/Mage.Sets/src/mage/cards/b/BrineGiant.java
@@ -67,9 +67,7 @@ class BrineGiantCostReductionEffect extends CostModificationEffectImpl {
     @Override
     public boolean apply(Game game, Ability source, Ability abilityToModify) {
         int count = game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(), game).size();
-        if (count > 0) {
-            CardUtil.reduceCost(abilityToModify, count);
-        }
+        CardUtil.reduceCost(abilityToModify, count);
         return true;
     }
 
diff --git a/Mage.Sets/src/mage/cards/d/DaruWarchief.java b/Mage.Sets/src/mage/cards/d/DaruWarchief.java
index 211b0173aa..abee47c84f 100644
--- a/Mage.Sets/src/mage/cards/d/DaruWarchief.java
+++ b/Mage.Sets/src/mage/cards/d/DaruWarchief.java
@@ -1,7 +1,5 @@
-
 package mage.cards.d;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.common.continuous.BoostControlledEffect;
@@ -15,8 +13,9 @@ import mage.constants.Zone;
 import mage.filter.FilterCard;
 import mage.filter.common.FilterCreaturePermanent;
 
+import java.util.UUID;
+
 /**
- *
  * @author North
  */
 public final class DaruWarchief extends CardImpl {
@@ -30,7 +29,7 @@ public final class DaruWarchief extends CardImpl {
     }
 
     public DaruWarchief(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}{W}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{W}");
         this.subtype.add(SubType.HUMAN);
         this.subtype.add(SubType.SOLDIER);
 
@@ -39,6 +38,7 @@ public final class DaruWarchief extends CardImpl {
 
         // Soldier spells you cast cost {1} less to cast.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filter, 1)));
+
         // Soldier creatures you control get +1/+2.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(1, 2, Duration.WhileOnBattlefield, filterCreatures, false)));
     }
diff --git a/Mage.Sets/src/mage/cards/e/EmbalmersTools.java b/Mage.Sets/src/mage/cards/e/EmbalmersTools.java
index bb7c1284c1..59fa7e2e9a 100644
--- a/Mage.Sets/src/mage/cards/e/EmbalmersTools.java
+++ b/Mage.Sets/src/mage/cards/e/EmbalmersTools.java
@@ -1,8 +1,5 @@
-
 package mage.cards.e;
 
-import java.util.UUID;
-import mage.Mana;
 import mage.abilities.Ability;
 import mage.abilities.ActivatedAbility;
 import mage.abilities.common.SimpleActivatedAbility;
@@ -24,8 +21,9 @@ import mage.target.TargetPlayer;
 import mage.target.common.TargetControlledPermanent;
 import mage.util.CardUtil;
 
+import java.util.UUID;
+
 /**
- *
  * @author fireshoes
  */
 public final class EmbalmersTools extends CardImpl {
@@ -81,10 +79,7 @@ class EmbalmersToolsEffect extends CostModificationEffectImpl {
     public boolean apply(Game game, Ability source, Ability abilityToModify) {
         Player controller = game.getPlayer(abilityToModify.getControllerId());
         if (controller != null) {
-            Mana mana = abilityToModify.getManaCostsToPay().getMana();
-            if (mana.count() > 1 && mana.getGeneric() > 0) {
-                CardUtil.reduceCost(abilityToModify, 1);
-            }
+            CardUtil.reduceCost(abilityToModify, 1);
             return true;
         }
         return false;
diff --git a/Mage.Sets/src/mage/cards/e/EyeOfUgin.java b/Mage.Sets/src/mage/cards/e/EyeOfUgin.java
index 5aa63ff887..d2aec862fd 100644
--- a/Mage.Sets/src/mage/cards/e/EyeOfUgin.java
+++ b/Mage.Sets/src/mage/cards/e/EyeOfUgin.java
@@ -1,8 +1,5 @@
-
-
 package mage.cards.e;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.common.SimpleStaticAbility;
@@ -21,6 +18,8 @@ import mage.filter.common.FilterCreatureCard;
 import mage.filter.predicate.mageobject.ColorlessPredicate;
 import mage.target.common.TargetCardInLibrary;
 
+import java.util.UUID;
+
 /**
  * @author maurer.it_at_gmail.com
  */
@@ -41,6 +40,7 @@ public final class EyeOfUgin extends CardImpl {
 
         // Colorless Eldrazi spells you cast cost {2} less to cast.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filterSpells, 2)));
+
         // {7}, {tap}: Search your library for a colorless creature card, reveal it, and put it into your hand. Then shuffle your library.
         Ability searchAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD,
                 new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter), true),
diff --git a/Mage.Sets/src/mage/cards/f/Fluctuator.java b/Mage.Sets/src/mage/cards/f/Fluctuator.java
index c0a1e52d08..29433b881d 100644
--- a/Mage.Sets/src/mage/cards/f/Fluctuator.java
+++ b/Mage.Sets/src/mage/cards/f/Fluctuator.java
@@ -1,8 +1,5 @@
 package mage.cards.f;
 
-import java.util.LinkedHashSet;
-import java.util.Set;
-import java.util.UUID;
 import mage.Mana;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleStaticAbility;
@@ -16,8 +13,11 @@ import mage.game.Game;
 import mage.players.Player;
 import mage.util.CardUtil;
 
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class Fluctuator extends CardImpl {
@@ -69,7 +69,7 @@ class FluctuatorEffect extends CostModificationEffectImpl {
             }
             if (reduceMax > 0) {
                 int reduce;
-                if (game.inCheckPlayableState()) {
+                if (game.inCheckPlayableState() || !controller.isHuman()) {
                     reduce = reduceMax;
                 } else {
                     ChoiceImpl choice = new ChoiceImpl(true);
diff --git a/Mage.Sets/src/mage/cards/f/FrogtosserBanneret.java b/Mage.Sets/src/mage/cards/f/FrogtosserBanneret.java
index 886577ba81..dd107b833f 100644
--- a/Mage.Sets/src/mage/cards/f/FrogtosserBanneret.java
+++ b/Mage.Sets/src/mage/cards/f/FrogtosserBanneret.java
@@ -1,7 +1,5 @@
-
 package mage.cards.f;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect;
@@ -14,8 +12,9 @@ import mage.constants.Zone;
 import mage.filter.FilterCard;
 import mage.filter.predicate.Predicates;
 
+import java.util.UUID;
+
 /**
- *
  * @author North
  */
 public final class FrogtosserBanneret extends CardImpl {
@@ -29,7 +28,7 @@ public final class FrogtosserBanneret extends CardImpl {
     }
 
     public FrogtosserBanneret(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}");
         this.subtype.add(SubType.GOBLIN);
         this.subtype.add(SubType.ROGUE);
 
@@ -38,6 +37,7 @@ public final class FrogtosserBanneret extends CardImpl {
 
         // Haste
         this.addAbility(HasteAbility.getInstance());
+
         // Goblin spells and Rogue spells you cast cost {1} less to cast.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filter, 1)));
     }
diff --git a/Mage.Sets/src/mage/cards/g/GateColossus.java b/Mage.Sets/src/mage/cards/g/GateColossus.java
index b1c013f495..e2387b7ccd 100644
--- a/Mage.Sets/src/mage/cards/g/GateColossus.java
+++ b/Mage.Sets/src/mage/cards/g/GateColossus.java
@@ -84,9 +84,7 @@ class GateColossusCostReductionEffect extends CostModificationEffectImpl {
     @Override
     public boolean apply(Game game, Ability source, Ability abilityToModify) {
         int count = game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(), game).size();
-        if (count > 0) {
-            CardUtil.reduceCost(abilityToModify, count);
-        }
+        CardUtil.reduceCost(abilityToModify, count);
         return true;
     }
 
diff --git a/Mage.Sets/src/mage/cards/g/GearseekerSerpent.java b/Mage.Sets/src/mage/cards/g/GearseekerSerpent.java
index 30468d663c..6d0991c962 100644
--- a/Mage.Sets/src/mage/cards/g/GearseekerSerpent.java
+++ b/Mage.Sets/src/mage/cards/g/GearseekerSerpent.java
@@ -66,9 +66,7 @@ class GearseekerSerpentCostReductionEffect extends CostModificationEffectImpl {
     @Override
     public boolean apply(Game game, Ability source, Ability abilityToModify) {
         int count = game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(), game).size();
-        if (count > 0) {
-            CardUtil.reduceCost(abilityToModify, count);
-        }
+        CardUtil.reduceCost(abilityToModify, count);
         return true;
     }
 
diff --git a/Mage.Sets/src/mage/cards/g/GoblinWarchief.java b/Mage.Sets/src/mage/cards/g/GoblinWarchief.java
index 13106e9ba2..9ae9194961 100644
--- a/Mage.Sets/src/mage/cards/g/GoblinWarchief.java
+++ b/Mage.Sets/src/mage/cards/g/GoblinWarchief.java
@@ -1,7 +1,5 @@
-
 package mage.cards.g;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
@@ -16,8 +14,9 @@ import mage.constants.Zone;
 import mage.filter.FilterCard;
 import mage.filter.common.FilterCreaturePermanent;
 
+import java.util.UUID;
+
 /**
- *
  * @author jonubuu
  */
 public final class GoblinWarchief extends CardImpl {
@@ -38,6 +37,7 @@ public final class GoblinWarchief extends CardImpl {
 
         // Goblin spells you cast cost {1} less to cast.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filterSpells, 1)));
+
         // Goblins you control have haste.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect(HasteAbility.getInstance(),
                 Duration.WhileOnBattlefield, new FilterCreaturePermanent(SubType.GOBLIN, "Goblins"), false)));
diff --git a/Mage.Sets/src/mage/cards/g/GrandArbiterAugustinIV.java b/Mage.Sets/src/mage/cards/g/GrandArbiterAugustinIV.java
index 93c9692277..9c247eb3a7 100644
--- a/Mage.Sets/src/mage/cards/g/GrandArbiterAugustinIV.java
+++ b/Mage.Sets/src/mage/cards/g/GrandArbiterAugustinIV.java
@@ -1,7 +1,5 @@
-
 package mage.cards.g;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.ObjectColor;
 import mage.abilities.Ability;
@@ -17,21 +15,23 @@ import mage.filter.predicate.mageobject.ColorPredicate;
 import mage.game.Game;
 import mage.util.CardUtil;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class GrandArbiterAugustinIV extends CardImpl {
 
     private static final FilterCard filterWhite = new FilterCard("White spells");
     private static final FilterCard filterBlue = new FilterCard("Blue spells");
+
     static {
         filterWhite.add(new ColorPredicate(ObjectColor.WHITE));
         filterBlue.add(new ColorPredicate(ObjectColor.BLUE));
     }
 
     public GrandArbiterAugustinIV(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}{U}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{U}");
         addSuperType(SuperType.LEGENDARY);
         this.subtype.add(SubType.HUMAN);
         this.subtype.add(SubType.ADVISOR);
@@ -41,8 +41,10 @@ public final class GrandArbiterAugustinIV extends CardImpl {
 
         // White spells you cast cost {1} less to cast.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filterWhite, 1)));
+
         // Blue spells you cast cost {1} less to cast.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filterBlue, 1)));
+
         // Spells your opponents cast cost {1} more to cast.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GrandArbiterAugustinIVCostIncreaseEffect()));
     }
diff --git a/Mage.Sets/src/mage/cards/h/Heartstone.java b/Mage.Sets/src/mage/cards/h/Heartstone.java
index f2dabae26c..9b65d308f3 100644
--- a/Mage.Sets/src/mage/cards/h/Heartstone.java
+++ b/Mage.Sets/src/mage/cards/h/Heartstone.java
@@ -1,34 +1,27 @@
-
 package mage.cards.h;
 
-import java.util.UUID;
-import mage.Mana;
 import mage.abilities.Ability;
 import mage.abilities.ActivatedAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.common.cost.CostModificationEffectImpl;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.AbilityType;
-import mage.constants.CardType;
-import mage.constants.CostModificationType;
-import mage.constants.Duration;
-import mage.constants.Outcome;
-import mage.constants.Zone;
+import mage.constants.*;
 import mage.filter.common.FilterCreaturePermanent;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
 import mage.players.Player;
 import mage.util.CardUtil;
 
+import java.util.UUID;
+
 /**
- *
  * @author pcasaretto_at_gmail.com
  */
 public final class Heartstone extends CardImpl {
 
     public Heartstone(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}");
+        super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}");
 
         // Activated abilities of creatures cost {1} less to activate. This effect can't reduce the amount of mana an ability costs to activate to less than one mana.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new HeartstoneEffect()));
@@ -62,10 +55,7 @@ class HeartstoneEffect extends CostModificationEffectImpl {
     public boolean apply(Game game, Ability source, Ability abilityToModify) {
         Player controller = game.getPlayer(abilityToModify.getControllerId());
         if (controller != null) {
-            Mana mana = abilityToModify.getManaCostsToPay().getMana();
-            if (mana.count() > 1 && mana.getGeneric() > 0) {
-                CardUtil.reduceCost(abilityToModify, 1);
-            }
+            CardUtil.reduceCost(abilityToModify, 1);
             return true;
         }
         return false;
diff --git a/Mage.Sets/src/mage/cards/h/HeroOfIroas.java b/Mage.Sets/src/mage/cards/h/HeroOfIroas.java
index 21855980f9..f00ae978c6 100644
--- a/Mage.Sets/src/mage/cards/h/HeroOfIroas.java
+++ b/Mage.Sets/src/mage/cards/h/HeroOfIroas.java
@@ -1,7 +1,5 @@
-
 package mage.cards.h;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect;
@@ -15,19 +13,21 @@ import mage.constants.Zone;
 import mage.counters.CounterType;
 import mage.filter.FilterCard;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class HeroOfIroas extends CardImpl {
 
     private static final FilterCard filter = new FilterCard("Aura spells");
+
     static {
         filter.add(SubType.AURA.getPredicate());
     }
 
     public HeroOfIroas(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{W}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}");
         this.subtype.add(SubType.HUMAN);
         this.subtype.add(SubType.SOLDIER);
 
@@ -36,6 +36,7 @@ public final class HeroOfIroas extends CardImpl {
 
         // Aura spells you cast cost {1} less to cast.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filter, 1)));
+
         // <i>Heroic</i> &mdash; Whenever you cast a spell that targets Hero of Iroas, put a +1/+1 counter on Hero of Iroas.
         this.addAbility(new HeroicAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance())));
     }
diff --git a/Mage.Sets/src/mage/cards/h/HumOfTheRadix.java b/Mage.Sets/src/mage/cards/h/HumOfTheRadix.java
index 3eafa3ed17..7e806c12c6 100644
--- a/Mage.Sets/src/mage/cards/h/HumOfTheRadix.java
+++ b/Mage.Sets/src/mage/cards/h/HumOfTheRadix.java
@@ -1,7 +1,5 @@
-
 package mage.cards.h;
 
-import java.util.UUID;
 import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.SpellAbility;
@@ -9,17 +7,14 @@ import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.common.cost.CostModificationEffectImpl;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.CardType;
-import mage.constants.CostModificationType;
-import mage.constants.Duration;
-import mage.constants.Outcome;
-import mage.constants.Zone;
+import mage.constants.*;
 import mage.filter.common.FilterArtifactPermanent;
 import mage.game.Game;
 import mage.util.CardUtil;
 
+import java.util.UUID;
+
 /**
- *
  * @author Pete Rossi
  */
 public final class HumOfTheRadix extends CardImpl {
@@ -56,9 +51,7 @@ class HumOfTheRadixCostIncreaseEffect extends CostModificationEffectImpl {
     @Override
     public boolean apply(Game game, Ability source, Ability abilityToModify) {
         int additionalCost = game.getBattlefield().getAllActivePermanents(new FilterArtifactPermanent(), abilityToModify.getControllerId(), game).size();
-        if (additionalCost > 0) {
-            CardUtil.increaseCost(abilityToModify, additionalCost);
-        }
+        CardUtil.increaseCost(abilityToModify, additionalCost);
         return true;
     }
 
diff --git a/Mage.Sets/src/mage/cards/m/MythUnbound.java b/Mage.Sets/src/mage/cards/m/MythUnbound.java
index 08e62af7f6..0b2e0ea26d 100644
--- a/Mage.Sets/src/mage/cards/m/MythUnbound.java
+++ b/Mage.Sets/src/mage/cards/m/MythUnbound.java
@@ -77,10 +77,8 @@ class MythUnboundCostReductionEffect extends CostModificationEffectImpl {
         if (spellAbility != null) {
             CommanderPlaysCountWatcher watcher = game.getState().getWatcher(CommanderPlaysCountWatcher.class);
             int castCount = watcher.getPlaysCount(abilityToModify.getSourceId());
-            if (castCount > 0) {
-                CardUtil.reduceCost(spellAbility, castCount);
-                return true;
-            }
+            CardUtil.reduceCost(spellAbility, castCount);
+            return true;
         }
         return false;
     }
diff --git a/Mage.Sets/src/mage/cards/n/NightscapeFamiliar.java b/Mage.Sets/src/mage/cards/n/NightscapeFamiliar.java
index ad4ec58da7..2a7ae10e1d 100644
--- a/Mage.Sets/src/mage/cards/n/NightscapeFamiliar.java
+++ b/Mage.Sets/src/mage/cards/n/NightscapeFamiliar.java
@@ -1,7 +1,5 @@
-
 package mage.cards.n;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.ObjectColor;
 import mage.abilities.common.SimpleActivatedAbility;
@@ -18,8 +16,9 @@ import mage.filter.FilterCard;
 import mage.filter.predicate.Predicates;
 import mage.filter.predicate.mageobject.ColorPredicate;
 
+import java.util.UUID;
+
 /**
- *
  * @author North
  */
 public final class NightscapeFamiliar extends CardImpl {
@@ -33,7 +32,7 @@ public final class NightscapeFamiliar extends CardImpl {
     }
 
     public NightscapeFamiliar(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}");
         this.subtype.add(SubType.ZOMBIE);
 
         this.power = new MageInt(1);
@@ -41,6 +40,7 @@ public final class NightscapeFamiliar extends CardImpl {
 
         // Blue spells and red spells you cast cost {1} less to cast.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filter, 1)));
+
         // {1}{B}: Regenerate Nightscape Familiar.
         this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateSourceEffect(), new ManaCostsImpl("{1}{B}")));
     }
diff --git a/Mage.Sets/src/mage/cards/r/RakdosLordOfRiots.java b/Mage.Sets/src/mage/cards/r/RakdosLordOfRiots.java
index 37f5452921..00a84e1aee 100644
--- a/Mage.Sets/src/mage/cards/r/RakdosLordOfRiots.java
+++ b/Mage.Sets/src/mage/cards/r/RakdosLordOfRiots.java
@@ -105,10 +105,8 @@ class RakdosLordOfRiotsCostReductionEffect extends CostModificationEffectImpl {
         Ability spellAbility = abilityToModify;
         if (spellAbility != null) {
             int amount = OpponentsLostLifeCount.instance.calculate(game, source, this);
-            if (amount > 0) {
-                CardUtil.reduceCost(spellAbility, amount);
-                return true;
-            }
+            CardUtil.reduceCost(spellAbility, amount);
+            return true;
         }
         return false;
     }
diff --git a/Mage.Sets/src/mage/cards/s/StonybrookBanneret.java b/Mage.Sets/src/mage/cards/s/StonybrookBanneret.java
index 70b28e219c..3c6a09327f 100644
--- a/Mage.Sets/src/mage/cards/s/StonybrookBanneret.java
+++ b/Mage.Sets/src/mage/cards/s/StonybrookBanneret.java
@@ -1,7 +1,5 @@
-
 package mage.cards.s;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect;
@@ -14,8 +12,9 @@ import mage.constants.Zone;
 import mage.filter.FilterCard;
 import mage.filter.predicate.Predicates;
 
+import java.util.UUID;
+
 /**
- *
  * @author North
  */
 public final class StonybrookBanneret extends CardImpl {
@@ -29,7 +28,7 @@ public final class StonybrookBanneret extends CardImpl {
     }
 
     public StonybrookBanneret(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}");
         this.subtype.add(SubType.MERFOLK);
         this.subtype.add(SubType.WIZARD);
 
@@ -38,6 +37,7 @@ public final class StonybrookBanneret extends CardImpl {
 
         // Islandwalk
         this.addAbility(new IslandwalkAbility());
+
         // Merfolk spells and Wizard spells you cast cost {1} less to cast.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filter, 1)));
     }
diff --git a/Mage.Sets/src/mage/cards/s/StormscapeFamiliar.java b/Mage.Sets/src/mage/cards/s/StormscapeFamiliar.java
index 31f5d31778..d09f512d5f 100644
--- a/Mage.Sets/src/mage/cards/s/StormscapeFamiliar.java
+++ b/Mage.Sets/src/mage/cards/s/StormscapeFamiliar.java
@@ -1,7 +1,5 @@
-
 package mage.cards.s;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.ObjectColor;
 import mage.abilities.common.SimpleStaticAbility;
@@ -16,8 +14,9 @@ import mage.filter.FilterCard;
 import mage.filter.predicate.Predicates;
 import mage.filter.predicate.mageobject.ColorPredicate;
 
+import java.util.UUID;
+
 /**
- *
  * @author North
  */
 public final class StormscapeFamiliar extends CardImpl {
@@ -31,7 +30,7 @@ public final class StormscapeFamiliar extends CardImpl {
     }
 
     public StormscapeFamiliar(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}");
         this.subtype.add(SubType.BIRD);
 
         this.power = new MageInt(1);
@@ -39,6 +38,7 @@ public final class StormscapeFamiliar extends CardImpl {
 
         // Flying
         this.addAbility(FlyingAbility.getInstance());
+
         // White spells and black spells you cast cost {1} less to cast.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filter, 1)));
     }
diff --git a/Mage.Sets/src/mage/cards/s/SunscapeFamiliar.java b/Mage.Sets/src/mage/cards/s/SunscapeFamiliar.java
index 420701d6d9..dfacbc0626 100644
--- a/Mage.Sets/src/mage/cards/s/SunscapeFamiliar.java
+++ b/Mage.Sets/src/mage/cards/s/SunscapeFamiliar.java
@@ -1,7 +1,5 @@
-
 package mage.cards.s;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.ObjectColor;
 import mage.abilities.common.SimpleStaticAbility;
@@ -16,8 +14,9 @@ import mage.filter.FilterCard;
 import mage.filter.predicate.Predicates;
 import mage.filter.predicate.mageobject.ColorPredicate;
 
+import java.util.UUID;
+
 /**
- *
  * @author North
  */
 public final class SunscapeFamiliar extends CardImpl {
@@ -31,7 +30,7 @@ public final class SunscapeFamiliar extends CardImpl {
     }
 
     public SunscapeFamiliar(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{W}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}");
         this.subtype.add(SubType.WALL);
 
         this.power = new MageInt(0);
@@ -39,6 +38,7 @@ public final class SunscapeFamiliar extends CardImpl {
 
         // Defender
         this.addAbility(DefenderAbility.getInstance());
+
         // Green spells and blue spells you cast cost {1} less to cast.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filter, 1)));
     }
diff --git a/Mage.Sets/src/mage/cards/t/TheImmortalSun.java b/Mage.Sets/src/mage/cards/t/TheImmortalSun.java
index f2795b87c9..dd3c5078cf 100644
--- a/Mage.Sets/src/mage/cards/t/TheImmortalSun.java
+++ b/Mage.Sets/src/mage/cards/t/TheImmortalSun.java
@@ -1,8 +1,5 @@
-
 package mage.cards.t;
 
-import java.util.Optional;
-import java.util.UUID;
 import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.LoyaltyAbility;
@@ -14,19 +11,16 @@ import mage.abilities.effects.common.continuous.BoostControlledEffect;
 import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.CardType;
-import mage.constants.Duration;
-import mage.constants.Outcome;
-import mage.constants.SuperType;
-import mage.constants.TargetController;
-import mage.constants.Zone;
+import mage.constants.*;
 import mage.filter.FilterCard;
 import mage.game.Game;
 import mage.game.events.GameEvent;
 import mage.game.permanent.Permanent;
 
+import java.util.Optional;
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class TheImmortalSun extends CardImpl {
@@ -38,11 +32,14 @@ public final class TheImmortalSun extends CardImpl {
 
         // Players can't activate planeswalkers' loyalty abilities.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new TheImmortalSunCantActivateEffect()));
+
         // At the beginning of your draw step, draw an additional card.
         this.addAbility(new BeginningOfDrawTriggeredAbility(new DrawCardSourceControllerEffect(1)
                 .setText("draw an additional card"), TargetController.YOU, false));
+
         // Spells you cast cost {1} less to cast.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(new FilterCard("Spells"), 1)));
+
         // Creatures you control get +1/+1.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield)));
     }
diff --git a/Mage.Sets/src/mage/cards/t/ThunderscapeFamiliar.java b/Mage.Sets/src/mage/cards/t/ThunderscapeFamiliar.java
index d66b96c6d4..3bea2e1357 100644
--- a/Mage.Sets/src/mage/cards/t/ThunderscapeFamiliar.java
+++ b/Mage.Sets/src/mage/cards/t/ThunderscapeFamiliar.java
@@ -1,7 +1,5 @@
-
 package mage.cards.t;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.ObjectColor;
 import mage.abilities.common.SimpleStaticAbility;
@@ -16,8 +14,9 @@ import mage.filter.FilterCard;
 import mage.filter.predicate.Predicates;
 import mage.filter.predicate.mageobject.ColorPredicate;
 
+import java.util.UUID;
+
 /**
- *
  * @author North
  */
 public final class ThunderscapeFamiliar extends CardImpl {
@@ -31,7 +30,7 @@ public final class ThunderscapeFamiliar extends CardImpl {
     }
 
     public ThunderscapeFamiliar(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{R}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}");
         this.subtype.add(SubType.KAVU);
 
         this.power = new MageInt(1);
@@ -39,6 +38,7 @@ public final class ThunderscapeFamiliar extends CardImpl {
 
         // First strike
         this.addAbility(FirstStrikeAbility.getInstance());
+
         // Black spells and green spells you cast cost {1} less to cast.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filter, 1)));
     }
diff --git a/Mage.Sets/src/mage/cards/u/UndeadWarchief.java b/Mage.Sets/src/mage/cards/u/UndeadWarchief.java
index c8a10d9f5c..6725f512d4 100644
--- a/Mage.Sets/src/mage/cards/u/UndeadWarchief.java
+++ b/Mage.Sets/src/mage/cards/u/UndeadWarchief.java
@@ -1,7 +1,5 @@
-
 package mage.cards.u;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.common.continuous.BoostControlledEffect;
@@ -15,8 +13,9 @@ import mage.constants.Zone;
 import mage.filter.FilterCard;
 import mage.filter.common.FilterCreaturePermanent;
 
+import java.util.UUID;
+
 /**
- *
  * @author North
  */
 public final class UndeadWarchief extends CardImpl {
@@ -30,7 +29,7 @@ public final class UndeadWarchief extends CardImpl {
     }
 
     public UndeadWarchief(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{B}{B}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{B}");
         this.subtype.add(SubType.ZOMBIE);
 
         this.power = new MageInt(1);
@@ -38,6 +37,7 @@ public final class UndeadWarchief extends CardImpl {
 
         // Zombie spells you cast cost {1} less to cast.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filter, 1)));
+
         // Zombie creatures you control get +2/+1.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(2, 1, Duration.WhileOnBattlefield, filterCreatures, false)));
     }
diff --git a/Mage.Sets/src/mage/cards/w/WardenOfEvosIsle.java b/Mage.Sets/src/mage/cards/w/WardenOfEvosIsle.java
index d8f5556047..a3834c1c39 100644
--- a/Mage.Sets/src/mage/cards/w/WardenOfEvosIsle.java
+++ b/Mage.Sets/src/mage/cards/w/WardenOfEvosIsle.java
@@ -1,7 +1,5 @@
-
 package mage.cards.w;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect;
@@ -14,20 +12,22 @@ import mage.constants.Zone;
 import mage.filter.FilterCard;
 import mage.filter.predicate.mageobject.AbilityPredicate;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class WardenOfEvosIsle extends CardImpl {
 
     private static final FilterCard filter = new FilterCard("Creature spells with flying");
+
     static {
         filter.add(CardType.CREATURE.getPredicate());
         filter.add(new AbilityPredicate(FlyingAbility.class));
     }
 
     public WardenOfEvosIsle(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{U}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}");
         this.subtype.add(SubType.BIRD);
         this.subtype.add(SubType.WIZARD);
 
@@ -36,6 +36,7 @@ public final class WardenOfEvosIsle extends CardImpl {
 
         // Flying
         this.addAbility(FlyingAbility.getInstance());
+
         // Creature spells with flying you cast cost {1} less to cast.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filter, 1)));
 
diff --git a/Mage/src/main/java/mage/abilities/effects/common/AffinityEffect.java b/Mage/src/main/java/mage/abilities/effects/common/AffinityEffect.java
index 617a18934e..f44dfcf545 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/AffinityEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/AffinityEffect.java
@@ -29,9 +29,7 @@ public class AffinityEffect extends CostModificationEffectImpl {
     public boolean apply(Game game, Ability source, Ability abilityToModify) {
         // abilityToModify.getControllerId() works with Sen Triplets and in multiplayer games, see https://github.com/magefree/mage/issues/5931
         int count = game.getBattlefield().getActivePermanents(filter, abilityToModify.getControllerId(), source.getId(), game).size();
-        if (count > 0) {
-            CardUtil.reduceCost(abilityToModify, count);
-        }
+        CardUtil.reduceCost(abilityToModify, count);
         return true;
     }
 
diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/PlanarDieRollCostIncreasingEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/PlanarDieRollCostIncreasingEffect.java
new file mode 100644
index 0000000000..0adb9dd283
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/effects/common/cost/PlanarDieRollCostIncreasingEffect.java
@@ -0,0 +1,53 @@
+package mage.abilities.effects.common.cost;
+
+import mage.abilities.Ability;
+import mage.constants.CostModificationType;
+import mage.constants.Duration;
+import mage.constants.Outcome;
+import mage.game.Game;
+import mage.players.Player;
+import mage.util.CardUtil;
+import mage.watchers.common.PlanarRollWatcher;
+
+import java.util.UUID;
+
+/**
+ * @author JayDi85
+ */
+public class PlanarDieRollCostIncreasingEffect extends CostModificationEffectImpl {
+
+    private final UUID originalId;
+
+    public PlanarDieRollCostIncreasingEffect(UUID originalId) {
+        super(Duration.EndOfGame, Outcome.Benefit, CostModificationType.INCREASE_COST);
+        this.originalId = originalId;
+    }
+
+    private PlanarDieRollCostIncreasingEffect(final PlanarDieRollCostIncreasingEffect effect) {
+        super(effect);
+        this.originalId = effect.originalId;
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source, Ability abilityToModify) {
+        Player activePlayer = game.getPlayer(game.getActivePlayerId());
+        PlanarRollWatcher watcher = game.getState().getWatcher(PlanarRollWatcher.class);
+        if (activePlayer == null && watcher == null) {
+            return false;
+        }
+
+        int rolledCounter = watcher.getNumberTimesPlanarDieRolled(activePlayer.getId());
+        CardUtil.increaseCost(abilityToModify, rolledCounter);
+        return true;
+    }
+
+    @Override
+    public boolean applies(Ability abilityToModify, Ability source, Game game) {
+        return abilityToModify.getOriginalId().equals(originalId);
+    }
+
+    @Override
+    public PlanarDieRollCostIncreasingEffect copy() {
+        return new PlanarDieRollCostIncreasingEffect(this);
+    }
+}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionSourceForOpponentsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionSourceForOpponentsEffect.java
index 25b3ce931f..6b6509870a 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionSourceForOpponentsEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionSourceForOpponentsEffect.java
@@ -26,9 +26,7 @@ public class SpellCostReductionSourceForOpponentsEffect extends CostModification
     @Override
     public boolean apply(Game game, Ability source, Ability abilityToModify) {
         int count = game.getOpponents(source.getControllerId()).size();
-        if (count > 0) {
-            CardUtil.reduceCost(abilityToModify, count);
-        }
+        CardUtil.reduceCost(abilityToModify, count);
         return true;
     }
 
diff --git a/Mage/src/main/java/mage/game/command/planes/AcademyAtTolariaWestPlane.java b/Mage/src/main/java/mage/game/command/planes/AcademyAtTolariaWestPlane.java
index 0f25459150..c5fce051a7 100644
--- a/Mage/src/main/java/mage/game/command/planes/AcademyAtTolariaWestPlane.java
+++ b/Mage/src/main/java/mage/game/command/planes/AcademyAtTolariaWestPlane.java
@@ -12,6 +12,7 @@ import mage.abilities.dynamicvalue.common.StaticValue;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.RollPlanarDieEffect;
+import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect;
 import mage.abilities.effects.common.discard.DiscardHandControllerEffect;
 import mage.constants.Outcome;
 import mage.constants.Planes;
diff --git a/Mage/src/main/java/mage/game/command/planes/AgyremPlane.java b/Mage/src/main/java/mage/game/command/planes/AgyremPlane.java
index 319842dae7..ee88f5d6f4 100644
--- a/Mage/src/main/java/mage/game/command/planes/AgyremPlane.java
+++ b/Mage/src/main/java/mage/game/command/planes/AgyremPlane.java
@@ -14,8 +14,10 @@ import mage.abilities.effects.RestrictionEffect;
 import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect;
 import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetEffect;
 import mage.abilities.effects.common.RollPlanarDieEffect;
+import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect;
 import mage.cards.Card;
 import mage.constants.*;
+import mage.filter.common.FilterCreaturePermanent;
 import mage.filter.predicate.Predicates;
 import mage.filter.predicate.mageobject.ColorPredicate;
 import mage.game.Game;
@@ -28,7 +30,6 @@ import mage.watchers.common.PlanarRollWatcher;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
-import mage.filter.common.FilterCreaturePermanent;
 
 /**
  * @author spjspj
diff --git a/Mage/src/main/java/mage/game/command/planes/AkoumPlane.java b/Mage/src/main/java/mage/game/command/planes/AkoumPlane.java
index 145b36af7b..57607eb484 100644
--- a/Mage/src/main/java/mage/game/command/planes/AkoumPlane.java
+++ b/Mage/src/main/java/mage/game/command/planes/AkoumPlane.java
@@ -8,6 +8,7 @@ import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.DestroyTargetEffect;
 import mage.abilities.effects.common.RollPlanarDieEffect;
 import mage.abilities.effects.common.continuous.CastAsThoughItHadFlashAllEffect;
+import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect;
 import mage.constants.*;
 import mage.filter.FilterCard;
 import mage.filter.common.FilterCreaturePermanent;
diff --git a/Mage/src/main/java/mage/game/command/planes/AstralArenaPlane.java b/Mage/src/main/java/mage/game/command/planes/AstralArenaPlane.java
index 8fa0a54369..2a60058195 100644
--- a/Mage/src/main/java/mage/game/command/planes/AstralArenaPlane.java
+++ b/Mage/src/main/java/mage/game/command/planes/AstralArenaPlane.java
@@ -9,6 +9,7 @@ import mage.abilities.effects.Effect;
 import mage.abilities.effects.RestrictionEffect;
 import mage.abilities.effects.common.DamageAllEffect;
 import mage.abilities.effects.common.RollPlanarDieEffect;
+import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect;
 import mage.constants.Duration;
 import mage.constants.Planes;
 import mage.constants.TargetController;
diff --git a/Mage/src/main/java/mage/game/command/planes/BantPlane.java b/Mage/src/main/java/mage/game/command/planes/BantPlane.java
index 80ab2ff662..a717305033 100644
--- a/Mage/src/main/java/mage/game/command/planes/BantPlane.java
+++ b/Mage/src/main/java/mage/game/command/planes/BantPlane.java
@@ -1,7 +1,6 @@
 package mage.game.command.planes;
 
 import mage.ObjectColor;
-import mage.abilities.Ability;
 import mage.abilities.common.ActivateIfConditionActivatedAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.condition.common.IsStillOnPlaneCondition;
@@ -13,27 +12,26 @@ import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.RollPlanarDieEffect;
 import mage.abilities.effects.common.continuous.GainAbilityAllEffect;
 import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
-import mage.abilities.effects.common.cost.CostModificationEffectImpl;
+import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect;
 import mage.abilities.effects.common.counter.AddCountersTargetEffect;
 import mage.abilities.keyword.ExaltedAbility;
 import mage.abilities.keyword.IndestructibleAbility;
-import mage.constants.*;
+import mage.constants.Duration;
+import mage.constants.Planes;
+import mage.constants.TargetController;
+import mage.constants.Zone;
 import mage.counters.CounterType;
 import mage.filter.StaticFilters;
 import mage.filter.common.FilterCreaturePermanent;
 import mage.filter.predicate.Predicates;
 import mage.filter.predicate.mageobject.ColorPredicate;
-import mage.game.Game;
 import mage.game.command.Plane;
-import mage.players.Player;
 import mage.target.Target;
 import mage.target.common.TargetCreaturePermanent;
-import mage.util.CardUtil;
 import mage.watchers.common.PlanarRollWatcher;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.UUID;
 
 /**
  * @author spjspj
@@ -83,42 +81,3 @@ public class BantPlane extends Plane {
         this.getAbilities().add(new SimpleStaticAbility(Zone.ALL, new PlanarDieRollCostIncreasingEffect(chaosAbility.getOriginalId())));
     }
 }
-
-class PlanarDieRollCostIncreasingEffect extends CostModificationEffectImpl {
-
-    private final UUID originalId;
-
-    PlanarDieRollCostIncreasingEffect(UUID originalId) {
-        super(Duration.EndOfGame, Outcome.Benefit, CostModificationType.INCREASE_COST);
-        this.originalId = originalId;
-    }
-
-    PlanarDieRollCostIncreasingEffect(final PlanarDieRollCostIncreasingEffect effect) {
-        super(effect);
-        this.originalId = effect.originalId;
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        Player activePlayer = game.getPlayer(game.getActivePlayerId());
-        if (activePlayer != null) {
-            PlanarRollWatcher watcher = game.getState().getWatcher(PlanarRollWatcher.class);
-            int rolledCounter = 0;
-            if (watcher != null) {
-                rolledCounter = watcher.getNumberTimesPlanarDieRolled(activePlayer.getId());
-            }
-            CardUtil.increaseCost(abilityToModify, rolledCounter);
-        }
-        return true;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        return abilityToModify.getOriginalId().equals(originalId);
-    }
-
-    @Override
-    public PlanarDieRollCostIncreasingEffect copy() {
-        return new PlanarDieRollCostIncreasingEffect(this);
-    }
-}
diff --git a/Mage/src/main/java/mage/game/command/planes/EdgeOfMalacolPlane.java b/Mage/src/main/java/mage/game/command/planes/EdgeOfMalacolPlane.java
index 8897d4c0e0..60ce3e1e88 100644
--- a/Mage/src/main/java/mage/game/command/planes/EdgeOfMalacolPlane.java
+++ b/Mage/src/main/java/mage/game/command/planes/EdgeOfMalacolPlane.java
@@ -9,6 +9,7 @@ import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.RollPlanarDieEffect;
 import mage.abilities.effects.common.UntapAllControllerEffect;
+import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect;
 import mage.abilities.effects.common.counter.AddCountersTargetEffect;
 import mage.constants.*;
 import mage.counters.CounterType;
diff --git a/Mage/src/main/java/mage/game/command/planes/FeedingGroundsPlane.java b/Mage/src/main/java/mage/game/command/planes/FeedingGroundsPlane.java
index c81020ed7b..f60f151720 100644
--- a/Mage/src/main/java/mage/game/command/planes/FeedingGroundsPlane.java
+++ b/Mage/src/main/java/mage/game/command/planes/FeedingGroundsPlane.java
@@ -12,6 +12,7 @@ import mage.abilities.dynamicvalue.common.TargetConvertedManaCost;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.RollPlanarDieEffect;
 import mage.abilities.effects.common.cost.CostModificationEffectImpl;
+import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect;
 import mage.abilities.effects.common.counter.AddCountersTargetEffect;
 import mage.cards.Card;
 import mage.constants.*;
diff --git a/Mage/src/main/java/mage/game/command/planes/FieldsOfSummerPlane.java b/Mage/src/main/java/mage/game/command/planes/FieldsOfSummerPlane.java
index d95c76cb87..ae561f6dca 100644
--- a/Mage/src/main/java/mage/game/command/planes/FieldsOfSummerPlane.java
+++ b/Mage/src/main/java/mage/game/command/planes/FieldsOfSummerPlane.java
@@ -11,6 +11,7 @@ import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.GainLifeEffect;
 import mage.abilities.effects.common.GainLifeTargetEffect;
 import mage.abilities.effects.common.RollPlanarDieEffect;
+import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect;
 import mage.constants.*;
 import mage.filter.FilterSpell;
 import mage.game.Game;
diff --git a/Mage/src/main/java/mage/game/command/planes/HedronFieldsOfAgadeemPlane.java b/Mage/src/main/java/mage/game/command/planes/HedronFieldsOfAgadeemPlane.java
index 5f8c0cf404..4e498c5bfe 100644
--- a/Mage/src/main/java/mage/game/command/planes/HedronFieldsOfAgadeemPlane.java
+++ b/Mage/src/main/java/mage/game/command/planes/HedronFieldsOfAgadeemPlane.java
@@ -9,6 +9,7 @@ import mage.abilities.effects.Effect;
 import mage.abilities.effects.RestrictionEffect;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.abilities.effects.common.RollPlanarDieEffect;
+import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect;
 import mage.constants.*;
 import mage.filter.common.FilterCreaturePermanent;
 import mage.filter.predicate.mageobject.PowerPredicate;
diff --git a/Mage/src/main/java/mage/game/command/planes/LetheLakePlane.java b/Mage/src/main/java/mage/game/command/planes/LetheLakePlane.java
index 2806a2af63..9bd2c8e0b9 100644
--- a/Mage/src/main/java/mage/game/command/planes/LetheLakePlane.java
+++ b/Mage/src/main/java/mage/game/command/planes/LetheLakePlane.java
@@ -10,6 +10,7 @@ import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.PutLibraryIntoGraveTargetEffect;
 import mage.abilities.effects.common.PutTopCardOfLibraryIntoGraveTargetEffect;
 import mage.abilities.effects.common.RollPlanarDieEffect;
+import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect;
 import mage.constants.Planes;
 import mage.constants.TargetController;
 import mage.constants.Zone;
diff --git a/Mage/src/main/java/mage/game/command/planes/NayaPlane.java b/Mage/src/main/java/mage/game/command/planes/NayaPlane.java
index 62908adaa4..764a479d4c 100644
--- a/Mage/src/main/java/mage/game/command/planes/NayaPlane.java
+++ b/Mage/src/main/java/mage/game/command/planes/NayaPlane.java
@@ -12,6 +12,7 @@ import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.RollPlanarDieEffect;
 import mage.abilities.effects.common.continuous.BoostTargetEffect;
 import mage.abilities.effects.common.continuous.PlayAdditionalLandsAllEffect;
+import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect;
 import mage.constants.Duration;
 import mage.constants.Planes;
 import mage.constants.TargetController;
diff --git a/Mage/src/main/java/mage/game/command/planes/PanopticonPlane.java b/Mage/src/main/java/mage/game/command/planes/PanopticonPlane.java
index 7adc83c3f6..54691deffa 100644
--- a/Mage/src/main/java/mage/game/command/planes/PanopticonPlane.java
+++ b/Mage/src/main/java/mage/game/command/planes/PanopticonPlane.java
@@ -11,6 +11,7 @@ import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.abilities.effects.common.DrawCardTargetEffect;
 import mage.abilities.effects.common.RollPlanarDieEffect;
+import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect;
 import mage.constants.Planes;
 import mage.constants.TargetController;
 import mage.constants.Zone;
diff --git a/Mage/src/main/java/mage/game/command/planes/TazeemPlane.java b/Mage/src/main/java/mage/game/command/planes/TazeemPlane.java
index 7ca6ca6bc9..1671cd0664 100644
--- a/Mage/src/main/java/mage/game/command/planes/TazeemPlane.java
+++ b/Mage/src/main/java/mage/game/command/planes/TazeemPlane.java
@@ -11,6 +11,7 @@ import mage.abilities.effects.Effect;
 import mage.abilities.effects.RestrictionEffect;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.abilities.effects.common.RollPlanarDieEffect;
+import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect;
 import mage.constants.Duration;
 import mage.constants.Planes;
 import mage.constants.TargetController;
diff --git a/Mage/src/main/java/mage/game/command/planes/TheDarkBaronyPlane.java b/Mage/src/main/java/mage/game/command/planes/TheDarkBaronyPlane.java
index e9b6866ce1..ab5682e9aa 100644
--- a/Mage/src/main/java/mage/game/command/planes/TheDarkBaronyPlane.java
+++ b/Mage/src/main/java/mage/game/command/planes/TheDarkBaronyPlane.java
@@ -10,6 +10,7 @@ import mage.abilities.costs.mana.GenericManaCost;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.LoseLifeTargetEffect;
 import mage.abilities.effects.common.RollPlanarDieEffect;
+import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect;
 import mage.abilities.effects.common.discard.DiscardEachPlayerEffect;
 import mage.constants.Planes;
 import mage.constants.SetTargetPointer;
diff --git a/Mage/src/main/java/mage/game/command/planes/TheEonFogPlane.java b/Mage/src/main/java/mage/game/command/planes/TheEonFogPlane.java
index ca69b487cd..d153355cbf 100644
--- a/Mage/src/main/java/mage/game/command/planes/TheEonFogPlane.java
+++ b/Mage/src/main/java/mage/game/command/planes/TheEonFogPlane.java
@@ -9,6 +9,7 @@ import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.RollPlanarDieEffect;
 import mage.abilities.effects.common.UntapAllControllerEffect;
+import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect;
 import mage.constants.*;
 import mage.filter.common.FilterControlledPermanent;
 import mage.game.Game;
diff --git a/Mage/src/main/java/mage/game/command/planes/TheGreatForestPlane.java b/Mage/src/main/java/mage/game/command/planes/TheGreatForestPlane.java
index a15dada75a..3d363d8707 100644
--- a/Mage/src/main/java/mage/game/command/planes/TheGreatForestPlane.java
+++ b/Mage/src/main/java/mage/game/command/planes/TheGreatForestPlane.java
@@ -10,6 +10,7 @@ import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.RollPlanarDieEffect;
 import mage.abilities.effects.common.continuous.BoostControlledEffect;
 import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
+import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect;
 import mage.abilities.keyword.TrampleAbility;
 import mage.constants.*;
 import mage.filter.StaticFilters;
diff --git a/Mage/src/main/java/mage/game/command/planes/TheZephyrMazePlane.java b/Mage/src/main/java/mage/game/command/planes/TheZephyrMazePlane.java
index 105c399d26..b46cc06d12 100644
--- a/Mage/src/main/java/mage/game/command/planes/TheZephyrMazePlane.java
+++ b/Mage/src/main/java/mage/game/command/planes/TheZephyrMazePlane.java
@@ -11,6 +11,7 @@ import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.RollPlanarDieEffect;
 import mage.abilities.effects.common.continuous.BoostAllEffect;
 import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
+import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.constants.Duration;
 import mage.constants.Planes;
diff --git a/Mage/src/main/java/mage/game/command/planes/TrailOfTheMageRingsPlane.java b/Mage/src/main/java/mage/game/command/planes/TrailOfTheMageRingsPlane.java
index 28e32d4519..be02a597c8 100644
--- a/Mage/src/main/java/mage/game/command/planes/TrailOfTheMageRingsPlane.java
+++ b/Mage/src/main/java/mage/game/command/planes/TrailOfTheMageRingsPlane.java
@@ -8,6 +8,7 @@ import mage.abilities.costs.mana.GenericManaCost;
 import mage.abilities.effects.ContinuousEffectImpl;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.RollPlanarDieEffect;
+import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect;
 import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect;
 import mage.abilities.keyword.ReboundAbility;
 import mage.cards.Card;
diff --git a/Mage/src/main/java/mage/game/command/planes/TrugaJunglePlane.java b/Mage/src/main/java/mage/game/command/planes/TrugaJunglePlane.java
index 74024299e2..f04613c19c 100644
--- a/Mage/src/main/java/mage/game/command/planes/TrugaJunglePlane.java
+++ b/Mage/src/main/java/mage/game/command/planes/TrugaJunglePlane.java
@@ -10,6 +10,7 @@ import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.RevealLibraryPutIntoHandEffect;
 import mage.abilities.effects.common.RollPlanarDieEffect;
 import mage.abilities.effects.common.continuous.GainAbilityAllEffect;
+import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect;
 import mage.abilities.mana.AnyColorManaAbility;
 import mage.constants.Duration;
 import mage.constants.Planes;
diff --git a/Mage/src/main/java/mage/game/command/planes/TurriIslandPlane.java b/Mage/src/main/java/mage/game/command/planes/TurriIslandPlane.java
index 80f76fe91b..5291caf194 100644
--- a/Mage/src/main/java/mage/game/command/planes/TurriIslandPlane.java
+++ b/Mage/src/main/java/mage/game/command/planes/TurriIslandPlane.java
@@ -10,6 +10,7 @@ import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.RevealLibraryPutIntoHandEffect;
 import mage.abilities.effects.common.RollPlanarDieEffect;
 import mage.abilities.effects.common.cost.CostModificationEffectImpl;
+import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect;
 import mage.cards.Card;
 import mage.constants.*;
 import mage.filter.FilterCard;
diff --git a/Mage/src/main/java/mage/game/command/planes/UndercityReachesPlane.java b/Mage/src/main/java/mage/game/command/planes/UndercityReachesPlane.java
index b9b1168cc7..8c0eb0de09 100644
--- a/Mage/src/main/java/mage/game/command/planes/UndercityReachesPlane.java
+++ b/Mage/src/main/java/mage/game/command/planes/UndercityReachesPlane.java
@@ -12,6 +12,7 @@ import mage.abilities.effects.common.DrawCardTargetEffect;
 import mage.abilities.effects.common.RollPlanarDieEffect;
 import mage.abilities.effects.common.continuous.MaximumHandSizeControllerEffect;
 import mage.abilities.effects.common.continuous.MaximumHandSizeControllerEffect.HandSizeModification;
+import mage.abilities.effects.common.cost.PlanarDieRollCostIncreasingEffect;
 import mage.constants.*;
 import mage.filter.FilterCard;
 import mage.game.Game;

From d2d892a7cbb736361b25aa6b54be3a2400f0b063 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Sat, 27 Jun 2020 23:47:04 +0200
Subject: [PATCH 541/586] * Fixed that permanents under non owner control sine
 they are on the battlefield were no exiled if the controller left the game
 (e.g. Captive Audience) (fixes #5593).

---
 .../multiplayer/PlayerLeftGameRange1Test.java |  53 +++++-
 .../base/impl/CardTestPlayerAPIImpl.java      | 178 +++++++++---------
 ...dUnderControlOfOpponentOfChoiceEffect.java | 145 +++++++-------
 Mage/src/main/java/mage/game/GameImpl.java    |  36 ++--
 .../java/mage/game/permanent/Permanent.java   |  14 +-
 .../mage/game/permanent/PermanentImpl.java    |  26 +--
 .../main/java/mage/players/PlayerImpl.java    |  91 +++++----
 7 files changed, 308 insertions(+), 235 deletions(-)

diff --git a/Mage.Tests/src/test/java/org/mage/test/multiplayer/PlayerLeftGameRange1Test.java b/Mage.Tests/src/test/java/org/mage/test/multiplayer/PlayerLeftGameRange1Test.java
index c37ce869a2..8699e82cd9 100644
--- a/Mage.Tests/src/test/java/org/mage/test/multiplayer/PlayerLeftGameRange1Test.java
+++ b/Mage.Tests/src/test/java/org/mage/test/multiplayer/PlayerLeftGameRange1Test.java
@@ -1,5 +1,6 @@
 package org.mage.test.multiplayer;
 
+import java.io.FileNotFoundException;
 import mage.constants.MultiplayerAttackOption;
 import mage.constants.PhaseStep;
 import mage.constants.RangeOfInfluence;
@@ -14,8 +15,6 @@ import org.junit.Assert;
 import org.junit.Test;
 import org.mage.test.serverside.base.CardTestMultiPlayerBase;
 
-import java.io.FileNotFoundException;
-
 /**
  * @author LevelX2
  */
@@ -344,4 +343,54 @@ public class PlayerLeftGameRange1Test extends CardTestMultiPlayerBase {
         Assert.assertTrue("Staff of player B could be used", staffPlayerB.isTapped());
 
     }
+
+    /**
+     * Captive Audience doesn't work correctly in multiplayer #5593
+     *
+     * Currently, if the controller of Captive Audience leaves the game, Captive
+     * Audience returns to its owner instead of being exiled.
+     */
+    @Test
+    public void TestCaptiveAudienceGoesToExile() {
+        addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
+        // Captive Audience enters the battlefield under the control of an opponent of your choice.
+        // At the beginning of your upkeep, choose one that hasn't been chosen —
+        // • Your life total becomes 4.
+        // • Discard your hand.
+        // • Each opponent creates five 2/2 black Zombie creature tokens.
+        addCard(Zone.HAND, playerA, "Captive Audience"); // Enchantment {5}{B}{R}
+
+        addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Pillarfield Ox", 1);
+
+        setChoice(playerA, "PlayerA"); // Starting Player
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Captive Audience");
+        setChoice(playerA, "PlayerD");
+
+        setModeChoice(playerD, "1");
+
+        attack(5, playerA, "Silvercoat Lion", playerD);
+        attack(5, playerA, "Pillarfield Ox", playerD);
+
+        setStopAt(5, PhaseStep.POSTCOMBAT_MAIN);
+
+        setStrictChooseMode(true);
+        execute();
+
+        assertAllCommandsUsed();
+
+        assertLife(playerA, 2);
+
+        Assert.assertFalse("Player D is no longer in the game", playerD.isInGame());
+
+        assertPermanentCount(playerD, 0);
+
+        assertPermanentCount(playerA, "Captive Audience", 0);
+        assertGraveyardCount(playerA, "Captive Audience", 0);
+        assertExileCount(playerA, "Captive Audience", 1);
+
+    }
+
 }
diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
index 71ff178235..df9b358c7e 100644
--- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
+++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
@@ -1,5 +1,13 @@
 package org.mage.test.serverside.base.impl;
 
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.UUID;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 import mage.MageObject;
 import mage.Mana;
 import mage.ObjectColor;
@@ -35,15 +43,6 @@ import org.mage.test.player.TestPlayer;
 import org.mage.test.serverside.base.CardTestAPI;
 import org.mage.test.serverside.base.MageTestPlayerBase;
 
-import java.io.FileNotFoundException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.UUID;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.stream.Collectors;
-
 /**
  * API for test initialization and asserting the test results.
  *
@@ -275,7 +274,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
             }
         }
         Assert.assertFalse("Wrong stop command on " + this.stopOnTurn + " / " + this.stopAtStep + " (" + this.stopAtStep.getIndex() + ")"
-                        + " (found actions after stop on " + maxTurn + " / " + maxPhase + ")",
+                + " (found actions after stop on " + maxTurn + " / " + maxPhase + ")",
                 (maxTurn > this.stopOnTurn) || (maxTurn == this.stopOnTurn && maxPhase > this.stopAtStep.getIndex()));
 
         for (Player player : currentGame.getPlayers().values()) {
@@ -508,8 +507,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Add a card to specified zone of specified player.
      *
      * @param gameZone {@link mage.constants.Zone} to add cards to.
-     * @param player   {@link Player} to add cards for. Use either playerA or
-     *                 playerB.
+     * @param player {@link Player} to add cards for. Use either playerA or
+     * playerB.
      * @param cardName Card name in string format.
      */
     @Override
@@ -521,10 +520,10 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Add any amount of cards to specified zone of specified player.
      *
      * @param gameZone {@link mage.constants.Zone} to add cards to.
-     * @param player   {@link Player} to add cards for. Use either playerA or
-     *                 playerB.
+     * @param player {@link Player} to add cards for. Use either playerA or
+     * playerB.
      * @param cardName Card name in string format.
-     * @param count    Amount of cards to be added.
+     * @param count Amount of cards to be added.
      */
     @Override
     public void addCard(Zone gameZone, TestPlayer player, String cardName, int count) {
@@ -535,13 +534,13 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Add any amount of cards to specified zone of specified player.
      *
      * @param gameZone {@link mage.constants.Zone} to add cards to.
-     * @param player   {@link Player} to add cards for. Use either playerA or
-     *                 playerB.
+     * @param player {@link Player} to add cards for. Use either playerA or
+     * playerB.
      * @param cardName Card name in string format.
-     * @param count    Amount of cards to be added.
-     * @param tapped   In case gameZone is Battlefield, determines whether
-     *                 permanent should be tapped. In case gameZone is other than Battlefield,
-     *                 {@link IllegalArgumentException} is thrown
+     * @param count Amount of cards to be added.
+     * @param tapped In case gameZone is Battlefield, determines whether
+     * permanent should be tapped. In case gameZone is other than Battlefield,
+     * {@link IllegalArgumentException} is thrown
      */
     @Override
     public void addCard(Zone gameZone, TestPlayer player, String cardName, int count, boolean tapped) {
@@ -622,7 +621,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Set player's initial life count.
      *
      * @param player {@link Player} to set life count for.
-     * @param life   Life count to set.
+     * @param life Life count to set.
      */
     @Override
     public void setLife(TestPlayer player, int life) {
@@ -699,7 +698,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert player's life count after test execution.
      *
      * @param player {@link Player} to get life for comparison.
-     * @param life   Expected player's life to compare with.
+     * @param life Expected player's life to compare with.
      */
     @Override
     public void assertLife(Player player, int life) throws AssertionError {
@@ -716,14 +715,14 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * params 3b. all: there is at least one creature with the cardName with the
      * different p\t params
      *
-     * @param player    {@link Player} to get creatures for comparison.
-     * @param cardName  Card name to compare with.
-     * @param power     Expected power to compare with.
+     * @param player {@link Player} to get creatures for comparison.
+     * @param cardName Card name to compare with.
+     * @param power Expected power to compare with.
      * @param toughness Expected toughness to compare with.
-     * @param scope     {@link mage.filter.Filter.ComparisonScope} Use ANY, if you
-     *                  want "at least one creature with given name should have specified p\t"
-     *                  Use ALL, if you want "all creature with gived name should have specified
-     *                  p\t"
+     * @param scope {@link mage.filter.Filter.ComparisonScope} Use ANY, if you
+     * want "at least one creature with given name should have specified p\t"
+     * Use ALL, if you want "all creature with gived name should have specified
+     * p\t"
      */
     @Override
     public void assertPowerToughness(Player player, String cardName, int power, int toughness, Filter.ComparisonScope scope)
@@ -813,8 +812,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * @param cardName
      * @param ability
      * @param mustHave true if creature should contain ability, false if it
-     *                 should NOT contain it instead
-     * @param count    number of permanents with that ability
+     * should NOT contain it instead
+     * @param count number of permanents with that ability
      * @throws AssertionError
      */
     public void assertAbility(Player player, String cardName, Ability ability, boolean mustHave, int count) throws AssertionError {
@@ -847,7 +846,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert permanent count under player's control.
      *
      * @param player {@link Player} which permanents should be counted.
-     * @param count  Expected count.
+     * @param count Expected count.
      */
     @Override
     public void assertPermanentCount(Player player, int count) throws AssertionError {
@@ -863,9 +862,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     /**
      * Assert permanent count under player's control.
      *
-     * @param player   {@link Player} which permanents should be counted.
+     * @param player {@link Player} which permanents should be counted.
      * @param cardName Name of the cards that should be counted.
-     * @param count    Expected count.
+     * @param count Expected count.
      */
     @Override
     public void assertPermanentCount(Player player, String cardName, int count) throws AssertionError {
@@ -915,8 +914,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert counter count on a permanent
      *
      * @param cardName Name of the cards that should be counted.
-     * @param type     Type of the counter that should be counted.
-     * @param count    Expected count.
+     * @param type Type of the counter that should be counted.
+     * @param count Expected count.
      */
     public void assertCounterCount(String cardName, CounterType type, int count) throws AssertionError {
         this.assertCounterCount(null, cardName, type, count);
@@ -939,8 +938,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert counter count on a card in exile
      *
      * @param cardName Name of the cards that should be counted.
-     * @param type     Type of the counter that should be counted.
-     * @param count    Expected count.
+     * @param type Type of the counter that should be counted.
+     * @param count Expected count.
      */
     public void assertCounterOnExiledCardCount(String cardName, CounterType type, int count) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -963,8 +962,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert counter count on a player
      *
      * @param player The player whos counters should be counted.
-     * @param type   Type of the counter that should be counted.
-     * @param count  Expected count.
+     * @param type Type of the counter that should be counted.
+     * @param count Expected count.
      */
     public void assertCounterCount(Player player, CounterType type, int count) throws AssertionError {
         Assert.assertEquals("(Battlefield) Counter counts are not equal (" + player.getName() + ':' + type + ')', count, player.getCounters().getCount(type));
@@ -974,7 +973,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert whether a permanent is a specified type or not
      *
      * @param cardName Name of the permanent that should be checked.
-     * @param type     A type to test for
+     * @param type A type to test for
      * @param mustHave true if creature should have type, false if it should not
      */
     public void assertType(String cardName, CardType type, boolean mustHave) throws AssertionError {
@@ -999,8 +998,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert whether a permanent is a specified type
      *
      * @param cardName Name of the permanent that should be checked.
-     * @param type     A type to test for
-     * @param subType  a subtype to test for
+     * @param type A type to test for
+     * @param subType a subtype to test for
      */
     public void assertType(String cardName, CardType type, SubType subType) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -1015,7 +1014,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert whether a permanent is not a specified type
      *
      * @param cardName Name of the permanent that should be checked.
-     * @param type     A type to test for
+     * @param type A type to test for
      */
     public void assertNotType(String cardName, CardType type) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -1027,7 +1026,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert whether a permanent is not a specified subtype
      *
      * @param cardName Name of the permanent that should be checked.
-     * @param subType  a subtype to test for
+     * @param subType a subtype to test for
      */
     public void assertNotSubtype(String cardName, SubType subType) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -1040,10 +1039,10 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     /**
      * Assert permanent color
      *
-     * @param player       player to check
-     * @param cardName     card name on battlefield from player
+     * @param player player to check
+     * @param cardName card name on battlefield from player
      * @param searchColors colors list with searchable values
-     * @param mustHave     must or not must have that colors
+     * @param mustHave must or not must have that colors
      */
     public void assertColor(Player player, String cardName, ObjectColor searchColors, boolean mustHave) {
         //Assert.assertNotEquals("", cardName);
@@ -1078,7 +1077,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert whether a permanent is tapped or not
      *
      * @param cardName Name of the permanent that should be checked.
-     * @param tapped   Whether the permanent is tapped or not
+     * @param tapped Whether the permanent is tapped or not
      */
     public void assertTapped(String cardName, boolean tapped) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -1105,8 +1104,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert whether X permanents of the same name are tapped or not.
      *
      * @param cardName Name of the permanent that should be checked.
-     * @param tapped   Whether the permanent is tapped or not
-     * @param count    The amount of this permanents that should be tapped
+     * @param tapped Whether the permanent is tapped or not
+     * @param count The amount of this permanents that should be tapped
      */
     public void assertTappedCount(String cardName, boolean tapped, int count) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -1128,7 +1127,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     /**
      * Assert whether a permanent is attacking or not
      *
-     * @param cardName  Name of the permanent that should be checked.
+     * @param cardName Name of the permanent that should be checked.
      * @param attacking Whether the permanent is attacking or not
      */
     public void assertAttacking(String cardName, boolean attacking) throws AssertionError {
@@ -1150,7 +1149,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert card count in player's hand.
      *
      * @param player {@link Player} who's hand should be counted.
-     * @param count  Expected count.
+     * @param count Expected count.
      */
     public void assertHandCount(Player player, int count) throws AssertionError {
         int actual = currentGame.getPlayer(player.getId()).getHand().size();
@@ -1160,9 +1159,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     /**
      * Assert card count in player's hand.
      *
-     * @param player   {@link Player} who's hand should be counted.
+     * @param player {@link Player} who's hand should be counted.
      * @param cardName Name of the cards that should be counted.
-     * @param count    Expected count.
+     * @param count Expected count.
      */
     public void assertHandCount(Player player, String cardName, int count) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -1210,7 +1209,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert card count in player's graveyard.
      *
      * @param player {@link Player} who's graveyard should be counted.
-     * @param count  Expected count.
+     * @param count Expected count.
      */
     public void assertGraveyardCount(Player player, int count) throws AssertionError {
         int actual = currentGame.getPlayer(player.getId()).getGraveyard().size();
@@ -1221,7 +1220,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert card count in exile.
      *
      * @param cardName Name of the cards that should be counted.
-     * @param count    Expected count.
+     * @param count Expected count.
      */
     public void assertExileCount(String cardName, int count) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -1259,9 +1258,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     /**
      * Assert card count in player's exile.
      *
-     * @param owner    {@link Player} who's exile should be counted.
+     * @param owner {@link Player} who's exile should be counted.
      * @param cardName Name of the cards that should be counted.
-     * @param count    Expected count.
+     * @param count Expected count.
      */
     public void assertExileCount(Player owner, String cardName, int count) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -1280,9 +1279,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     /**
      * Assert card count in player's graveyard.
      *
-     * @param player   {@link Player} who's graveyard should be counted.
+     * @param player {@link Player} who's graveyard should be counted.
      * @param cardName Name of the cards that should be counted.
-     * @param count    Expected count.
+     * @param count Expected count.
      */
     public void assertGraveyardCount(Player player, String cardName, int count) throws AssertionError {
         assertAliaseSupportInActivateCommand(cardName, true);
@@ -1301,7 +1300,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert library card count.
      *
      * @param player {@link Player} who's library should be counted.
-     * @param count  Expected count.
+     * @param count Expected count.
      */
     public void assertLibraryCount(Player player, int count) throws AssertionError {
         List<Card> libraryList = player.getLibrary().getCards(currentGame);
@@ -1312,9 +1311,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     /**
      * Assert specific card count in player's library.
      *
-     * @param player   {@link Player} who's library should be counted.
+     * @param player {@link Player} who's library should be counted.
      * @param cardName Name of the cards that should be counted.
-     * @param count    Expected count.
+     * @param count Expected count.
      */
     public void assertLibraryCount(Player player, String cardName, int count) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -1353,8 +1352,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     }
 
     /**
-     * Raise error on any unused commands, choices or targets
-     * If you want to test that ability can't be activated then use call checkPlayableAbility()
+     * Raise error on any unused commands, choices or targets If you want to
+     * test that ability can't be activated then use call checkPlayableAbility()
      *
      * @throws AssertionError
      */
@@ -1501,8 +1500,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * @param player
      * @param cardName
      * @param targetName for modes you can add "mode=3" before target name,
-     *                   multiple targets can be seperated by ^, not target marks as
-     *                   TestPlayer.NO_TARGET
+     * multiple targets can be seperated by ^, not target marks as
+     * TestPlayer.NO_TARGET
      */
     public void castSpell(int turnNum, PhaseStep step, TestPlayer player, String cardName, String targetName) {
         //Assert.assertNotEquals("", cardName);
@@ -1525,8 +1524,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * @param step
      * @param player
      * @param cardName
-     * @param targetName   for modal spells add the mode to the name e.g.
-     *                     "mode=2SilvercoatLion^mode3=PillarfieldOx"
+     * @param targetName for modal spells add the mode to the name e.g.
+     * "mode=2SilvercoatLion^mode3=PillarfieldOx"
      * @param spellOnStack
      */
     public void castSpell(int turnNum, PhaseStep step, TestPlayer player, String cardName, String targetName, String spellOnStack) {
@@ -1613,7 +1612,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * @param step
      * @param player
      * @param ability
-     * @param targetName   use NO_TARGET if there is no target to set
+     * @param targetName use NO_TARGET if there is no target to set
      * @param spellOnStack
      */
     public void activateAbility(int turnNum, PhaseStep step, TestPlayer player, String ability, String targetName, String spellOnStack) {
@@ -1626,8 +1625,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * @param step
      * @param player
      * @param ability
-     * @param targetName   if not target has to be defined use the constant
-     *                     NO_TARGET
+     * @param targetName if not target has to be defined use the constant
+     * NO_TARGET
      * @param spellOnStack
      * @param clause
      */
@@ -1691,9 +1690,12 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     }
 
     /**
-     * For use choices set "Yes" or "No" the the choice string. For X values set
-     * "X=[xValue]" example: for X=3 set choice string to "X=3".
-     * <br>For ColorChoice use "Red", "Green", "Blue", "Black" or "White"
+     * For use choices set "Yes" or "No" the the choice string.<br>
+     * For X values set "X=[xValue]" example: for X=3 set choice string to
+     * "X=3".<br>
+     * For ColorChoice use "Red", "Green", "Blue", "Black" or "White"<br>
+     * use command setModeChoice if you have to set a mode from modal
+     * ability<br>
      *
      * @param player
      * @param choice
@@ -1713,10 +1715,10 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      *
      * @param player
      * @param choice starting with "1" for mode 1, "2" for mode 2 and so on (to
-     *               set multiple modes call the command multiple times). If a spell mode can
-     *               be used only once like Demonic Pact, the value has to be set to the
-     *               number of the remaining modes (e.g. if only 2 are left the number need to
-     *               be 1 or 2).
+     * set multiple modes call the command multiple times). If a spell mode can
+     * be used only once like Demonic Pact, the value has to be set to the
+     * number of the remaining modes (e.g. if only 2 are left the number need to
+     * be 1 or 2).
      */
     public void setModeChoice(TestPlayer player, String choice) {
         player.addModeChoice(choice);
@@ -1727,12 +1729,12 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      *
      * @param player
      * @param target you can add multiple targets by separating them by the "^"
-     *               character e.g. "creatureName1^creatureName2" you can qualify the target
-     *               additional by setcode e.g. "creatureName-M15" you can add [no copy] to
-     *               the end of the target name to prohibit targets that are copied you can
-     *               add [only copy] to the end of the target name to allow only targets that
-     *               are copies. For modal spells use a prefix with the mode number:
-     *               mode=1Lightning Bolt^mode=2Silvercoat Lion
+     * character e.g. "creatureName1^creatureName2" you can qualify the target
+     * additional by setcode e.g. "creatureName-M15" you can add [no copy] to
+     * the end of the target name to prohibit targets that are copied you can
+     * add [only copy] to the end of the target name to allow only targets that
+     * are copies. For modal spells use a prefix with the mode number:
+     * mode=1Lightning Bolt^mode=2Silvercoat Lion
      */
     // TODO: mode options doesn't work here (see BrutalExpulsionTest)
     public void addTarget(TestPlayer player, String target) {
@@ -1764,7 +1766,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     /**
      * @param player
      * @param target use TestPlayer.TARGET_SKIP to 0 targets selects or to stop
-     *               "up two xxx" selection
+     * "up two xxx" selection
      * @param amount
      */
     public void addTargetAmount(TestPlayer player, String target, int amount) {
diff --git a/Mage/src/main/java/mage/abilities/effects/common/EntersBattlefieldUnderControlOfOpponentOfChoiceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/EntersBattlefieldUnderControlOfOpponentOfChoiceEffect.java
index 2d3a8c5639..5b1b4f4e94 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/EntersBattlefieldUnderControlOfOpponentOfChoiceEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/EntersBattlefieldUnderControlOfOpponentOfChoiceEffect.java
@@ -1,72 +1,73 @@
-/*
- * To change this license header, choose License Headers in Project Properties.
- * To change this template file, choose Tools | Templates
- * and open the template in the editor.
- */
-package mage.abilities.effects.common;
-
-import mage.abilities.Ability;
-import mage.abilities.effects.ContinuousEffect;
-import mage.abilities.effects.OneShotEffect;
-import mage.abilities.effects.common.continuous.GainControlTargetEffect;
-import mage.constants.Duration;
-import mage.constants.Outcome;
-import static mage.constants.Outcome.Benefit;
-import mage.game.Game;
-import mage.game.permanent.Permanent;
-import mage.players.Player;
-import mage.target.Target;
-import mage.target.common.TargetOpponent;
-import mage.target.targetpointer.FixedTarget;
-
-/**
- * Use this effect only with EntersBattlefieldAbility like abilities
- *
- * @author LevelX2
- */
-
-public class EntersBattlefieldUnderControlOfOpponentOfChoiceEffect extends OneShotEffect {
-
-    public EntersBattlefieldUnderControlOfOpponentOfChoiceEffect() {
-        super(Benefit);
-        staticText = "under the control of an opponent of your choice";
-    }
-
-    private EntersBattlefieldUnderControlOfOpponentOfChoiceEffect(final EntersBattlefieldUnderControlOfOpponentOfChoiceEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public EntersBattlefieldUnderControlOfOpponentOfChoiceEffect copy() {
-        return new EntersBattlefieldUnderControlOfOpponentOfChoiceEffect(this);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        Player controller = game.getPlayer(source.getControllerId());
-        if (controller == null) {
-            return false;
-        }
-        Target target = new TargetOpponent();
-        target.setNotTarget(true);
-        if (!controller.choose(Outcome.Benefit, target, source.getSourceId(), game)) {
-            return false;
-        }
-        Player opponent = game.getPlayer(target.getFirstTarget());
-        if (opponent == null) {
-            return false;
-        }
-        Permanent permanent = game.getPermanentEntering(source.getSourceId());
-        if (permanent != null) {
-            game.informPlayers(permanent.getLogName() + " enters the battlefield under the control of " + opponent.getLogName());
-        }
-        ContinuousEffect continuousEffect = new GainControlTargetEffect(
-                Duration.Custom, true, opponent.getId()
-        );
-        continuousEffect.setTargetPointer(new FixedTarget(
-                source.getSourceId(), source.getSourceObjectZoneChangeCounter()
-        ));
-        game.addEffect(continuousEffect, source);
-        return true;
-    }
-}
\ No newline at end of file
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package mage.abilities.effects.common;
+
+import mage.abilities.Ability;
+import mage.abilities.effects.ContinuousEffect;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.continuous.GainControlTargetEffect;
+import mage.constants.Duration;
+import mage.constants.Outcome;
+import static mage.constants.Outcome.Benefit;
+import mage.game.Game;
+import mage.game.permanent.Permanent;
+import mage.players.Player;
+import mage.target.Target;
+import mage.target.common.TargetOpponent;
+import mage.target.targetpointer.FixedTarget;
+
+/**
+ * Use this effect only with EntersBattlefieldAbility like abilities
+ *
+ * @author LevelX2
+ */
+public class EntersBattlefieldUnderControlOfOpponentOfChoiceEffect extends OneShotEffect {
+
+    public EntersBattlefieldUnderControlOfOpponentOfChoiceEffect() {
+        super(Benefit);
+        staticText = "under the control of an opponent of your choice";
+    }
+
+    private EntersBattlefieldUnderControlOfOpponentOfChoiceEffect(final EntersBattlefieldUnderControlOfOpponentOfChoiceEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public EntersBattlefieldUnderControlOfOpponentOfChoiceEffect copy() {
+        return new EntersBattlefieldUnderControlOfOpponentOfChoiceEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Player controller = game.getPlayer(source.getControllerId());
+        if (controller == null) {
+            return false;
+        }
+        Target target = new TargetOpponent();
+        target.setNotTarget(true);
+        if (!controller.choose(Outcome.Benefit, target, source.getSourceId(), game)) {
+            return false;
+        }
+        Player opponent = game.getPlayer(target.getFirstTarget());
+        if (opponent == null) {
+            return false;
+        }
+        Permanent permanent = game.getPermanentEntering(source.getSourceId());
+        if (permanent != null) {
+            permanent.setOriginalControllerId(opponent.getId()); // permanent was controlled by this player since the existance of this object so original controller has to be set to the first controller
+            permanent.setControllerId(opponent.getId()); // neccessary to set already here because spell caster never controlled the permanent (important for rule 800.4a)
+            game.informPlayers(permanent.getLogName() + " enters the battlefield under the control of " + opponent.getLogName());
+        }
+        ContinuousEffect continuousEffect = new GainControlTargetEffect(
+                Duration.Custom, true, opponent.getId()
+        );
+        continuousEffect.setTargetPointer(new FixedTarget(
+                source.getSourceId(), source.getSourceObjectZoneChangeCounter()
+        ));
+        game.addEffect(continuousEffect, source);
+        return true;
+    }
+}
diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java
index 673c2f056f..90a360c070 100644
--- a/Mage/src/main/java/mage/game/GameImpl.java
+++ b/Mage/src/main/java/mage/game/GameImpl.java
@@ -1,5 +1,9 @@
 package mage.game;
 
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.*;
+import java.util.Map.Entry;
 import mage.MageException;
 import mage.MageObject;
 import mage.abilities.*;
@@ -67,11 +71,6 @@ import mage.util.functions.ApplyToPermanent;
 import mage.watchers.common.*;
 import org.apache.log4j.Logger;
 
-import java.io.IOException;
-import java.io.Serializable;
-import java.util.*;
-import java.util.Map.Entry;
-
 public abstract class GameImpl implements Game, Serializable {
 
     private static final int ROLLBACK_TURNS_MAX = 4;
@@ -562,7 +561,7 @@ public abstract class GameImpl implements Game, Serializable {
 
     @Override
     public void saveState(boolean bookmark) {
-        if (!simulation && gameStates != null) {            
+        if (!simulation && gameStates != null) {
             if (bookmark || saveGame) {
                 gameStates.save(state);
             }
@@ -1549,7 +1548,7 @@ public abstract class GameImpl implements Game, Serializable {
     /**
      * @param emblem
      * @param sourceObject
-     * @param toPlayerId   controller and owner of the emblem
+     * @param toPlayerId controller and owner of the emblem
      */
     @Override
     public void addEmblem(Emblem emblem, MageObject sourceObject, UUID toPlayerId) {
@@ -1567,8 +1566,8 @@ public abstract class GameImpl implements Game, Serializable {
     /**
      * @param plane
      * @param sourceObject
-     * @param toPlayerId   controller and owner of the plane (may only be one per
-     *                     game..)
+     * @param toPlayerId controller and owner of the plane (may only be one per
+     * game..)
      * @return boolean - whether the plane was added successfully or not
      */
     @Override
@@ -1642,7 +1641,7 @@ public abstract class GameImpl implements Game, Serializable {
             newBluePrint.reset(this);
 
             //getState().addCard(permanent);
-            if (copyFromPermanent.isMorphed() || copyFromPermanent.isManifested() 
+            if (copyFromPermanent.isMorphed() || copyFromPermanent.isManifested()
                     || copyFromPermanent.isFaceDown(this)) {
                 MorphAbility.setPermanentToFaceDownCreature(newBluePrint);
             }
@@ -1805,7 +1804,7 @@ public abstract class GameImpl implements Game, Serializable {
                     break;
                 }
                 // triggered abilities that don't use the stack have to be executed first (e.g. Banisher Priest Return exiled creature
-                for (Iterator<TriggeredAbility> it = abilities.iterator(); it.hasNext(); ) {
+                for (Iterator<TriggeredAbility> it = abilities.iterator(); it.hasNext();) {
                     TriggeredAbility triggeredAbility = it.next();
                     if (!triggeredAbility.isUsesStack()) {
                         state.removeTriggeredAbility(triggeredAbility);
@@ -1880,7 +1879,7 @@ public abstract class GameImpl implements Game, Serializable {
                 Zone currentZone = this.getState().getZone(card.getId());
                 String currentZoneInfo = (currentZone == null ? "(error)" : "(" + currentZone.name() + ")");
                 if (player.chooseUse(Outcome.Benefit, "Move " + card.getIdName()
-                                + " to the command zone or leave it in current zone " + currentZoneInfo + "?", "You can only make this choice once per object",
+                        + " to the command zone or leave it in current zone " + currentZoneInfo + "?", "You can only make this choice once per object",
                         "Move to command", "Leave in current zone " + currentZoneInfo, null, this)) {
                     toMove.add(card);
                 } else {
@@ -2596,7 +2595,7 @@ public abstract class GameImpl implements Game, Serializable {
         }
         //20100423 - 800.4a
         Set<Card> toOutside = new HashSet<>();
-        for (Iterator<Permanent> it = getBattlefield().getAllPermanents().iterator(); it.hasNext(); ) {
+        for (Iterator<Permanent> it = getBattlefield().getAllPermanents().iterator(); it.hasNext();) {
             Permanent perm = it.next();
             if (perm.isOwnedBy(playerId)) {
                 if (perm.getAttachedTo() != null) {
@@ -2621,6 +2620,10 @@ public abstract class GameImpl implements Game, Serializable {
                 for (ContinuousEffect effect : getContinuousEffects().getLayeredEffects(this)) {
                     if (effect.hasLayer(Layer.ControlChangingEffects_2)) {
                         for (Ability ability : getContinuousEffects().getLayeredEffectAbilities(effect)) {
+                            if (effect.getTargetPointer().getTargets(this, ability).contains(perm.getId())) {
+                                effect.discard();
+                                continue Effects;
+                            }
                             for (Target target : ability.getTargets()) {
                                 for (UUID targetId : target.getTargets()) {
                                     if (targetId.equals(perm.getId())) {
@@ -2630,6 +2633,7 @@ public abstract class GameImpl implements Game, Serializable {
                                 }
                             }
                         }
+
                     }
                 }
             }
@@ -2641,7 +2645,7 @@ public abstract class GameImpl implements Game, Serializable {
         player.moveCards(toOutside, Zone.OUTSIDE, null, this);
         // triggered abilities that don't use the stack have to be executed
         List<TriggeredAbility> abilities = state.getTriggered(player.getId());
-        for (Iterator<TriggeredAbility> it = abilities.iterator(); it.hasNext(); ) {
+        for (Iterator<TriggeredAbility> it = abilities.iterator(); it.hasNext();) {
             TriggeredAbility triggeredAbility = it.next();
             if (!triggeredAbility.isUsesStack()) {
                 state.removeTriggeredAbility(triggeredAbility);
@@ -2661,7 +2665,7 @@ public abstract class GameImpl implements Game, Serializable {
 
         // Remove cards from the player in all exile zones
         for (ExileZone exile : this.getExile().getExileZones()) {
-            for (Iterator<UUID> it = exile.iterator(); it.hasNext(); ) {
+            for (Iterator<UUID> it = exile.iterator(); it.hasNext();) {
                 Card card = this.getCard(it.next());
                 if (card != null && card.isOwnedBy(playerId)) {
                     it.remove();
@@ -2671,7 +2675,7 @@ public abstract class GameImpl implements Game, Serializable {
 
         //Remove all commander/emblems/plane the player controls
         boolean addPlaneAgain = false;
-        for (Iterator<CommandObject> it = this.getState().getCommand().iterator(); it.hasNext(); ) {
+        for (Iterator<CommandObject> it = this.getState().getCommand().iterator(); it.hasNext();) {
             CommandObject obj = it.next();
             if (obj.isControlledBy(playerId)) {
                 if (obj instanceof Emblem) {
diff --git a/Mage/src/main/java/mage/game/permanent/Permanent.java b/Mage/src/main/java/mage/game/permanent/Permanent.java
index 7999673d27..f9612d1925 100644
--- a/Mage/src/main/java/mage/game/permanent/Permanent.java
+++ b/Mage/src/main/java/mage/game/permanent/Permanent.java
@@ -1,5 +1,8 @@
 package mage.game.permanent;
 
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
 import mage.MageObject;
 import mage.MageObjectReference;
 import mage.abilities.Ability;
@@ -10,12 +13,10 @@ import mage.game.Controllable;
 import mage.game.Game;
 import mage.game.GameState;
 
-import java.util.List;
-import java.util.Set;
-import java.util.UUID;
-
 public interface Permanent extends Card, Controllable {
 
+    void setOriginalControllerId(UUID controllerId);
+
     void setControllerId(UUID controllerId);
 
     boolean isTapped();
@@ -103,7 +104,8 @@ public interface Permanent extends Card, Controllable {
     /**
      * @param source
      * @param game
-     * @param silentMode - use it to ignore warning message for users (e.g. for checking only)
+     * @param silentMode - use it to ignore warning message for users (e.g. for
+     * checking only)
      * @return
      */
     boolean cantBeAttachedBy(MageObject source, Game game, boolean silentMode);
@@ -217,7 +219,7 @@ public interface Permanent extends Card, Controllable {
 
     /**
      * @param defenderId id of planeswalker or player to attack - can be empty
-     *                   to check generally
+     * to check generally
      * @param game
      * @return
      */
diff --git a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java
index b2e8fa813d..44b0261490 100644
--- a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java
+++ b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java
@@ -1,5 +1,7 @@
 package mage.game.permanent;
 
+import java.io.Serializable;
+import java.util.*;
 import mage.MageObject;
 import mage.MageObjectReference;
 import mage.ObjectColor;
@@ -38,9 +40,6 @@ import mage.util.GameLog;
 import mage.util.ThreadLocalStringBuilder;
 import org.apache.log4j.Logger;
 
-import java.io.Serializable;
-import java.util.*;
-
 /**
  * @author BetaSteward_at_googlemail.com
  */
@@ -184,6 +183,11 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
         abilities.setControllerId(controllerId);
     }
 
+    @Override
+    public void setOriginalControllerId(UUID originalControllerId) {
+        this.originalControllerId = originalControllerId;
+    }
+
     /**
      * Called before each applyEffects or if after a permanent was copied for
      * the copied object
@@ -793,7 +797,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
         this.attachedTo = attachToObjectId;
         this.attachedToZoneChangeCounter = game.getState().getZoneChangeCounter(attachToObjectId);
         for (Ability ability : this.getAbilities()) {
-            for (Iterator<Effect> ite = ability.getEffects(game, EffectType.CONTINUOUS).iterator(); ite.hasNext(); ) {
+            for (Iterator<Effect> ite = ability.getEffects(game, EffectType.CONTINUOUS).iterator(); ite.hasNext();) {
                 ContinuousEffect effect = (ContinuousEffect) ite.next();
                 game.getContinuousEffects().setOrder(effect);
                 // It's important to update the timestamp of the copied effect in ContinuousEffects because it does the action
@@ -848,8 +852,8 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
      * @param game
      * @param preventable
      * @param combat
-     * @param markDamage   If true, damage will be dealt later in applyDamage
-     *                     method
+     * @param markDamage If true, damage will be dealt later in applyDamage
+     * method
      * @return
      */
     private int damage(int damageAmount, UUID sourceId, Game game, boolean preventable, boolean combat, boolean markDamage, List<UUID> appliedEffects) {
@@ -1066,9 +1070,9 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
             if (game.getPlayer(this.getControllerId()).hasOpponent(sourceControllerId, game)
                     && game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, null, sourceControllerId, game) == null
                     && abilities.stream()
-                    .filter(HexproofBaseAbility.class::isInstance)
-                    .map(HexproofBaseAbility.class::cast)
-                    .anyMatch(ability -> ability.checkObject(source, game))) {
+                            .filter(HexproofBaseAbility.class::isInstance)
+                            .map(HexproofBaseAbility.class::cast)
+                            .anyMatch(ability -> ability.checkObject(source, game))) {
                 return false;
             }
 
@@ -1619,9 +1623,9 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
     public boolean moveToExile(UUID exileId, String name, UUID sourceId, Game game, List<UUID> appliedEffects) {
         Zone fromZone = game.getState().getZone(objectId);
         ZoneChangeEvent event = new ZoneChangeEvent(this, sourceId, ownerId, fromZone, Zone.EXILED, appliedEffects);
-        ZoneChangeInfo.Exile info = new ZoneChangeInfo.Exile(event, exileId, name);
+        ZoneChangeInfo.Exile zcInfo = new ZoneChangeInfo.Exile(event, exileId, name);
 
-        boolean successfullyMoved = ZonesHandler.moveCard(info, game);
+        boolean successfullyMoved = ZonesHandler.moveCard(zcInfo, game);
         //20180810 - 701.3d
         detachAllAttachments(game);
         return successfullyMoved;
diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java
index 696e98884e..0cba9775d2 100644
--- a/Mage/src/main/java/mage/players/PlayerImpl.java
+++ b/Mage/src/main/java/mage/players/PlayerImpl.java
@@ -1,6 +1,10 @@
 package mage.players;
 
 import com.google.common.collect.ImmutableMap;
+import java.io.Serializable;
+import java.util.*;
+import java.util.Map.Entry;
+import java.util.stream.Collectors;
 import mage.ConditionalMana;
 import mage.MageObject;
 import mage.MageObjectReference;
@@ -66,11 +70,6 @@ import mage.util.GameLog;
 import mage.util.RandomUtil;
 import org.apache.log4j.Logger;
 
-import java.io.Serializable;
-import java.util.*;
-import java.util.Map.Entry;
-import java.util.stream.Collectors;
-
 public abstract class PlayerImpl implements Player, Serializable {
 
     private static final Logger logger = Logger.getLogger(PlayerImpl.class);
@@ -614,9 +613,9 @@ public abstract class PlayerImpl implements Player, Serializable {
                     && this.hasOpponent(sourceControllerId, game)
                     && game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, null, sourceControllerId, game) == null
                     && abilities.stream()
-                    .filter(HexproofBaseAbility.class::isInstance)
-                    .map(HexproofBaseAbility.class::cast)
-                    .anyMatch(ability -> ability.checkObject(source, game))) {
+                            .filter(HexproofBaseAbility.class::isInstance)
+                            .map(HexproofBaseAbility.class::cast)
+                            .anyMatch(ability -> ability.checkObject(source, game))) {
                 return false;
             }
 
@@ -656,7 +655,7 @@ public abstract class PlayerImpl implements Player, Serializable {
                 game.informPlayers(getLogName() + " discards down to "
                         + this.maxHandSize
                         + (this.maxHandSize == 1
-                        ? " hand card" : " hand cards"));
+                                ? " hand card" : " hand cards"));
             }
             discard(hand.size() - this.maxHandSize, false, null, game);
         }
@@ -805,7 +804,7 @@ public abstract class PlayerImpl implements Player, Serializable {
         }
         GameEvent gameEvent = GameEvent.getEvent(GameEvent.EventType.DISCARD_CARD,
                 card.getId(), source == null
-                        ? null : source.getSourceId(), playerId);
+                ? null : source.getSourceId(), playerId);
         gameEvent.setFlag(source != null); // event from effect or from cost (source == null)
         if (game.replaceEvent(gameEvent, source)) {
             return false;
@@ -1842,9 +1841,9 @@ public abstract class PlayerImpl implements Player, Serializable {
     }
 
     private List<Permanent> getPermanentsThatCanBeUntapped(Game game,
-                                                           List<Permanent> canBeUntapped,
-                                                           RestrictionUntapNotMoreThanEffect handledEffect,
-                                                           Map<Entry<RestrictionUntapNotMoreThanEffect, Set<Ability>>, Integer> notMoreThanEffectsUsage) {
+            List<Permanent> canBeUntapped,
+            RestrictionUntapNotMoreThanEffect handledEffect,
+            Map<Entry<RestrictionUntapNotMoreThanEffect, Set<Ability>>, Integer> notMoreThanEffectsUsage) {
         List<Permanent> leftForUntap = new ArrayList<>();
         // select permanents that can still be untapped
         for (Permanent permanent : canBeUntapped) {
@@ -2553,7 +2552,7 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean searchLibrary(TargetCardInLibrary target, Ability source, Game game, UUID targetPlayerId,
-                                 boolean triggerEvents) {
+            boolean triggerEvents) {
         //20091005 - 701.14c
         Library searchedLibrary = null;
         String searchInfo = null;
@@ -2755,7 +2754,7 @@ public abstract class PlayerImpl implements Player, Serializable {
     /**
      * @param game
      * @param appliedEffects
-     * @param numSides       Number of sides the dice has
+     * @param numSides Number of sides the dice has
      * @return the number that the player rolled
      */
     @Override
@@ -2792,16 +2791,16 @@ public abstract class PlayerImpl implements Player, Serializable {
     /**
      * @param game
      * @param appliedEffects
-     * @param numberChaosSides  The number of chaos sides the planar die
-     *                          currently has (normally 1 but can be 5)
+     * @param numberChaosSides The number of chaos sides the planar die
+     * currently has (normally 1 but can be 5)
      * @param numberPlanarSides The number of chaos sides the planar die
-     *                          currently has (normally 1)
+     * currently has (normally 1)
      * @return the outcome that the player rolled. Either ChaosRoll, PlanarRoll
      * or NilRoll
      */
     @Override
     public PlanarDieRoll rollPlanarDie(Game game, List<UUID> appliedEffects, int numberChaosSides,
-                                       int numberPlanarSides) {
+            int numberPlanarSides) {
         int result = RandomUtil.nextInt(9) + 1;
         PlanarDieRoll roll = PlanarDieRoll.NIL_ROLL;
         if (numberChaosSides + numberPlanarSides > 9) {
@@ -2958,7 +2957,8 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     /**
      * @param ability
-     * @param availableMana if null, it won't be checked if enough mana is available
+     * @param availableMana if null, it won't be checked if enough mana is
+     * available
      * @param sourceObject
      * @param game
      * @return
@@ -3290,6 +3290,17 @@ public abstract class PlayerImpl implements Player, Serializable {
         return getPlayable(game, hidden, Zone.ALL, true);
     }
 
+    /**
+     * Returns a list of all available spells and abilities the player can
+     * currently cast/activate with his available ressources
+     *
+     * @param game
+     * @param hidden also from hidden objects (e.g. turned face down cards ?)
+     * @param fromZone of objects from which zone (ALL = from all zones)
+     * @param hideDuplicatedAbilities if equal abilities exist return only the
+     * first instance
+     * @return
+     */
     public List<ActivatedAbility> getPlayable(Game game, boolean hidden, Zone fromZone, boolean hideDuplicatedAbilities) {
         List<ActivatedAbility> playable = new ArrayList<>();
         if (shouldSkipGettingPlayable(game)) {
@@ -3655,7 +3666,7 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean canPaySacrificeCost(Permanent permanent, UUID sourceId,
-                                       UUID controllerId, Game game
+            UUID controllerId, Game game
     ) {
         return sacrificeCostFilter == null || !sacrificeCostFilter.match(permanent, sourceId, controllerId, game);
     }
@@ -3808,8 +3819,8 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCards(Card card, Zone toZone,
-                             Ability source, Game game,
-                             boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
+            Ability source, Game game,
+            boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
     ) {
         Set<Card> cardList = new HashSet<>();
         if (card != null) {
@@ -3820,22 +3831,22 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCards(Cards cards, Zone toZone,
-                             Ability source, Game game
+            Ability source, Game game
     ) {
         return moveCards(cards.getCards(game), toZone, source, game);
     }
 
     @Override
     public boolean moveCards(Set<Card> cards, Zone toZone,
-                             Ability source, Game game
+            Ability source, Game game
     ) {
         return moveCards(cards, toZone, source, game, false, false, false, null);
     }
 
     @Override
     public boolean moveCards(Set<Card> cards, Zone toZone,
-                             Ability source, Game game,
-                             boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
+            Ability source, Game game,
+            boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
     ) {
         if (cards.isEmpty()) {
             return true;
@@ -3937,8 +3948,8 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCardsToExile(Card card, Ability source,
-                                    Game game, boolean withName, UUID exileId,
-                                    String exileZoneName
+            Game game, boolean withName, UUID exileId,
+            String exileZoneName
     ) {
         Set<Card> cards = new HashSet<>();
         cards.add(card);
@@ -3947,8 +3958,8 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCardsToExile(Set<Card> cards, Ability source,
-                                    Game game, boolean withName, UUID exileId,
-                                    String exileZoneName
+            Game game, boolean withName, UUID exileId,
+            String exileZoneName
     ) {
         if (cards.isEmpty()) {
             return true;
@@ -3964,14 +3975,14 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCardToHandWithInfo(Card card, UUID sourceId,
-                                          Game game
+            Game game
     ) {
         return this.moveCardToHandWithInfo(card, sourceId, game, true);
     }
 
     @Override
     public boolean moveCardToHandWithInfo(Card card, UUID sourceId,
-                                          Game game, boolean withName
+            Game game, boolean withName
     ) {
         boolean result = false;
         Zone fromZone = game.getState().getZone(card.getId());
@@ -3996,7 +4007,7 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public Set<Card> moveCardsToGraveyardWithInfo(Set<Card> allCards, Ability source,
-                                                  Game game, Zone fromZone
+            Game game, Zone fromZone
     ) {
         UUID sourceId = source == null ? null : source.getSourceId();
         Set<Card> movedCards = new LinkedHashSet<>();
@@ -4004,7 +4015,7 @@ public abstract class PlayerImpl implements Player, Serializable {
             // identify cards from one owner
             Cards cards = new CardsImpl();
             UUID ownerId = null;
-            for (Iterator<Card> it = allCards.iterator(); it.hasNext(); ) {
+            for (Iterator<Card> it = allCards.iterator(); it.hasNext();) {
                 Card card = it.next();
                 if (cards.isEmpty()) {
                     ownerId = card.getOwnerId();
@@ -4067,7 +4078,7 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCardToGraveyardWithInfo(Card card, UUID sourceId,
-                                               Game game, Zone fromZone
+            Game game, Zone fromZone
     ) {
         if (card == null) {
             return false;
@@ -4096,8 +4107,8 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCardToLibraryWithInfo(Card card, UUID sourceId,
-                                             Game game, Zone fromZone,
-                                             boolean toTop, boolean withName
+            Game game, Zone fromZone,
+            boolean toTop, boolean withName
     ) {
         if (card == null) {
             return false;
@@ -4162,7 +4173,7 @@ public abstract class PlayerImpl implements Player, Serializable {
 
     @Override
     public boolean moveCardToExileWithInfo(Card card, UUID exileId, String exileName, UUID sourceId,
-                                           Game game, Zone fromZone, boolean withName) {
+            Game game, Zone fromZone, boolean withName) {
         if (card == null) {
             return false;
         }
@@ -4185,7 +4196,7 @@ public abstract class PlayerImpl implements Player, Serializable {
                 game.informPlayers(this.getLogName() + " moves " + (withName ? card.getLogName()
                         + (card.isCopy() ? " (Copy)" : "") : "a card face down") + ' '
                         + (fromZone != null ? "from " + fromZone.toString().toLowerCase(Locale.ENGLISH)
-                        + ' ' : "") + "to the exile zone");
+                                + ' ' : "") + "to the exile zone");
 
             }
             result = true;

From 621c144ac2676b3ff70ffeb1d95f84cd4a12cdea Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Sun, 28 Jun 2020 20:29:09 +0400
Subject: [PATCH 542/586] * This spell costs less to cast - added card hints
 for some cards;

---
 .../src/mage/cards/a/AcademyJourneymage.java      | 15 +++++++++------
 Mage.Sets/src/mage/cards/d/Discontinuity.java     |  4 +++-
 Mage.Sets/src/mage/cards/g/GustOfWind.java        |  6 ++++--
 Mage.Sets/src/mage/cards/h/HourOfRevelation.java  | 11 +++++++----
 Mage.Sets/src/mage/cards/i/IgneousElemental.java  | 12 ++++++++----
 Mage.Sets/src/mage/cards/i/IntoTheStory.java      |  6 ++++--
 Mage.Sets/src/mage/cards/l/LookoutsDispersal.java | 10 +++++++---
 Mage.Sets/src/mage/cards/n/NotOfThisWorld.java    |  4 +++-
 Mage.Sets/src/mage/cards/o/OakhameAdversary.java  |  6 ++++--
 .../src/mage/cards/t/TentativeConnection.java     | 11 +++++------
 Mage.Sets/src/mage/cards/w/WingedWords.java       | 14 +++++++++-----
 Mage.Sets/src/mage/cards/w/WizardsLightning.java  | 10 +++++++---
 Mage.Sets/src/mage/cards/w/WizardsRetort.java     | 10 +++++++---
 13 files changed, 77 insertions(+), 42 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/a/AcademyJourneymage.java b/Mage.Sets/src/mage/cards/a/AcademyJourneymage.java
index 239620e279..7036d80a8f 100644
--- a/Mage.Sets/src/mage/cards/a/AcademyJourneymage.java
+++ b/Mage.Sets/src/mage/cards/a/AcademyJourneymage.java
@@ -1,25 +1,26 @@
-
 package mage.cards.a;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.condition.Condition;
 import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
 import mage.abilities.effects.common.ReturnToHandTargetEffect;
 import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect;
-import mage.constants.SubType;
+import mage.abilities.hint.ConditionHint;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
+import mage.constants.SubType;
 import mage.constants.Zone;
 import mage.filter.StaticFilters;
 import mage.filter.common.FilterControlledPermanent;
 import mage.target.common.TargetCreaturePermanent;
 
+import java.util.UUID;
+
 /**
- *
  * @author JRHerlehy
  */
 public final class AcademyJourneymage extends CardImpl {
@@ -32,14 +33,16 @@ public final class AcademyJourneymage extends CardImpl {
 
     public AcademyJourneymage(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}");
-        
+
         this.subtype.add(SubType.HUMAN, SubType.WIZARD);
         this.power = new MageInt(3);
         this.toughness = new MageInt(2);
 
         // This spell costs {1} less to cast if you control a Wizard.
-        Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(1, new PermanentsOnTheBattlefieldCondition(filter)));
+        Condition condition = new PermanentsOnTheBattlefieldCondition(filter);
+        Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(1, condition));
         ability.setRuleAtTheTop(true);
+        ability.addHint(new ConditionHint(condition, "You control a Wizard"));
         this.addAbility(ability);
 
         // When Academy Journeymage enters the battlefield, return target creature an opponent controls to its owner's hand.
diff --git a/Mage.Sets/src/mage/cards/d/Discontinuity.java b/Mage.Sets/src/mage/cards/d/Discontinuity.java
index 93e62861f1..87f82cc923 100644
--- a/Mage.Sets/src/mage/cards/d/Discontinuity.java
+++ b/Mage.Sets/src/mage/cards/d/Discontinuity.java
@@ -5,6 +5,7 @@ import mage.abilities.condition.common.MyTurnCondition;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.common.EndTurnEffect;
 import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect;
+import mage.abilities.hint.common.MyTurnHint;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
@@ -23,7 +24,8 @@ public final class Discontinuity extends CardImpl {
         // As long as it's your turn, this spell costs {2}{U}{U} less to cast.
         this.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(
                 new ManaCostsImpl("{2}{U}{U}"), MyTurnCondition.instance
-        ).setText("as long as it's your turn, this spell costs {2}{U}{U} less to cast")));
+        ).setText("as long as it's your turn, this spell costs {2}{U}{U} less to cast"))
+                .addHint(MyTurnHint.instance));
 
         // End the turn.
         this.getSpellAbility().addEffect(new EndTurnEffect());
diff --git a/Mage.Sets/src/mage/cards/g/GustOfWind.java b/Mage.Sets/src/mage/cards/g/GustOfWind.java
index d0df498d57..19c5a506f2 100644
--- a/Mage.Sets/src/mage/cards/g/GustOfWind.java
+++ b/Mage.Sets/src/mage/cards/g/GustOfWind.java
@@ -1,12 +1,12 @@
 package mage.cards.g;
 
-import java.util.UUID;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.condition.Condition;
 import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.abilities.effects.common.ReturnToHandTargetEffect;
 import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect;
+import mage.abilities.hint.ConditionHint;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -19,6 +19,8 @@ import mage.filter.common.FilterNonlandPermanent;
 import mage.filter.predicate.mageobject.AbilityPredicate;
 import mage.target.TargetPermanent;
 
+import java.util.UUID;
+
 /**
  * @author TheElk801
  */
@@ -42,7 +44,7 @@ public final class GustOfWind extends CardImpl {
         // This spell costs {2} less to cast if you control a creature with flying.
         this.addAbility(new SimpleStaticAbility(
                 Zone.ALL, new SpellCostReductionSourceEffect(2, condition)
-        ).setRuleAtTheTop(true));
+        ).setRuleAtTheTop(true).addHint(new ConditionHint(condition, "You control a creature with flying")));
 
         // Return target nonland permanent you don't control to its owner's hand.
         this.getSpellAbility().addEffect(new ReturnToHandTargetEffect());
diff --git a/Mage.Sets/src/mage/cards/h/HourOfRevelation.java b/Mage.Sets/src/mage/cards/h/HourOfRevelation.java
index 2c359686be..8ba5adb763 100644
--- a/Mage.Sets/src/mage/cards/h/HourOfRevelation.java
+++ b/Mage.Sets/src/mage/cards/h/HourOfRevelation.java
@@ -1,11 +1,11 @@
-
 package mage.cards.h;
 
-import java.util.UUID;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
+import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
 import mage.abilities.effects.common.DestroyAllEffect;
 import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect;
+import mage.abilities.hint.ValueHint;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
@@ -13,8 +13,9 @@ import mage.constants.ComparisonType;
 import mage.constants.Zone;
 import mage.filter.common.FilterNonlandPermanent;
 
+import java.util.UUID;
+
 /**
- *
  * @author fireshoes
  */
 public final class HourOfRevelation extends CardImpl {
@@ -25,8 +26,10 @@ public final class HourOfRevelation extends CardImpl {
         // Hour of Revelation costs {3} less to cast if there are ten or more nonland permanents on the battlefield.
         SimpleStaticAbility ability = new SimpleStaticAbility(Zone.ALL,
                 new SpellCostReductionSourceEffect(3, new PermanentsOnTheBattlefieldCondition(
-                        new FilterNonlandPermanent("there are ten or more nonland permanents on the battlefield"), ComparisonType.MORE_THAN, 9, false)));
+                        new FilterNonlandPermanent("there are ten or more nonland permanents on the battlefield"),
+                        ComparisonType.MORE_THAN, 9, false)));
         ability.setRuleAtTheTop(true);
+        ability.addHint(new ValueHint("Nonland permanents on the battlefield", new PermanentsOnBattlefieldCount(new FilterNonlandPermanent())));
         this.addAbility(ability);
 
         // Destroy all nonland permanents.
diff --git a/Mage.Sets/src/mage/cards/i/IgneousElemental.java b/Mage.Sets/src/mage/cards/i/IgneousElemental.java
index 4aa3be3410..5d750cea5b 100644
--- a/Mage.Sets/src/mage/cards/i/IgneousElemental.java
+++ b/Mage.Sets/src/mage/cards/i/IgneousElemental.java
@@ -1,13 +1,14 @@
 package mage.cards.i;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.condition.Condition;
 import mage.abilities.condition.common.CardsInControllerGraveCondition;
 import mage.abilities.effects.common.DamageTargetEffect;
 import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect;
+import mage.abilities.hint.ConditionHint;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
@@ -16,6 +17,8 @@ import mage.constants.Zone;
 import mage.filter.StaticFilters;
 import mage.target.common.TargetCreaturePermanent;
 
+import java.util.UUID;
+
 /**
  * @author TheElk801
  */
@@ -29,10 +32,11 @@ public final class IgneousElemental extends CardImpl {
         this.toughness = new MageInt(3);
 
         // This spell costs {2} less to cast if there is a land card in your graveyard.
-        Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(
-                2, new CardsInControllerGraveCondition(1, StaticFilters.FILTER_CARD_LAND)
-        ).setText("This spell costs {2} less to cast if there is a land card in your graveyard."));
+        Condition condition = new CardsInControllerGraveCondition(1, StaticFilters.FILTER_CARD_LAND);
+        Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(2, condition)
+                .setText("This spell costs {2} less to cast if there is a land card in your graveyard."));
         ability.setRuleAtTheTop(true);
+        ability.addHint(new ConditionHint(condition, "There is a land card in your graveyard"));
         this.addAbility(ability);
 
         // When Igneous Elemental enters the battlefield, you may have it deal 2 damage to target creature.
diff --git a/Mage.Sets/src/mage/cards/i/IntoTheStory.java b/Mage.Sets/src/mage/cards/i/IntoTheStory.java
index e4ac44160c..02d5dfded4 100644
--- a/Mage.Sets/src/mage/cards/i/IntoTheStory.java
+++ b/Mage.Sets/src/mage/cards/i/IntoTheStory.java
@@ -1,11 +1,11 @@
 package mage.cards.i;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.condition.Condition;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect;
+import mage.abilities.hint.ConditionHint;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
@@ -14,6 +14,8 @@ import mage.game.Game;
 import mage.game.Graveyard;
 import mage.players.Player;
 
+import java.util.UUID;
+
 /**
  * @author TheElk801
  */
@@ -25,7 +27,7 @@ public final class IntoTheStory extends CardImpl {
         // This spell costs {3} less to cast if an opponent has seven or more cards in their graveyard.
         this.addAbility(new SimpleStaticAbility(
                 Zone.ALL, new SpellCostReductionSourceEffect(3, IntoTheStoryCondition.instance)
-        ).setRuleAtTheTop(true));
+        ).setRuleAtTheTop(true).addHint(new ConditionHint(IntoTheStoryCondition.instance, "Opponent has seven or more cards in their graveyard")));
 
         // Draw four cards.
         this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(4));
diff --git a/Mage.Sets/src/mage/cards/l/LookoutsDispersal.java b/Mage.Sets/src/mage/cards/l/LookoutsDispersal.java
index 6177fe818b..c3b224a46c 100644
--- a/Mage.Sets/src/mage/cards/l/LookoutsDispersal.java
+++ b/Mage.Sets/src/mage/cards/l/LookoutsDispersal.java
@@ -1,12 +1,13 @@
 package mage.cards.l;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.condition.Condition;
 import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
 import mage.abilities.costs.mana.GenericManaCost;
 import mage.abilities.effects.common.CounterUnlessPaysEffect;
 import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect;
+import mage.abilities.hint.ConditionHint;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
@@ -15,8 +16,9 @@ import mage.constants.Zone;
 import mage.filter.common.FilterControlledPermanent;
 import mage.target.TargetSpell;
 
+import java.util.UUID;
+
 /**
- *
  * @author TheElk801
  */
 public final class LookoutsDispersal extends CardImpl {
@@ -31,8 +33,10 @@ public final class LookoutsDispersal extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}");
 
         // Lookout's Dispersal costs {1} less to cast if you control a Pirate.
-        Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(1, new PermanentsOnTheBattlefieldCondition(filter)));
+        Condition condition = new PermanentsOnTheBattlefieldCondition(filter);
+        Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(1, condition));
         ability.setRuleAtTheTop(true);
+        ability.addHint(new ConditionHint(condition, "You control a Pirate"));
         this.addAbility(ability);
 
         // Counter target spell unless its controller pays {4}.
diff --git a/Mage.Sets/src/mage/cards/n/NotOfThisWorld.java b/Mage.Sets/src/mage/cards/n/NotOfThisWorld.java
index df8dde261f..edc5b0ed96 100644
--- a/Mage.Sets/src/mage/cards/n/NotOfThisWorld.java
+++ b/Mage.Sets/src/mage/cards/n/NotOfThisWorld.java
@@ -42,7 +42,9 @@ public final class NotOfThisWorld extends CardImpl {
         this.getSpellAbility().addTarget(new TargetStackObject(filter));
 
         // Not of This World costs {7} less to cast if it targets a spell or ability that targets a creature you control with power 7 or greater.
-        this.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(7, NotOfThisWorldCondition.instance).setCanWorksOnStackOnly(true)));
+        this.addAbility(new SimpleStaticAbility(Zone.ALL,
+                new SpellCostReductionSourceEffect(7, NotOfThisWorldCondition.instance).setCanWorksOnStackOnly(true))
+        );
     }
 
     private NotOfThisWorld(final NotOfThisWorld card) {
diff --git a/Mage.Sets/src/mage/cards/o/OakhameAdversary.java b/Mage.Sets/src/mage/cards/o/OakhameAdversary.java
index 7bd99cbe6e..6cd85144ab 100644
--- a/Mage.Sets/src/mage/cards/o/OakhameAdversary.java
+++ b/Mage.Sets/src/mage/cards/o/OakhameAdversary.java
@@ -1,6 +1,5 @@
 package mage.cards.o;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.ObjectColor;
 import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
@@ -9,6 +8,7 @@ import mage.abilities.condition.Condition;
 import mage.abilities.condition.common.OpponentControlsPermanentCondition;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect;
+import mage.abilities.hint.ConditionHint;
 import mage.abilities.keyword.DeathtouchAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -19,6 +19,8 @@ import mage.constants.Zone;
 import mage.filter.FilterPermanent;
 import mage.filter.predicate.mageobject.ColorPredicate;
 
+import java.util.UUID;
+
 /**
  * @author TheElk801
  */
@@ -44,7 +46,7 @@ public final class OakhameAdversary extends CardImpl {
         // This spell costs {2} less to cast if your opponent controls a green permanent.
         this.addAbility(new SimpleStaticAbility(
                 Zone.ALL, new SpellCostReductionSourceEffect(2, condition)
-        ).setRuleAtTheTop(true));
+        ).setRuleAtTheTop(true).addHint(new ConditionHint(condition, "Your opponent controls a green permanent")));
 
         // Deathtouch
         this.addAbility(DeathtouchAbility.getInstance());
diff --git a/Mage.Sets/src/mage/cards/t/TentativeConnection.java b/Mage.Sets/src/mage/cards/t/TentativeConnection.java
index 6dba4d8648..0683d1d77e 100644
--- a/Mage.Sets/src/mage/cards/t/TentativeConnection.java
+++ b/Mage.Sets/src/mage/cards/t/TentativeConnection.java
@@ -2,11 +2,13 @@ package mage.cards.t;
 
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.condition.Condition;
 import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
 import mage.abilities.effects.common.UntapTargetEffect;
 import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
 import mage.abilities.effects.common.continuous.GainControlTargetEffect;
 import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect;
+import mage.abilities.hint.ConditionHint;
 import mage.abilities.keyword.HasteAbility;
 import mage.abilities.keyword.MenaceAbility;
 import mage.cards.CardImpl;
@@ -37,13 +39,10 @@ public final class TentativeConnection extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{R}");
 
         // This spell costs {3} less to cast if you control a creature with menace.
-        Ability ability = new SimpleStaticAbility(
-                Zone.ALL,
-                new SpellCostReductionSourceEffect(
-                        3, new PermanentsOnTheBattlefieldCondition(filter)
-                )
-        );
+        Condition condition = new PermanentsOnTheBattlefieldCondition(filter);
+        Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(3, condition));
         ability.setRuleAtTheTop(true);
+        ability.addHint(new ConditionHint(condition, "You control a creature with menace"));
         this.addAbility(ability);
 
         // Gain control of target creature until end of turn. Untap that creature. It gains haste until end of turn.
diff --git a/Mage.Sets/src/mage/cards/w/WingedWords.java b/Mage.Sets/src/mage/cards/w/WingedWords.java
index 2f8f344dc4..316f39b006 100644
--- a/Mage.Sets/src/mage/cards/w/WingedWords.java
+++ b/Mage.Sets/src/mage/cards/w/WingedWords.java
@@ -1,10 +1,11 @@
 package mage.cards.w;
 
-import java.util.UUID;
 import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.condition.Condition;
 import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect;
+import mage.abilities.hint.ConditionHint;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -13,6 +14,8 @@ import mage.constants.Zone;
 import mage.filter.common.FilterControlledPermanent;
 import mage.filter.predicate.mageobject.AbilityPredicate;
 
+import java.util.UUID;
+
 /**
  * @author TheElk801
  */
@@ -29,10 +32,11 @@ public final class WingedWords extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}");
 
         // This spell costs {1} less to cast if you control a creature with flying.
-        this.addAbility(new SimpleStaticAbility(
-                Zone.ALL, new SpellCostReductionSourceEffect(
-                        1, new PermanentsOnTheBattlefieldCondition(filter)
-                )).setRuleAtTheTop(true));
+        Condition condition = new PermanentsOnTheBattlefieldCondition(filter);
+        this.addAbility(new SimpleStaticAbility(Zone.ALL,
+                new SpellCostReductionSourceEffect(1, condition))
+                .setRuleAtTheTop(true)
+                .addHint(new ConditionHint(condition, "You control a creature with flying")));
 
         // Draw two cards.
         this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2));
diff --git a/Mage.Sets/src/mage/cards/w/WizardsLightning.java b/Mage.Sets/src/mage/cards/w/WizardsLightning.java
index b1d69d0283..b8f76e0ffc 100644
--- a/Mage.Sets/src/mage/cards/w/WizardsLightning.java
+++ b/Mage.Sets/src/mage/cards/w/WizardsLightning.java
@@ -1,11 +1,12 @@
 package mage.cards.w;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.condition.Condition;
 import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
 import mage.abilities.effects.common.DamageTargetEffect;
 import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect;
+import mage.abilities.hint.ConditionHint;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
@@ -14,8 +15,9 @@ import mage.constants.Zone;
 import mage.filter.common.FilterControlledPermanent;
 import mage.target.common.TargetAnyTarget;
 
+import java.util.UUID;
+
 /**
- *
  * @author Will
  */
 public final class WizardsLightning extends CardImpl {
@@ -30,8 +32,10 @@ public final class WizardsLightning extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R}");
 
         // Wizard's Lightning costs {2} less to cast if you control a Wizard.
-        Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(2, new PermanentsOnTheBattlefieldCondition(filter)));
+        Condition condition = new PermanentsOnTheBattlefieldCondition(filter);
+        Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(2, condition));
         ability.setRuleAtTheTop(true);
+        ability.addHint(new ConditionHint(condition, "You control a Wizard"));
         this.addAbility(ability);
 
         // Wizard's Lightning deals 3 damage to any target.
diff --git a/Mage.Sets/src/mage/cards/w/WizardsRetort.java b/Mage.Sets/src/mage/cards/w/WizardsRetort.java
index d58c678ad3..192b9e449e 100644
--- a/Mage.Sets/src/mage/cards/w/WizardsRetort.java
+++ b/Mage.Sets/src/mage/cards/w/WizardsRetort.java
@@ -1,11 +1,12 @@
 package mage.cards.w;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.condition.Condition;
 import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
 import mage.abilities.effects.common.CounterTargetEffect;
 import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect;
+import mage.abilities.hint.ConditionHint;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
@@ -14,8 +15,9 @@ import mage.constants.Zone;
 import mage.filter.common.FilterControlledPermanent;
 import mage.target.TargetSpell;
 
+import java.util.UUID;
+
 /**
- *
  * @author Will
  */
 public final class WizardsRetort extends CardImpl {
@@ -30,8 +32,10 @@ public final class WizardsRetort extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}{U}");
 
         // Wizard's Retort costs {1} less to cast if you control a Wizard.
-        Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(1, new PermanentsOnTheBattlefieldCondition(filter)));
+        Condition condition = new PermanentsOnTheBattlefieldCondition(filter);
+        Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(1, condition));
         ability.setRuleAtTheTop(true);
+        ability.addHint(new ConditionHint(condition, "You control a Wizard"));
         this.addAbility(ability);
 
         // Counter target spell.

From 1a0dca906798acbcb87ba0a1f44a46a3e633f29b Mon Sep 17 00:00:00 2001
From: htrajan <htrajan2002@yahoo.com>
Date: Sun, 28 Jun 2020 20:24:59 -0700
Subject: [PATCH 543/586] fix M21 bugs

---
 Mage.Sets/src/mage/cards/g/GarrukSavageHerald.java | 12 +++++++++---
 Mage.Sets/src/mage/cards/l/LilianaDeathMage.java   |  8 ++++++--
 Mage.Sets/src/mage/cards/n/Necromentia.java        |  5 +++++
 Mage.Sets/src/mage/cards/s/SanctumOfAll.java       |  6 ++++--
 4 files changed, 24 insertions(+), 7 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/g/GarrukSavageHerald.java b/Mage.Sets/src/mage/cards/g/GarrukSavageHerald.java
index 614653fe34..2115e2ddab 100644
--- a/Mage.Sets/src/mage/cards/g/GarrukSavageHerald.java
+++ b/Mage.Sets/src/mage/cards/g/GarrukSavageHerald.java
@@ -5,6 +5,7 @@ import mage.abilities.LoyaltyAbility;
 import mage.abilities.common.DamageAsThoughNotBlockedAbility;
 import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
 import mage.abilities.effects.ContinuousEffect;
+import mage.abilities.effects.Effect;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.DamageWithPowerFromOneToAnotherTargetEffect;
 import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
@@ -12,7 +13,12 @@ import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.cards.CardsImpl;
-import mage.constants.*;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.Outcome;
+import mage.constants.SubType;
+import mage.constants.SuperType;
+import mage.constants.Zone;
 import mage.filter.common.FilterCreaturePermanent;
 import mage.filter.predicate.mageobject.AnotherTargetPredicate;
 import mage.game.Game;
@@ -39,7 +45,7 @@ public final class GarrukSavageHerald extends CardImpl {
         this.addAbility(new LoyaltyAbility(new GarrukSavageHeraldEffect(), 1));
 
         // −2: Target creature you control deals damage equal to its power to another target creature.
-        DamageWithPowerFromOneToAnotherTargetEffect effect = new DamageWithPowerFromOneToAnotherTargetEffect();
+        Effect effect = new DamageWithPowerFromOneToAnotherTargetEffect();
         effect.setText("Target creature you control deals damage equal to its power to another target creature");
 
         Ability minusAbility = new LoyaltyAbility(effect, -2);
@@ -47,7 +53,7 @@ public final class GarrukSavageHerald extends CardImpl {
         controlledCreature.setTargetTag(1);
         minusAbility.addTarget(controlledCreature);
 
-        FilterCreaturePermanent filter = new FilterCreaturePermanent("Another creature: damage dealt to");
+        FilterCreaturePermanent filter = new FilterCreaturePermanent("another creature to deal damage to");
         filter.add(new AnotherTargetPredicate(2));
         TargetCreaturePermanent anotherTargetCreature = new TargetCreaturePermanent(filter);
         minusAbility.addTarget(anotherTargetCreature);
diff --git a/Mage.Sets/src/mage/cards/l/LilianaDeathMage.java b/Mage.Sets/src/mage/cards/l/LilianaDeathMage.java
index 45c189ded9..0e1d8341ce 100644
--- a/Mage.Sets/src/mage/cards/l/LilianaDeathMage.java
+++ b/Mage.Sets/src/mage/cards/l/LilianaDeathMage.java
@@ -9,7 +9,11 @@ import mage.abilities.effects.common.LoseLifeTargetControllerEffect;
 import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.*;
+import mage.constants.CardType;
+import mage.constants.Outcome;
+import mage.constants.SubType;
+import mage.constants.SuperType;
+import mage.constants.Zone;
 import mage.filter.StaticFilters;
 import mage.game.Game;
 import mage.players.Player;
@@ -78,7 +82,7 @@ class LilianaDeathMagePlusEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player player = game.getPlayer(source.getControllerId());
-        if (player == null || !player.chooseUse(Outcome.Benefit, "Return a creature card from your graveyard to your hand?", source, game)) {
+        if (player == null) {
             return false;
         }
         Card card = game.getCard(source.getTargets().get(0).getFirstTarget());
diff --git a/Mage.Sets/src/mage/cards/n/Necromentia.java b/Mage.Sets/src/mage/cards/n/Necromentia.java
index 985e0fc22e..888d90311d 100644
--- a/Mage.Sets/src/mage/cards/n/Necromentia.java
+++ b/Mage.Sets/src/mage/cards/n/Necromentia.java
@@ -20,6 +20,7 @@ import mage.target.TargetCard;
 import mage.target.common.TargetCardInLibrary;
 import mage.target.common.TargetOpponent;
 
+import java.util.HashSet;
 import java.util.UUID;
 
 /**
@@ -94,6 +95,8 @@ class NecromentiaEffect extends OneShotEffect {
                     numberOfCardsExiledFromHand = target.getTargets().size();
                     controller.moveCards(new CardsImpl(target.getTargets()), Zone.EXILED, source, game);
                 }
+            } else {
+                targetPlayer.revealCards(targetPlayer.getName() + "'s Hand", targetPlayer.getHand(), game);
             }
 
             // cards in Library
@@ -106,6 +109,8 @@ class NecromentiaEffect extends OneShotEffect {
                 if (controller.choose(Outcome.Exile, cardsInLibrary, targetLib, game)) {
                     controller.moveCards(new CardsImpl(targetLib.getTargets()), Zone.EXILED, source, game);
                 }
+            } else {
+                targetPlayer.revealCards(targetPlayer.getName() + "'s Library", new CardsImpl(new HashSet<>(targetPlayer.getLibrary().getCards(game))), game);
             }
 
             targetPlayer.shuffleLibrary(source, game);
diff --git a/Mage.Sets/src/mage/cards/s/SanctumOfAll.java b/Mage.Sets/src/mage/cards/s/SanctumOfAll.java
index ccb8c70cf7..97386229b2 100644
--- a/Mage.Sets/src/mage/cards/s/SanctumOfAll.java
+++ b/Mage.Sets/src/mage/cards/s/SanctumOfAll.java
@@ -97,9 +97,11 @@ class SanctumOfAllTriggerEffect extends ReplacementEffectImpl {
             // Only trigger while you control six or more Shrines
             int numShrines = SanctumOfAll.count.calculate(game, source, this);
             if (numShrines >= 6) {
-                // Only for triggers of Shrines
+                // Only for triggers of other Shrines
                 Permanent permanent = game.getPermanent(event.getSourceId());
-                return permanent != null && permanent.hasSubtype(SubType.SHRINE, game);
+                return permanent != null
+                        && !permanent.getId().equals(source.getSourceId())
+                        && permanent.hasSubtype(SubType.SHRINE, game);
             }
         }
         return false;

From faa375907ffa53592601a8acd5641336f4ddc1b4 Mon Sep 17 00:00:00 2001
From: htrajan <htrajan2002@yahoo.com>
Date: Sun, 28 Jun 2020 21:42:59 -0700
Subject: [PATCH 544/586] withChooseHint

---
 Mage.Sets/src/mage/cards/g/GarrukSavageHerald.java | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/g/GarrukSavageHerald.java b/Mage.Sets/src/mage/cards/g/GarrukSavageHerald.java
index 2115e2ddab..5d2aaf48a8 100644
--- a/Mage.Sets/src/mage/cards/g/GarrukSavageHerald.java
+++ b/Mage.Sets/src/mage/cards/g/GarrukSavageHerald.java
@@ -53,10 +53,10 @@ public final class GarrukSavageHerald extends CardImpl {
         controlledCreature.setTargetTag(1);
         minusAbility.addTarget(controlledCreature);
 
-        FilterCreaturePermanent filter = new FilterCreaturePermanent("another creature to deal damage to");
+        FilterCreaturePermanent filter = new FilterCreaturePermanent();
         filter.add(new AnotherTargetPredicate(2));
         TargetCreaturePermanent anotherTargetCreature = new TargetCreaturePermanent(filter);
-        minusAbility.addTarget(anotherTargetCreature);
+        minusAbility.addTarget(anotherTargetCreature.withChooseHint("another creature to deal damage to"));
 
         this.addAbility(minusAbility);
 

From e4ebf50d42c5752cd6aa539b72da9777e6f6281d Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Mon, 29 Jun 2020 09:28:46 +0400
Subject: [PATCH 545/586] * Artifact you control effects - added card hints;

---
 .../src/mage/cards/a/AkiriLineSlinger.java    | 22 ++++-------
 .../src/mage/cards/c/CranialPlating.java      | 23 +++++-------
 .../src/mage/cards/d/DeadeyePlunderers.java   | 24 ++++--------
 Mage.Sets/src/mage/cards/f/FiligreeAngel.java | 30 +++++----------
 .../src/mage/cards/h/HungerOfTheNim.java      | 21 ++++-------
 Mage.Sets/src/mage/cards/i/Irradiate.java     |  9 +++--
 .../src/mage/cards/k/KarnScionOfUrza.java     |  3 +-
 Mage.Sets/src/mage/cards/m/MephiticOoze.java  | 24 ++++--------
 Mage.Sets/src/mage/cards/n/NimDevourer.java   | 37 +++++++------------
 Mage.Sets/src/mage/cards/n/NimGrotesque.java  | 18 +++++----
 Mage.Sets/src/mage/cards/o/OverrideCard.java  |  4 +-
 .../src/mage/cards/p/PowerstoneShard.java     | 11 ++++--
 .../src/mage/cards/t/TidyConclusion.java      | 14 +++----
 .../mage/cards/u/UrzaLordHighArtificer.java   | 17 ++++-----
 .../src/mage/cards/v/VaultOfCatlacan.java     | 16 ++++----
 ...ount.java => ArtifactYouControlCount.java} |  8 ++--
 ...lHint.java => ArtifactYouControlHint.java} |  6 +--
 .../keyword/AffinityForArtifactsAbility.java  |  4 +-
 .../permanent/token/KarnConstructToken.java   | 16 ++------
 19 files changed, 126 insertions(+), 181 deletions(-)
 rename Mage/src/main/java/mage/abilities/dynamicvalue/common/{ArtifactsYouControlCount.java => ArtifactYouControlCount.java} (76%)
 rename Mage/src/main/java/mage/abilities/hint/common/{ArtifactsYouControlHint.java => ArtifactYouControlHint.java} (73%)

diff --git a/Mage.Sets/src/mage/cards/a/AkiriLineSlinger.java b/Mage.Sets/src/mage/cards/a/AkiriLineSlinger.java
index 5cd997b7e0..507e6796b0 100644
--- a/Mage.Sets/src/mage/cards/a/AkiriLineSlinger.java
+++ b/Mage.Sets/src/mage/cards/a/AkiriLineSlinger.java
@@ -1,36 +1,29 @@
-
 package mage.cards.a;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.common.SimpleStaticAbility;
-import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
+import mage.abilities.dynamicvalue.common.ArtifactYouControlCount;
 import mage.abilities.dynamicvalue.common.StaticValue;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.continuous.BoostSourceEffect;
+import mage.abilities.hint.common.ArtifactYouControlHint;
 import mage.abilities.keyword.FirstStrikeAbility;
 import mage.abilities.keyword.PartnerAbility;
 import mage.abilities.keyword.VigilanceAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.*;
-import mage.filter.common.FilterControlledPermanent;
+
+import java.util.UUID;
 
 /**
- *
  * @author spjspj
  */
 public final class AkiriLineSlinger extends CardImpl {
 
-    private static final FilterControlledPermanent filter = new FilterControlledPermanent("artifact you control");
-
-    static {
-        filter.add(CardType.ARTIFACT.getPredicate());
-    }
-
     public AkiriLineSlinger(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}{W}");
-        
+
         addSuperType(SuperType.LEGENDARY);
         this.subtype.add(SubType.KOR);
         this.subtype.add(SubType.SOLDIER);
@@ -45,9 +38,10 @@ public final class AkiriLineSlinger extends CardImpl {
         this.addAbility(VigilanceAbility.getInstance());
 
         // Akiri, Line-Slinger gets +1/+0 for each artifact you control.
-        Effect effect = new BoostSourceEffect(new PermanentsOnBattlefieldCount(filter), StaticValue.get(0), Duration.WhileOnBattlefield);
+        Effect effect = new BoostSourceEffect(ArtifactYouControlCount.instance, StaticValue.get(0), Duration.WhileOnBattlefield);
         effect.setText("{this} gets +1/+0 for each artifact you control");
-        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect));
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)
+                .addHint(ArtifactYouControlHint.instance));
 
         // Partner
         this.addAbility(PartnerAbility.getInstance());
diff --git a/Mage.Sets/src/mage/cards/c/CranialPlating.java b/Mage.Sets/src/mage/cards/c/CranialPlating.java
index 4ff75ee085..cdafd2c9ac 100644
--- a/Mage.Sets/src/mage/cards/c/CranialPlating.java
+++ b/Mage.Sets/src/mage/cards/c/CranialPlating.java
@@ -1,47 +1,44 @@
-
 package mage.cards.c;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.costs.mana.GenericManaCost;
 import mage.abilities.costs.mana.ManaCostsImpl;
-import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
+import mage.abilities.dynamicvalue.common.ArtifactYouControlCount;
 import mage.abilities.dynamicvalue.common.StaticValue;
 import mage.abilities.effects.common.AttachEffect;
 import mage.abilities.effects.common.continuous.BoostEquippedEffect;
+import mage.abilities.hint.common.ArtifactYouControlHint;
 import mage.abilities.keyword.EquipAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.SubType;
 import mage.constants.Outcome;
+import mage.constants.SubType;
 import mage.constants.Zone;
-import mage.filter.common.FilterControlledPermanent;
 import mage.target.common.TargetControlledCreaturePermanent;
 
+import java.util.UUID;
+
 /**
- *
  * @author Loki
  */
 public final class CranialPlating extends CardImpl {
-    private static final FilterControlledPermanent filterCounted = new FilterControlledPermanent("artifacts you control");
-
-    static {
-        filterCounted.add(CardType.ARTIFACT.getPredicate());
-    }
 
     public CranialPlating(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{2}");
+        super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}");
         this.subtype.add(SubType.EQUIPMENT);
 
         // Equipped creature gets +1/+0 for each artifact you control.
-        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(new PermanentsOnBattlefieldCount(filterCounted), StaticValue.get(0))));
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(ArtifactYouControlCount.instance, StaticValue.get(0)))
+                .addHint(ArtifactYouControlHint.instance));
+
         // {B}{B}: Attach Cranial Plating to target creature you control.
         Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AttachEffect(Outcome.BoostCreature, "Attach {this} to target creature you control"), new ManaCostsImpl("{B}{B}"));
         ability.addTarget(new TargetControlledCreaturePermanent());
         this.addAbility(ability);
+
         // Equip {1}
         this.addAbility(new EquipAbility(Outcome.BoostCreature, new GenericManaCost(1)));
     }
diff --git a/Mage.Sets/src/mage/cards/d/DeadeyePlunderers.java b/Mage.Sets/src/mage/cards/d/DeadeyePlunderers.java
index 121c950190..180c7cf2b4 100644
--- a/Mage.Sets/src/mage/cards/d/DeadeyePlunderers.java
+++ b/Mage.Sets/src/mage/cards/d/DeadeyePlunderers.java
@@ -1,37 +1,28 @@
-
 package mage.cards.d;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
-import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
+import mage.abilities.dynamicvalue.common.ArtifactYouControlCount;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.abilities.effects.common.continuous.BoostSourceEffect;
+import mage.abilities.hint.common.ArtifactYouControlHint;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.SubType;
 import mage.constants.Duration;
-import mage.constants.TargetController;
+import mage.constants.SubType;
 import mage.constants.Zone;
-import mage.filter.FilterPermanent;
 import mage.game.permanent.token.TreasureToken;
 
+import java.util.UUID;
+
 /**
- *
  * @author TheElk801
  */
 public final class DeadeyePlunderers extends CardImpl {
 
-    private static final FilterPermanent filter = new FilterPermanent("artifact you control");
-
-    static {
-        filter.add(CardType.ARTIFACT.getPredicate());
-        filter.add(TargetController.YOU.getControllerPredicate());
-    }
-
     public DeadeyePlunderers(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{B}");
 
@@ -41,8 +32,9 @@ public final class DeadeyePlunderers extends CardImpl {
         this.toughness = new MageInt(3);
 
         // Deadeye Plunderers gets +1/+1 for each artifact you control.
-        PermanentsOnBattlefieldCount count = new PermanentsOnBattlefieldCount(filter);
-        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostSourceEffect(count, count, Duration.WhileOnBattlefield)));
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
+                new BoostSourceEffect(ArtifactYouControlCount.instance, ArtifactYouControlCount.instance, Duration.WhileOnBattlefield)
+        ).addHint(ArtifactYouControlHint.instance));
 
         // {2}{U}{B}: Create a colorless artifact token named Treasure with "{T}, Sacrifice this artifact: Add one mana of any color."
         this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new TreasureToken()), new ManaCostsImpl("{2}{U}{B}")));
diff --git a/Mage.Sets/src/mage/cards/f/FiligreeAngel.java b/Mage.Sets/src/mage/cards/f/FiligreeAngel.java
index 638f78ff10..2f06035903 100644
--- a/Mage.Sets/src/mage/cards/f/FiligreeAngel.java
+++ b/Mage.Sets/src/mage/cards/f/FiligreeAngel.java
@@ -1,34 +1,30 @@
-
-
 package mage.cards.f;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.dynamicvalue.common.ArtifactYouControlCount;
 import mage.abilities.effects.OneShotEffect;
+import mage.abilities.hint.common.ArtifactYouControlHint;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.SubType;
 import mage.constants.Outcome;
-import mage.filter.FilterPermanent;
-import mage.filter.common.FilterControlledPermanent;
+import mage.constants.SubType;
 import mage.game.Game;
 import mage.players.Player;
 
+import java.util.UUID;
+
 /**
- *
  * @author Loki
  */
 public final class FiligreeAngel extends CardImpl {
 
-    public FiligreeAngel (UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{5}{W}{W}{U}");
+    public FiligreeAngel(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{5}{W}{W}{U}");
         this.subtype.add(SubType.ANGEL);
-
-
         this.power = new MageInt(4);
         this.toughness = new MageInt(4);
 
@@ -36,10 +32,10 @@ public final class FiligreeAngel extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
 
         // When Filigree Angel enters the battlefield, you gain 3 life for each artifact you control.
-        this.addAbility(new EntersBattlefieldTriggeredAbility(new FiligreeAngelEffect()));
+        this.addAbility(new EntersBattlefieldTriggeredAbility(new FiligreeAngelEffect()).addHint(ArtifactYouControlHint.instance));
     }
 
-    public FiligreeAngel (final FiligreeAngel card) {
+    public FiligreeAngel(final FiligreeAngel card) {
         super(card);
     }
 
@@ -50,12 +46,6 @@ public final class FiligreeAngel extends CardImpl {
 }
 
 class FiligreeAngelEffect extends OneShotEffect {
-    
-    private static final FilterPermanent filter = new FilterControlledPermanent();
-
-    static {
-        filter.add(CardType.ARTIFACT.getPredicate());
-    }
 
     public FiligreeAngelEffect() {
         super(Outcome.GainLife);
@@ -70,7 +60,7 @@ class FiligreeAngelEffect extends OneShotEffect {
     public boolean apply(Game game, Ability source) {
         Player player = game.getPlayer(source.getControllerId());
         if (player != null) {
-            int life = game.getBattlefield().count(filter, source.getSourceId(), source.getControllerId(), game) * 3;
+            int life = ArtifactYouControlCount.instance.calculate(game, source, this) * 3;
             player.gainLife(life, game, source);
         }
         return true;
diff --git a/Mage.Sets/src/mage/cards/h/HungerOfTheNim.java b/Mage.Sets/src/mage/cards/h/HungerOfTheNim.java
index 657891ee47..28dfd0b529 100644
--- a/Mage.Sets/src/mage/cards/h/HungerOfTheNim.java
+++ b/Mage.Sets/src/mage/cards/h/HungerOfTheNim.java
@@ -1,38 +1,31 @@
-
 package mage.cards.h;
 
-import java.util.UUID;
-import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
+import mage.abilities.dynamicvalue.common.ArtifactYouControlCount;
 import mage.abilities.dynamicvalue.common.StaticValue;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.continuous.BoostTargetEffect;
+import mage.abilities.hint.common.ArtifactYouControlHint;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.Duration;
-import mage.filter.common.FilterControlledPermanent;
 import mage.target.common.TargetCreaturePermanent;
 
+import java.util.UUID;
+
 /**
- *
  * @author fireshoes
  */
 public final class HungerOfTheNim extends CardImpl {
 
-    private static final FilterControlledPermanent filter = new FilterControlledPermanent("artifact you control");
-
-    static {
-        filter.add(CardType.ARTIFACT.getPredicate());
-    }
-
     public HungerOfTheNim(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{B}");
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}");
 
         // Target creature gets +1/+0 until end of turn for each artifact you control.
-        Effect effect = new BoostTargetEffect(new PermanentsOnBattlefieldCount(filter), StaticValue.get(0), Duration.EndOfTurn, true);
-        effect.setText("Target creature gets +1/+0 until end of turn for each artifact you control");
+        Effect effect = new BoostTargetEffect(ArtifactYouControlCount.instance, StaticValue.get(0), Duration.EndOfTurn, true);
         getSpellAbility().addEffect(effect);
         getSpellAbility().addTarget(new TargetCreaturePermanent());
+        getSpellAbility().addHint(ArtifactYouControlHint.instance);
     }
 
     public HungerOfTheNim(final HungerOfTheNim card) {
diff --git a/Mage.Sets/src/mage/cards/i/Irradiate.java b/Mage.Sets/src/mage/cards/i/Irradiate.java
index 2e19012e7c..7208209f31 100644
--- a/Mage.Sets/src/mage/cards/i/Irradiate.java
+++ b/Mage.Sets/src/mage/cards/i/Irradiate.java
@@ -1,9 +1,8 @@
-
 package mage.cards.i;
 
-import java.util.UUID;
 import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
 import mage.abilities.effects.common.continuous.BoostTargetEffect;
+import mage.abilities.hint.common.ArtifactYouControlHint;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
@@ -11,19 +10,21 @@ import mage.constants.Duration;
 import mage.filter.common.FilterControlledArtifactPermanent;
 import mage.target.common.TargetCreaturePermanent;
 
+import java.util.UUID;
+
 /**
- *
  * @author LoneFox
  */
 public final class Irradiate extends CardImpl {
 
     public Irradiate(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{B}");
+        super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{B}");
 
         // Target creature gets -1/-1 until end of turn for each artifact you control.
         PermanentsOnBattlefieldCount count = new PermanentsOnBattlefieldCount(new FilterControlledArtifactPermanent(), -1);
         this.getSpellAbility().addEffect(new BoostTargetEffect(count, count, Duration.EndOfTurn, true));
         this.getSpellAbility().addTarget(new TargetCreaturePermanent());
+        this.getSpellAbility().addHint(ArtifactYouControlHint.instance);
     }
 
     public Irradiate(final Irradiate card) {
diff --git a/Mage.Sets/src/mage/cards/k/KarnScionOfUrza.java b/Mage.Sets/src/mage/cards/k/KarnScionOfUrza.java
index b42cf9d604..db1fcbb798 100644
--- a/Mage.Sets/src/mage/cards/k/KarnScionOfUrza.java
+++ b/Mage.Sets/src/mage/cards/k/KarnScionOfUrza.java
@@ -6,6 +6,7 @@ import mage.abilities.LoyaltyAbility;
 import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.CreateTokenEffect;
+import mage.abilities.hint.common.ArtifactYouControlHint;
 import mage.cards.*;
 import mage.constants.*;
 import mage.counters.CounterType;
@@ -19,7 +20,6 @@ import mage.target.Target;
 import mage.target.TargetCard;
 import mage.target.common.TargetOpponent;
 
-import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import java.util.UUID;
@@ -46,6 +46,7 @@ public final class KarnScionOfUrza extends CardImpl {
 
         // -2: Create a 0/0 colorless Construct artifact creature token with "This creature gets +1/+1 for each artifact you control."
         LoyaltyAbility ability3 = new LoyaltyAbility(new KarnConstructEffect(), -2);
+        ability3.addHint(ArtifactYouControlHint.instance);
         this.addAbility(ability3);
     }
 
diff --git a/Mage.Sets/src/mage/cards/m/MephiticOoze.java b/Mage.Sets/src/mage/cards/m/MephiticOoze.java
index a89562f39e..f90dbcc6d7 100644
--- a/Mage.Sets/src/mage/cards/m/MephiticOoze.java
+++ b/Mage.Sets/src/mage/cards/m/MephiticOoze.java
@@ -1,45 +1,37 @@
-
 package mage.cards.m;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.common.DealsCombatDamageToACreatureTriggeredAbility;
 import mage.abilities.common.SimpleStaticAbility;
-import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
+import mage.abilities.dynamicvalue.common.ArtifactYouControlCount;
 import mage.abilities.dynamicvalue.common.StaticValue;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.DestroyTargetEffect;
 import mage.abilities.effects.common.continuous.BoostSourceEffect;
+import mage.abilities.hint.common.ArtifactYouControlHint;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.SubType;
 import mage.constants.Duration;
+import mage.constants.SubType;
 import mage.constants.Zone;
-import mage.filter.common.FilterControlledPermanent;
+
+import java.util.UUID;
 
 /**
- *
  * @author fireshoes
  */
 public final class MephiticOoze extends CardImpl {
 
-    private static final FilterControlledPermanent filter = new FilterControlledPermanent("artifact you control");
-
-    static {
-        filter.add(CardType.ARTIFACT.getPredicate());
-    }
-
     public MephiticOoze(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{B}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}");
         this.subtype.add(SubType.OOZE);
         this.power = new MageInt(0);
         this.toughness = new MageInt(5);
 
         // Mephitic Ooze gets +1/+0 for each artifact you control.
-        Effect effect = new BoostSourceEffect(new PermanentsOnBattlefieldCount(filter), StaticValue.get(0), Duration.WhileOnBattlefield);
-        effect.setText("{this} gets +1/+0 for each artifact you control");
-        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect));
+        Effect effect = new BoostSourceEffect(ArtifactYouControlCount.instance, StaticValue.get(0), Duration.WhileOnBattlefield);
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect).addHint(ArtifactYouControlHint.instance));
 
         // Whenever Mephitic Ooze deals combat damage to a creature, destroy that creature. The creature can't be regenerated.
         this.addAbility(new DealsCombatDamageToACreatureTriggeredAbility(new DestroyTargetEffect(true), false, true));
diff --git a/Mage.Sets/src/mage/cards/n/NimDevourer.java b/Mage.Sets/src/mage/cards/n/NimDevourer.java
index d386661b5c..80c31583b9 100644
--- a/Mage.Sets/src/mage/cards/n/NimDevourer.java
+++ b/Mage.Sets/src/mage/cards/n/NimDevourer.java
@@ -1,59 +1,50 @@
-
 package mage.cards.n;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.condition.common.IsStepCondition;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.decorator.ConditionalActivatedAbility;
-import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
+import mage.abilities.dynamicvalue.common.ArtifactYouControlCount;
 import mage.abilities.dynamicvalue.common.StaticValue;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect;
 import mage.abilities.effects.common.continuous.BoostSourceEffect;
+import mage.abilities.hint.common.ArtifactYouControlHint;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.CardType;
-import mage.constants.SubType;
-import mage.constants.Duration;
-import mage.constants.Outcome;
-import mage.constants.PhaseStep;
-import mage.constants.Zone;
+import mage.constants.*;
 import mage.filter.common.FilterControlledCreaturePermanent;
-import mage.filter.common.FilterControlledPermanent;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
 import mage.players.Player;
 import mage.target.Target;
 import mage.target.common.TargetControlledPermanent;
 
+import java.util.UUID;
+
 /**
- *
  * @author fireshoes
  */
 public final class NimDevourer extends CardImpl {
-    
-    private static final FilterControlledPermanent filter = new FilterControlledPermanent("artifact you control");
-
-    static {
-        filter.add(CardType.ARTIFACT.getPredicate());
-    }
 
     public NimDevourer(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}{B}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}");
         this.subtype.add(SubType.ZOMBIE);
         this.power = new MageInt(4);
         this.toughness = new MageInt(1);
 
         // Nim Devourer gets +1/+0 for each artifact you control.
-        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostSourceEffect(new PermanentsOnBattlefieldCount(filter), StaticValue.get(0), Duration.WhileOnBattlefield)));
-        
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
+                new BoostSourceEffect(ArtifactYouControlCount.instance, StaticValue.get(0), Duration.WhileOnBattlefield))
+                .addHint(ArtifactYouControlHint.instance)
+        );
+
         // {B}{B}: Return Nim Devourer from your graveyard to the battlefield, then sacrifice a creature. Activate this ability only during your upkeep.
-        Ability ability = new ConditionalActivatedAbility(Zone.GRAVEYARD, 
-                new ReturnSourceFromGraveyardToBattlefieldEffect(), 
-                new ManaCostsImpl("{B}{B}"), 
+        Ability ability = new ConditionalActivatedAbility(Zone.GRAVEYARD,
+                new ReturnSourceFromGraveyardToBattlefieldEffect(),
+                new ManaCostsImpl("{B}{B}"),
                 new IsStepCondition(PhaseStep.UPKEEP), null);
         ability.addEffect(new NimDevourerEffect());
         this.addAbility(ability);
diff --git a/Mage.Sets/src/mage/cards/n/NimGrotesque.java b/Mage.Sets/src/mage/cards/n/NimGrotesque.java
index 03442660b8..63cb3360fb 100644
--- a/Mage.Sets/src/mage/cards/n/NimGrotesque.java
+++ b/Mage.Sets/src/mage/cards/n/NimGrotesque.java
@@ -1,35 +1,37 @@
-
 package mage.cards.n;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.common.SimpleStaticAbility;
-import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
+import mage.abilities.dynamicvalue.common.ArtifactYouControlCount;
 import mage.abilities.dynamicvalue.common.StaticValue;
 import mage.abilities.effects.common.continuous.BoostSourceEffect;
+import mage.abilities.hint.common.ArtifactYouControlHint;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.SubType;
 import mage.constants.Duration;
+import mage.constants.SubType;
 import mage.constants.Zone;
-import mage.filter.common.FilterControlledArtifactPermanent;
+
+import java.util.UUID;
 
 /**
- *
  * @author Plopman
  */
 public final class NimGrotesque extends CardImpl {
 
     public NimGrotesque(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{6}{B}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{B}");
         this.subtype.add(SubType.ZOMBIE);
 
         this.power = new MageInt(3);
         this.toughness = new MageInt(6);
 
         // Nim Grotesque gets +1/+0 for each artifact you control.
-        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostSourceEffect(new PermanentsOnBattlefieldCount(new FilterControlledArtifactPermanent()), StaticValue.get(0), Duration.WhileOnBattlefield)));
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
+                new BoostSourceEffect(ArtifactYouControlCount.instance, StaticValue.get(0), Duration.WhileOnBattlefield))
+                .addHint(ArtifactYouControlHint.instance)
+        );
     }
 
     public NimGrotesque(final NimGrotesque card) {
diff --git a/Mage.Sets/src/mage/cards/o/OverrideCard.java b/Mage.Sets/src/mage/cards/o/OverrideCard.java
index d69ba56a7d..59ad499c0c 100644
--- a/Mage.Sets/src/mage/cards/o/OverrideCard.java
+++ b/Mage.Sets/src/mage/cards/o/OverrideCard.java
@@ -4,12 +4,12 @@ import mage.MageObject;
 import mage.abilities.Ability;
 import mage.abilities.Mode;
 import mage.abilities.costs.Cost;
+import mage.abilities.dynamicvalue.common.ArtifactYouControlCount;
 import mage.abilities.effects.OneShotEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.Outcome;
-import mage.filter.common.FilterArtifactPermanent;
 import mage.game.Game;
 import mage.game.stack.StackObject;
 import mage.players.Player;
@@ -64,7 +64,7 @@ class OverrideEffect extends OneShotEffect {
             Player player = game.getPlayer(spell.getControllerId());
             Player controller = game.getPlayer(source.getControllerId());
             if (player != null && controller != null) {
-                int amount = game.getBattlefield().countAll(new FilterArtifactPermanent(), source.getControllerId(), game);
+                int amount = ArtifactYouControlCount.instance.calculate(game, source, this);
                 if (amount > 0) {
                     Cost cost = ManaUtil.createManaCost(amount, false);
                     if (!cost.pay(source, game, spell.getControllerId(), spell.getControllerId(), false)) {
diff --git a/Mage.Sets/src/mage/cards/p/PowerstoneShard.java b/Mage.Sets/src/mage/cards/p/PowerstoneShard.java
index 7862217f16..722f1d88bd 100644
--- a/Mage.Sets/src/mage/cards/p/PowerstoneShard.java
+++ b/Mage.Sets/src/mage/cards/p/PowerstoneShard.java
@@ -1,9 +1,9 @@
-
 package mage.cards.p;
 
-import java.util.UUID;
 import mage.Mana;
+import mage.abilities.dynamicvalue.DynamicValue;
 import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
+import mage.abilities.hint.ValueHint;
 import mage.abilities.mana.DynamicManaAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -11,8 +11,9 @@ import mage.constants.CardType;
 import mage.filter.common.FilterControlledPermanent;
 import mage.filter.predicate.mageobject.NamePredicate;
 
+import java.util.UUID;
+
 /**
- *
  * @author TheElk801
  */
 public final class PowerstoneShard extends CardImpl {
@@ -28,7 +29,9 @@ public final class PowerstoneShard extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}");
 
         // {T}: Add {C} for each artifact you control named Powerstone Shard.
-        this.addAbility(new DynamicManaAbility(Mana.ColorlessMana(1), new PermanentsOnBattlefieldCount(filter)));
+        DynamicValue xValue = new PermanentsOnBattlefieldCount(filter);
+        this.addAbility(new DynamicManaAbility(Mana.ColorlessMana(1), xValue)
+                .addHint(new ValueHint("Artifact you control named Powerstone Shard", xValue)));
     }
 
     public PowerstoneShard(final PowerstoneShard card) {
diff --git a/Mage.Sets/src/mage/cards/t/TidyConclusion.java b/Mage.Sets/src/mage/cards/t/TidyConclusion.java
index 69b74d23b7..c362070795 100644
--- a/Mage.Sets/src/mage/cards/t/TidyConclusion.java
+++ b/Mage.Sets/src/mage/cards/t/TidyConclusion.java
@@ -1,29 +1,29 @@
-
 package mage.cards.t;
 
-import java.util.UUID;
-import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
+import mage.abilities.dynamicvalue.common.ArtifactYouControlCount;
 import mage.abilities.effects.common.DestroyTargetEffect;
 import mage.abilities.effects.common.GainLifeEffect;
+import mage.abilities.hint.common.ArtifactYouControlHint;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.filter.common.FilterControlledArtifactPermanent;
 import mage.target.common.TargetCreaturePermanent;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class TidyConclusion extends CardImpl {
 
     public TidyConclusion(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{B}{B}");
+        super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{B}{B}");
 
         // Destroy target creature. You gain 1 life for each artifact you control.
         this.getSpellAbility().addEffect(new DestroyTargetEffect());
         this.getSpellAbility().addTarget(new TargetCreaturePermanent());
-        this.getSpellAbility().addEffect(new GainLifeEffect(new PermanentsOnBattlefieldCount(new FilterControlledArtifactPermanent())));
+        this.getSpellAbility().addEffect(new GainLifeEffect(ArtifactYouControlCount.instance));
+        this.getSpellAbility().addHint(ArtifactYouControlHint.instance);
     }
 
     public TidyConclusion(final TidyConclusion card) {
diff --git a/Mage.Sets/src/mage/cards/u/UrzaLordHighArtificer.java b/Mage.Sets/src/mage/cards/u/UrzaLordHighArtificer.java
index 616e8dbb6e..2d783f9150 100644
--- a/Mage.Sets/src/mage/cards/u/UrzaLordHighArtificer.java
+++ b/Mage.Sets/src/mage/cards/u/UrzaLordHighArtificer.java
@@ -1,16 +1,17 @@
 package mage.cards.u;
 
 import mage.MageInt;
+import mage.MageObject;
 import mage.Mana;
 import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.common.TapTargetCost;
 import mage.abilities.costs.mana.GenericManaCost;
-
-import mage.abilities.effects.ContinuousEffect;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.CreateTokenEffect;
+import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect;
+import mage.abilities.hint.common.ArtifactYouControlHint;
 import mage.abilities.mana.SimpleManaAbility;
 import mage.cards.Card;
 import mage.cards.CardImpl;
@@ -24,12 +25,8 @@ import mage.game.Game;
 import mage.game.permanent.token.KarnConstructToken;
 import mage.players.Player;
 import mage.target.common.TargetControlledPermanent;
-import mage.target.targetpointer.FixedTargets;
-import mage.util.CardUtil;
 
 import java.util.UUID;
-import mage.MageObject;
-import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect;
 
 /**
  * @author TheElk801
@@ -53,7 +50,9 @@ public final class UrzaLordHighArtificer extends CardImpl {
         this.toughness = new MageInt(4);
 
         // When Urza, Lord High Artificer enters the battlefield, create a 0/0 colorless Construct artifact creature token with "This creature gets +1/+1 for each artifact you control."
-        this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new KarnConstructToken())));
+        this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new KarnConstructToken()))
+                .addHint(ArtifactYouControlHint.instance)
+        );
 
         // Tap an untapped artifact you control: Add {U}.
         this.addAbility(new SimpleManaAbility(
@@ -100,7 +99,7 @@ class UrzaLordHighArtificerEffect extends OneShotEffect {
         }
         controller.shuffleLibrary(source, game);
         Card card = controller.getLibrary().getFromTop(game);
-        return PlayFromNotOwnHandZoneTargetEffect.exileAndPlayFromExile(game, source, card, 
-                TargetController.YOU, Duration.EndOfTurn, true);          
+        return PlayFromNotOwnHandZoneTargetEffect.exileAndPlayFromExile(game, source, card,
+                TargetController.YOU, Duration.EndOfTurn, true);
     }
 }
diff --git a/Mage.Sets/src/mage/cards/v/VaultOfCatlacan.java b/Mage.Sets/src/mage/cards/v/VaultOfCatlacan.java
index 06ea9db04b..2fa55288f7 100644
--- a/Mage.Sets/src/mage/cards/v/VaultOfCatlacan.java
+++ b/Mage.Sets/src/mage/cards/v/VaultOfCatlacan.java
@@ -1,14 +1,13 @@
-
 package mage.cards.v;
 
-import java.util.UUID;
 import mage.Mana;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.costs.common.TapSourceCost;
-import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
-import mage.abilities.effects.mana.DynamicManaEffect;
+import mage.abilities.dynamicvalue.common.ArtifactYouControlCount;
 import mage.abilities.effects.common.InfoEffect;
+import mage.abilities.effects.mana.DynamicManaEffect;
+import mage.abilities.hint.common.ArtifactYouControlHint;
 import mage.abilities.mana.AnyColorManaAbility;
 import mage.abilities.mana.SimpleManaAbility;
 import mage.cards.CardImpl;
@@ -16,10 +15,10 @@ import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.SuperType;
 import mage.constants.Zone;
-import mage.filter.StaticFilters;
+
+import java.util.UUID;
 
 /**
- *
  * @author LevelX2
  */
 public final class VaultOfCatlacan extends CardImpl {
@@ -40,8 +39,9 @@ public final class VaultOfCatlacan extends CardImpl {
 
         // {T}: Add {U} for each artifact you control.
         this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD,
-                new DynamicManaEffect(Mana.BlueMana(1), new PermanentsOnBattlefieldCount(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT)),
-                new TapSourceCost()));
+                new DynamicManaEffect(Mana.BlueMana(1), ArtifactYouControlCount.instance),
+                new TapSourceCost())
+                .addHint(ArtifactYouControlHint.instance));
 
     }
 
diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/ArtifactsYouControlCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/ArtifactYouControlCount.java
similarity index 76%
rename from Mage/src/main/java/mage/abilities/dynamicvalue/common/ArtifactsYouControlCount.java
rename to Mage/src/main/java/mage/abilities/dynamicvalue/common/ArtifactYouControlCount.java
index 93613cf69a..4c626a97a9 100644
--- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/ArtifactsYouControlCount.java
+++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/ArtifactYouControlCount.java
@@ -9,7 +9,7 @@ import mage.game.Game;
 /**
  * @author JayDi85
  */
-public enum ArtifactsYouControlCount implements DynamicValue {
+public enum ArtifactYouControlCount implements DynamicValue {
 
     instance;
 
@@ -19,17 +19,17 @@ public enum ArtifactsYouControlCount implements DynamicValue {
     }
 
     @Override
-    public ArtifactsYouControlCount copy() {
+    public ArtifactYouControlCount copy() {
         return instance;
     }
 
     @Override
     public String toString() {
-        return "X";
+        return "1"; // uses "for each" effects, so must be 1, not X
     }
 
     @Override
     public String getMessage() {
-        return "artifacts you control";
+        return "artifact you control";
     }
 }
diff --git a/Mage/src/main/java/mage/abilities/hint/common/ArtifactsYouControlHint.java b/Mage/src/main/java/mage/abilities/hint/common/ArtifactYouControlHint.java
similarity index 73%
rename from Mage/src/main/java/mage/abilities/hint/common/ArtifactsYouControlHint.java
rename to Mage/src/main/java/mage/abilities/hint/common/ArtifactYouControlHint.java
index d4b253da8e..960c416385 100644
--- a/Mage/src/main/java/mage/abilities/hint/common/ArtifactsYouControlHint.java
+++ b/Mage/src/main/java/mage/abilities/hint/common/ArtifactYouControlHint.java
@@ -1,7 +1,7 @@
 package mage.abilities.hint.common;
 
 import mage.abilities.Ability;
-import mage.abilities.dynamicvalue.common.ArtifactsYouControlCount;
+import mage.abilities.dynamicvalue.common.ArtifactYouControlCount;
 import mage.abilities.hint.Hint;
 import mage.abilities.hint.ValueHint;
 import mage.game.Game;
@@ -9,10 +9,10 @@ import mage.game.Game;
 /**
  * @author JayDi85
  */
-public enum ArtifactsYouControlHint implements Hint {
+public enum ArtifactYouControlHint implements Hint {
 
     instance;
-    private static final Hint hint = new ValueHint("Artifacts you control", ArtifactsYouControlCount.instance);
+    private static final Hint hint = new ValueHint("Artifacts you control", ArtifactYouControlCount.instance);
 
     @Override
     public String getText(Game game, Ability ability) {
diff --git a/Mage/src/main/java/mage/abilities/keyword/AffinityForArtifactsAbility.java b/Mage/src/main/java/mage/abilities/keyword/AffinityForArtifactsAbility.java
index 26cecb80a6..9f35ffadbc 100644
--- a/Mage/src/main/java/mage/abilities/keyword/AffinityForArtifactsAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/AffinityForArtifactsAbility.java
@@ -2,7 +2,7 @@ package mage.abilities.keyword;
 
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.common.AffinityEffect;
-import mage.abilities.hint.common.ArtifactsYouControlHint;
+import mage.abilities.hint.common.ArtifactYouControlHint;
 import mage.constants.Zone;
 import mage.filter.StaticFilters;
 
@@ -15,7 +15,7 @@ public class AffinityForArtifactsAbility extends SimpleStaticAbility {
         super(Zone.ALL, new AffinityEffect(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT));
         setRuleAtTheTop(true);
 
-        this.addHint(ArtifactsYouControlHint.instance);
+        this.addHint(ArtifactYouControlHint.instance);
     }
 
     public AffinityForArtifactsAbility(final AffinityForArtifactsAbility ability) {
diff --git a/Mage/src/main/java/mage/game/permanent/token/KarnConstructToken.java b/Mage/src/main/java/mage/game/permanent/token/KarnConstructToken.java
index 842cc6d7da..95b330293d 100644
--- a/Mage/src/main/java/mage/game/permanent/token/KarnConstructToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/KarnConstructToken.java
@@ -2,14 +2,12 @@ package mage.game.permanent.token;
 
 import mage.MageInt;
 import mage.abilities.common.SimpleStaticAbility;
-import mage.abilities.dynamicvalue.DynamicValue;
-import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
+import mage.abilities.dynamicvalue.common.ArtifactYouControlCount;
 import mage.abilities.effects.common.continuous.BoostSourceEffect;
 import mage.constants.CardType;
 import mage.constants.Duration;
 import mage.constants.SubType;
 import mage.constants.Zone;
-import mage.filter.common.FilterControlledPermanent;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -20,12 +18,6 @@ import java.util.List;
  */
 public final class KarnConstructToken extends TokenImpl {
 
-    private static final FilterControlledPermanent filter = new FilterControlledPermanent("artifacts you control");
-
-    static {
-        filter.add(CardType.ARTIFACT.getPredicate());
-    }
-
     static final private List<String> tokenImageSets = new ArrayList<>();
 
     static {
@@ -41,10 +33,8 @@ public final class KarnConstructToken extends TokenImpl {
         power = new MageInt(0);
         toughness = new MageInt(0);
 
-        DynamicValue value = new PermanentsOnBattlefieldCount(filter);
-        this.addAbility(new SimpleStaticAbility(
-                Zone.BATTLEFIELD,
-                new BoostSourceEffect(value, value, Duration.WhileOnBattlefield)
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
+                new BoostSourceEffect(ArtifactYouControlCount.instance, ArtifactYouControlCount.instance, Duration.WhileOnBattlefield)
                         .setText("This creature gets +1/+1 for each artifact you control")
         ));
     }

From 4652ebd790ed0d7d12d2e334c51288b3be31abcf Mon Sep 17 00:00:00 2001
From: htrajan <htrajan2002@yahoo.com>
Date: Sun, 28 Jun 2020 23:06:29 -0700
Subject: [PATCH 546/586] [M21] Fix Enthralling Hold (#6745)

---
 Mage.Sets/src/mage/cards/d/DreamLeash.java    | 19 ++++++++--
 .../src/mage/cards/e/EnthrallingHold.java     | 38 +++++++++++++------
 2 files changed, 42 insertions(+), 15 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/d/DreamLeash.java b/Mage.Sets/src/mage/cards/d/DreamLeash.java
index bccc18cffd..b557bd4bf4 100644
--- a/Mage.Sets/src/mage/cards/d/DreamLeash.java
+++ b/Mage.Sets/src/mage/cards/d/DreamLeash.java
@@ -1,7 +1,6 @@
 
 package mage.cards.d;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.common.AttachEffect;
@@ -10,13 +9,15 @@ import mage.abilities.keyword.EnchantAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.SubType;
 import mage.constants.Outcome;
+import mage.constants.SubType;
 import mage.constants.Zone;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
 import mage.target.TargetPermanent;
 
+import java.util.UUID;
+
 /**
  *
  * @author maxlebedev
@@ -52,10 +53,20 @@ public final class DreamLeash extends CardImpl {
 
 class DreamLeashTarget extends TargetPermanent {
 
+    DreamLeashTarget() {}
+
+    private DreamLeashTarget(DreamLeashTarget target) {
+        super(target);
+    }
+
+    @Override
+    public DreamLeashTarget copy() {
+        return new DreamLeashTarget(this);
+    }
+
     @Override
     public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
-
-        if(super.canTarget(controllerId, id, source, game)){
+        if (super.canTarget(controllerId, id, source, game)) {
             Permanent permanent = game.getPermanent(id);
             return permanent.isTapped();
         }
diff --git a/Mage.Sets/src/mage/cards/e/EnthrallingHold.java b/Mage.Sets/src/mage/cards/e/EnthrallingHold.java
index 9060709592..fa28c52a39 100644
--- a/Mage.Sets/src/mage/cards/e/EnthrallingHold.java
+++ b/Mage.Sets/src/mage/cards/e/EnthrallingHold.java
@@ -12,9 +12,8 @@ import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.constants.SubType;
 import mage.constants.Zone;
-import mage.filter.FilterPermanent;
-import mage.filter.common.FilterCreaturePermanent;
-import mage.filter.predicate.permanent.TappedPredicate;
+import mage.game.Game;
+import mage.game.permanent.Permanent;
 import mage.target.TargetPermanent;
 import mage.target.common.TargetCreaturePermanent;
 
@@ -26,26 +25,19 @@ import java.util.UUID;
  */
 public final class EnthrallingHold extends CardImpl {
 
-    private static final FilterPermanent filter = new FilterCreaturePermanent();
-
-    static {
-        filter.add(TappedPredicate.instance);
-    }
-
     public EnthrallingHold(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}{U}");
         
         this.subtype.add(SubType.AURA);
 
         // Enchant creature
-        TargetPermanent auraTarget = new TargetCreaturePermanent();
+        TargetPermanent auraTarget = new EnthrallingHoldTarget();
         this.getSpellAbility().addTarget(auraTarget);
         this.getSpellAbility().addEffect(new AttachEffect(Outcome.GainControl));
         Ability ability = new EnchantAbility(auraTarget.getTargetName());
         this.addAbility(ability);
 
         // You can't choose an untapped creature as this spell's target as you cast it.
-        auraTarget.replaceFilter(filter);
         Effect controlEnchantedEffect = new ControlEnchantedEffect();
         controlEnchantedEffect.setText("You can't choose an untapped creature as this spell's target as you cast it.<br>" + controlEnchantedEffect.getText(null));
 
@@ -62,3 +54,27 @@ public final class EnthrallingHold extends CardImpl {
         return new EnthrallingHold(this);
     }
 }
+
+class EnthrallingHoldTarget extends TargetCreaturePermanent {
+
+    EnthrallingHoldTarget() {}
+
+    private EnthrallingHoldTarget(EnthrallingHoldTarget target) {
+        super(target);
+    }
+
+    @Override
+    public EnthrallingHoldTarget copy() {
+        return new EnthrallingHoldTarget(this);
+    }
+
+    @Override
+    public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
+        if (super.canTarget(controllerId, id, source, game)) {
+            Permanent permanent = game.getPermanent(id);
+            return permanent.isTapped();
+        }
+        return false;
+    }
+
+}
\ No newline at end of file

From ca29e61b1d136530803470717ef3422878fd4276 Mon Sep 17 00:00:00 2001
From: htrajan <htrajan2002@yahoo.com>
Date: Mon, 29 Jun 2020 01:41:46 -0700
Subject: [PATCH 547/586] [M21] actually fix Enthralling Hold (#6746)

---
 Mage.Sets/src/mage/cards/d/DreamLeash.java    |  7 ++
 .../src/mage/cards/e/EnthrallingHold.java     |  9 ++-
 .../cards/single/m21/EnthrallingHoldTest.java | 79 +++++++++++++++++++
 Mage/src/main/java/mage/target/Target.java    |  2 +
 .../src/main/java/mage/target/TargetImpl.java | 16 +++-
 5 files changed, 110 insertions(+), 3 deletions(-)
 create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/single/m21/EnthrallingHoldTest.java

diff --git a/Mage.Sets/src/mage/cards/d/DreamLeash.java b/Mage.Sets/src/mage/cards/d/DreamLeash.java
index b557bd4bf4..a68a361904 100644
--- a/Mage.Sets/src/mage/cards/d/DreamLeash.java
+++ b/Mage.Sets/src/mage/cards/d/DreamLeash.java
@@ -12,6 +12,7 @@ import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.constants.SubType;
 import mage.constants.Zone;
+import mage.filter.StaticFilters;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
 import mage.target.TargetPermanent;
@@ -73,4 +74,10 @@ class DreamLeashTarget extends TargetPermanent {
         return false;
     }
 
+    // See ruling: https://www.mtgsalvation.com/forums/magic-fundamentals/magic-rulings/magic-rulings-archives/253345-dream-leash
+    @Override
+    public boolean stillLegalTarget(UUID id, Ability source, Game game) {
+        Permanent permanent = game.getPermanent(id);
+        return permanent != null && StaticFilters.FILTER_PERMANENT.match(permanent, game);
+    }
 }
diff --git a/Mage.Sets/src/mage/cards/e/EnthrallingHold.java b/Mage.Sets/src/mage/cards/e/EnthrallingHold.java
index fa28c52a39..f1a53c0a3f 100644
--- a/Mage.Sets/src/mage/cards/e/EnthrallingHold.java
+++ b/Mage.Sets/src/mage/cards/e/EnthrallingHold.java
@@ -12,6 +12,7 @@ import mage.constants.CardType;
 import mage.constants.Outcome;
 import mage.constants.SubType;
 import mage.constants.Zone;
+import mage.filter.StaticFilters;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
 import mage.target.TargetPermanent;
@@ -27,7 +28,7 @@ public final class EnthrallingHold extends CardImpl {
 
     public EnthrallingHold(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}{U}");
-        
+
         this.subtype.add(SubType.AURA);
 
         // Enchant creature
@@ -77,4 +78,10 @@ class EnthrallingHoldTarget extends TargetCreaturePermanent {
         return false;
     }
 
+    // See ruling: https://www.mtgsalvation.com/forums/magic-fundamentals/magic-rulings/magic-rulings-archives/253345-dream-leash
+    @Override
+    public boolean stillLegalTarget(UUID id, Ability source, Game game) {
+        Permanent permanent = game.getPermanent(id);
+        return permanent != null && StaticFilters.FILTER_PERMANENT_CREATURE.match(permanent, game);
+    }
 }
\ No newline at end of file
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/EnthrallingHoldTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/EnthrallingHoldTest.java
new file mode 100644
index 0000000000..a322395590
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/EnthrallingHoldTest.java
@@ -0,0 +1,79 @@
+package org.mage.test.cards.single.m21;
+
+import mage.constants.PhaseStep;
+import mage.constants.Zone;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestPlayerBase;
+
+public class EnthrallingHoldTest extends CardTestPlayerBase {
+
+    @Test
+    public void testTappedTarget_untapped_doesNotFizzle() {
+        // Traxos, Scourge of Kroog enters the battlefield tapped and doesn't untap during your untap step.
+        addCard(Zone.BATTLEFIELD, playerB, "Traxos, Scourge of Kroog");
+
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 6);
+        /*
+         * {3}{U}{U}
+         * Enchant creature
+         * You can't choose an untapped creature as this spell's target as you cast it.
+         * You control enchanted creature.
+         */
+        addCard(Zone.HAND, playerA, "Enthralling Hold");
+        /*
+         * {U}
+         * You may tap or untap target artifact, creature, or land.
+         */
+        addCard(Zone.HAND, playerA, "Twiddle");
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Enthralling Hold", "Traxos, Scourge of Kroog");
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Twiddle", "Traxos, Scourge of Kroog");
+
+        setChoice(playerA, "Yes");
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerB, "Traxos, Scourge of Kroog", 0);
+        assertPermanentCount(playerA, "Traxos, Scourge of Kroog", 1);
+        assertPermanentCount(playerA, "Enthralling Hold", 1);
+    }
+
+    @Test
+    public void testTappedTarget_becomesIllegal_fizzles() {
+        // Traxos, Scourge of Kroog enters the battlefield tapped and doesn't untap during your untap step.
+        addCard(Zone.BATTLEFIELD, playerB, "Traxos, Scourge of Kroog");
+
+        addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5);
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
+        /*
+         * {3}{U}{U}
+         * Enchant creature
+         * You can't choose an untapped creature as this spell's target as you cast it.
+         * You control enchanted creature.
+         */
+        addCard(Zone.HAND, playerA, "Enthralling Hold");
+        /*
+         * {1}{B}
+         * Destroy target nonblack creature
+         */
+        addCard(Zone.HAND, playerA, "Doom Blade");
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Enthralling Hold", "Traxos, Scourge of Kroog");
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Doom Blade", "Traxos, Scourge of Kroog");
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerB, "Traxos, Scourge of Kroog", 0);
+        assertPermanentCount(playerA, "Traxos, Scourge of Kroog", 0);
+
+        assertGraveyardCount(playerB, "Traxos, Scourge of Kroog", 1);
+        assertGraveyardCount(playerA, "Enthralling Hold", 1);
+        assertGraveyardCount(playerA, "Doom Blade", 1);
+    }
+}
diff --git a/Mage/src/main/java/mage/target/Target.java b/Mage/src/main/java/mage/target/Target.java
index 267c97558b..87c6e89c07 100644
--- a/Mage/src/main/java/mage/target/Target.java
+++ b/Mage/src/main/java/mage/target/Target.java
@@ -52,6 +52,8 @@ public interface Target extends Serializable {
 
     boolean canTarget(UUID id, Ability source, Game game);
 
+    boolean stillLegalTarget(UUID id, Ability source, Game game);
+
     boolean canTarget(UUID playerId, UUID id, Ability source, Game game);
 
     boolean isLegal(Ability source, Game game);
diff --git a/Mage/src/main/java/mage/target/TargetImpl.java b/Mage/src/main/java/mage/target/TargetImpl.java
index 9d0eaebd0d..c7e656c70e 100644
--- a/Mage/src/main/java/mage/target/TargetImpl.java
+++ b/Mage/src/main/java/mage/target/TargetImpl.java
@@ -12,7 +12,14 @@ import mage.game.events.GameEvent.EventType;
 import mage.players.Player;
 import mage.util.RandomUtil;
 
-import java.util.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
 
 /**
  * @author BetaSteward_at_googlemail.com
@@ -337,7 +344,7 @@ public abstract class TargetImpl implements Target {
                 illegalTargets.add(targetId);
                 continue;
             }
-            if (!canTarget(targetId, source, game)) {
+            if (!stillLegalTarget(targetId, source, game)) {
                 illegalTargets.add(targetId);
             }
         }
@@ -473,6 +480,11 @@ public abstract class TargetImpl implements Target {
         return null;
     }
 
+    @Override
+    public boolean stillLegalTarget(UUID id, Ability source, Game game) {
+        return canTarget(id, source, game);
+    }
+
     @Override
     public void setNotTarget(boolean notTarget) {
         this.notTarget = notTarget;

From cf3feff76a247689ae5425eddfa75a20a273e888 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Mon, 29 Jun 2020 12:52:14 +0400
Subject: [PATCH 548/586] Cost reduction effects - refactor, removed redundant
 custom effects, added card hints;

---
 .../src/mage/cards/a/AncientStoneIdol.java    |  63 ++------
 .../src/mage/cards/a/AvatarOfGrowth.java      |   6 +-
 Mage.Sets/src/mage/cards/b/BedlamReveler.java |  21 ++-
 Mage.Sets/src/mage/cards/b/BrineGiant.java    |  55 ++-----
 .../src/mage/cards/c/CrypticSerpent.java      |  13 +-
 Mage.Sets/src/mage/cards/d/DreamLeash.java    |  11 +-
 Mage.Sets/src/mage/cards/e/Embercleave.java   |  55 ++-----
 .../src/mage/cards/e/EmryLurkerOfTheLoch.java |  44 +-----
 .../src/mage/cards/f/FoulTongueShriek.java    |  21 +--
 Mage.Sets/src/mage/cards/g/GateColossus.java  |   8 +-
 .../src/mage/cards/g/GatekeeperGargoyle.java  |   2 +-
 .../src/mage/cards/g/GearseekerSerpent.java   |  52 ++-----
 Mage.Sets/src/mage/cards/g/Ghoultree.java     |  16 +-
 .../mage/cards/g/GlaiveOfTheGuildpact.java    |   5 +-
 Mage.Sets/src/mage/cards/h/HoldTheGates.java  |  11 +-
 Mage.Sets/src/mage/cards/k/KhalniHydra.java   |  71 +++------
 Mage.Sets/src/mage/cards/m/Molderhulk.java    |  16 +-
 .../src/mage/cards/n/NemesisOfMortals.java    |  12 +-
 .../src/mage/cards/o/OreScaleGuardian.java    |  11 +-
 .../mage/cards/t/TheCauldronOfEternity.java   |  63 ++------
 .../src/mage/cards/t/TheCircleOfLoyalty.java  |  61 +++-----
 .../src/mage/cards/t/TheMagicMirror.java      |  15 +-
 .../mage/cards/t/TorgaarFamineIncarnate.java  |  25 ++--
 .../modification/CostReduceForEachTest.java   | 140 ++++++++++++++++++
 .../base/impl/CardTestPlayerAPIImpl.java      |   6 +
 .../common/AttackingCreatureCount.java        |  28 +++-
 .../common/AttackingFilterCreatureCount.java  |  65 --------
 .../common/GateYouControlCount.java           |   4 +-
 .../continuous/BoostControlledEffect.java     |  30 ++--
 .../cost/CostModificationEffectImpl.java      |   6 +
 ...ReductionForEachCardInGraveyardEffect.java |  59 --------
 ...SpellCostReductionForEachSourceEffect.java |  93 ++++++++++++
 .../cost/SpellCostReductionSourceEffect.java  |   5 +-
 ...CostReductionSourceForOpponentsEffect.java |  42 ------
 .../abilities/keyword/UndauntedAbility.java   |  14 +-
 35 files changed, 506 insertions(+), 643 deletions(-)
 create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostReduceForEachTest.java
 delete mode 100644 Mage/src/main/java/mage/abilities/dynamicvalue/common/AttackingFilterCreatureCount.java
 delete mode 100644 Mage/src/main/java/mage/abilities/effects/common/cost/SourceCostReductionForEachCardInGraveyardEffect.java
 create mode 100644 Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionForEachSourceEffect.java
 delete mode 100644 Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionSourceForOpponentsEffect.java

diff --git a/Mage.Sets/src/mage/cards/a/AncientStoneIdol.java b/Mage.Sets/src/mage/cards/a/AncientStoneIdol.java
index bc7ddf8dac..499c85e265 100644
--- a/Mage.Sets/src/mage/cards/a/AncientStoneIdol.java
+++ b/Mage.Sets/src/mage/cards/a/AncientStoneIdol.java
@@ -1,31 +1,25 @@
 package mage.cards.a;
 
-import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.Ability;
-import mage.abilities.SpellAbility;
 import mage.abilities.common.DiesTriggeredAbility;
 import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.dynamicvalue.DynamicValue;
+import mage.abilities.dynamicvalue.common.AttackingCreatureCount;
 import mage.abilities.effects.common.CreateTokenEffect;
-import mage.abilities.effects.common.cost.CostModificationEffectImpl;
-import mage.constants.SubType;
+import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect;
+import mage.abilities.hint.ValueHint;
 import mage.abilities.keyword.FlashAbility;
 import mage.abilities.keyword.TrampleAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.CostModificationType;
-import mage.constants.Duration;
-import mage.constants.Outcome;
+import mage.constants.SubType;
 import mage.constants.Zone;
-import mage.filter.common.FilterCreaturePermanent;
-import mage.filter.predicate.permanent.AttackingPredicate;
-import mage.game.Game;
 import mage.game.permanent.token.StoneTrapIdolToken;
-import mage.util.CardUtil;
+
+import java.util.UUID;
 
 /**
- *
  * @author TheElk801
  */
 public final class AncientStoneIdol extends CardImpl {
@@ -41,7 +35,10 @@ public final class AncientStoneIdol extends CardImpl {
         this.addAbility(FlashAbility.getInstance());
 
         // This spell costs {1} less to cast for each attacking creature.
-        this.addAbility(new SimpleStaticAbility(Zone.ALL, new AncientStoneIdolCostReductionEffect()));
+        DynamicValue xValue = new AttackingCreatureCount();
+        this.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionForEachSourceEffect(1, xValue))
+                .addHint(new ValueHint("Attacking creatures", xValue))
+        );
 
         // Trample
         this.addAbility(TrampleAbility.getInstance());
@@ -59,41 +56,3 @@ public final class AncientStoneIdol extends CardImpl {
         return new AncientStoneIdol(this);
     }
 }
-
-class AncientStoneIdolCostReductionEffect extends CostModificationEffectImpl {
-
-    private static final FilterCreaturePermanent filter = new FilterCreaturePermanent();
-
-    static {
-        filter.add(AttackingPredicate.instance);
-    }
-
-    public AncientStoneIdolCostReductionEffect() {
-        super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.REDUCE_COST);
-        staticText = "This spell costs {1} less to cast for each attacking creature";
-    }
-
-    protected AncientStoneIdolCostReductionEffect(AncientStoneIdolCostReductionEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        int reductionAmount = game.getBattlefield().count(filter, source.getSourceId(), source.getControllerId(), game);
-        CardUtil.reduceCost(abilityToModify, reductionAmount);
-        return true;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        if ((abilityToModify instanceof SpellAbility) && abilityToModify.getSourceId().equals(source.getSourceId())) {
-            return game.getCard(abilityToModify.getSourceId()) != null;
-        }
-        return false;
-    }
-
-    @Override
-    public AncientStoneIdolCostReductionEffect copy() {
-        return new AncientStoneIdolCostReductionEffect(this);
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/a/AvatarOfGrowth.java b/Mage.Sets/src/mage/cards/a/AvatarOfGrowth.java
index cd41b415dd..0bee9d45c8 100644
--- a/Mage.Sets/src/mage/cards/a/AvatarOfGrowth.java
+++ b/Mage.Sets/src/mage/cards/a/AvatarOfGrowth.java
@@ -4,8 +4,9 @@ import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.dynamicvalue.common.OpponentsCount;
 import mage.abilities.effects.OneShotEffect;
-import mage.abilities.effects.common.cost.SpellCostReductionSourceForOpponentsEffect;
+import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect;
 import mage.abilities.keyword.TrampleAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -35,7 +36,8 @@ public final class AvatarOfGrowth extends CardImpl {
         this.toughness = new MageInt(4);
 
         // This spell costs {1} less to cast for each opponent you have.
-        this.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceForOpponentsEffect("This spell costs {1} less to cast for each opponent you have")));
+        this.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionForEachSourceEffect(1, OpponentsCount.instance)
+                .setText("This spell costs {1} less to cast for each opponent you have")));
 
         // Trample
         this.addAbility(TrampleAbility.getInstance());
diff --git a/Mage.Sets/src/mage/cards/b/BedlamReveler.java b/Mage.Sets/src/mage/cards/b/BedlamReveler.java
index 6f6175f7cc..5e4a2b29cc 100644
--- a/Mage.Sets/src/mage/cards/b/BedlamReveler.java
+++ b/Mage.Sets/src/mage/cards/b/BedlamReveler.java
@@ -1,4 +1,3 @@
-
 package mage.cards.b;
 
 import mage.MageInt;
@@ -8,7 +7,7 @@ import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.dynamicvalue.DynamicValue;
 import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
-import mage.abilities.effects.common.cost.SourceCostReductionForEachCardInGraveyardEffect;
+import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect;
 import mage.abilities.effects.common.discard.DiscardHandControllerEffect;
 import mage.abilities.hint.ValueHint;
 import mage.abilities.keyword.ProwessAbility;
@@ -26,27 +25,25 @@ import java.util.UUID;
  */
 public final class BedlamReveler extends CardImpl {
 
-    private static final DynamicValue cardsCount = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY);
-
     public BedlamReveler(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{R}{R}");
         this.subtype.add(SubType.DEVIL, SubType.HORROR);
         this.power = new MageInt(3);
         this.toughness = new MageInt(4);
 
-        // Bedlam Reveler costs {1} less to cast for each instant or sorcery card in your graveyard.
-        this.addAbility(new SimpleStaticAbility(
-                Zone.ALL, new SourceCostReductionForEachCardInGraveyardEffect(StaticFilters.FILTER_CARD_INSTANT_AND_SORCERY)
-        ).addHint(new ValueHint("Instant and sorcery cards in your graveyard", cardsCount)));
+        // This spell costs {1} less to cast for each instant and sorcery card in your graveyard.
+        DynamicValue xValue = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY);
+        Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionForEachSourceEffect(1, xValue));
+        ability.setRuleAtTheTop(true);
+        ability.addHint(new ValueHint("Instant or sourcery card in your graveyard", xValue));
+        this.addAbility(ability);
 
         // Prowess
         this.addAbility(new ProwessAbility());
 
         // When Bedlam Reveler enters the battlefield, discard your hand, then draw three cards.
-        Ability ability = new EntersBattlefieldTriggeredAbility(
-                new DiscardHandControllerEffect().setText("discard your hand,")
-        );
-        ability.addEffect(new DrawCardSourceControllerEffect(3).setText("then draw three cards"));
+        ability = new EntersBattlefieldTriggeredAbility(new DiscardHandControllerEffect());
+        ability.addEffect(new DrawCardSourceControllerEffect(3).concatBy(", then"));
         this.addAbility(ability);
     }
 
diff --git a/Mage.Sets/src/mage/cards/b/BrineGiant.java b/Mage.Sets/src/mage/cards/b/BrineGiant.java
index 9b02061df9..98d9ca61f4 100644
--- a/Mage.Sets/src/mage/cards/b/BrineGiant.java
+++ b/Mage.Sets/src/mage/cards/b/BrineGiant.java
@@ -1,18 +1,17 @@
 package mage.cards.b;
 
 import mage.MageInt;
-import mage.abilities.Ability;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.dynamicvalue.DynamicValue;
 import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
-import mage.abilities.effects.common.cost.CostModificationEffectImpl;
+import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect;
 import mage.abilities.hint.ValueHint;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.*;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.Zone;
 import mage.filter.common.FilterControlledPermanent;
-import mage.game.Game;
-import mage.util.CardUtil;
 
 import java.util.UUID;
 
@@ -21,8 +20,13 @@ import java.util.UUID;
  */
 public final class BrineGiant extends CardImpl {
 
-    private static final DynamicValue xValue
-            = new PermanentsOnBattlefieldCount(BrineGiantCostReductionEffect.filter);
+    static final FilterControlledPermanent filter = new FilterControlledPermanent("enchantment you control");
+
+    static {
+        filter.add(CardType.ENCHANTMENT.getPredicate());
+    }
+
+    private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter);
 
     public BrineGiant(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{U}");
@@ -33,7 +37,7 @@ public final class BrineGiant extends CardImpl {
 
         // This spell costs {1} less to cast for each enchantment you control.
         this.addAbility(new SimpleStaticAbility(
-                Zone.ALL, new BrineGiantCostReductionEffect()
+                Zone.ALL, new SpellCostReductionForEachSourceEffect(1, xValue)
         ).addHint(new ValueHint("Enchantments you control", xValue)));
     }
 
@@ -45,39 +49,4 @@ public final class BrineGiant extends CardImpl {
     public BrineGiant copy() {
         return new BrineGiant(this);
     }
-}
-
-class BrineGiantCostReductionEffect extends CostModificationEffectImpl {
-
-    static final FilterControlledPermanent filter = new FilterControlledPermanent();
-
-    static {
-        filter.add(CardType.ENCHANTMENT.getPredicate());
-    }
-
-    BrineGiantCostReductionEffect() {
-        super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.REDUCE_COST);
-        staticText = "This spell costs {1} less to cast for each enchantment you control";
-    }
-
-    private BrineGiantCostReductionEffect(final BrineGiantCostReductionEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        int count = game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(), game).size();
-        CardUtil.reduceCost(abilityToModify, count);
-        return true;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        return abilityToModify.getSourceId().equals(source.getSourceId());
-    }
-
-    @Override
-    public BrineGiantCostReductionEffect copy() {
-        return new BrineGiantCostReductionEffect(this);
-    }
 }
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/c/CrypticSerpent.java b/Mage.Sets/src/mage/cards/c/CrypticSerpent.java
index fb28357301..bf49861457 100644
--- a/Mage.Sets/src/mage/cards/c/CrypticSerpent.java
+++ b/Mage.Sets/src/mage/cards/c/CrypticSerpent.java
@@ -1,10 +1,11 @@
 package mage.cards.c;
 
 import mage.MageInt;
+import mage.abilities.Ability;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.dynamicvalue.DynamicValue;
 import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount;
-import mage.abilities.effects.common.cost.SourceCostReductionForEachCardInGraveyardEffect;
+import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect;
 import mage.abilities.hint.ValueHint;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -12,7 +13,6 @@ import mage.constants.CardType;
 import mage.constants.SubType;
 import mage.constants.Zone;
 import mage.filter.StaticFilters;
-import mage.filter.common.FilterInstantOrSorceryCard;
 
 import java.util.UUID;
 
@@ -21,8 +21,6 @@ import java.util.UUID;
  */
 public final class CrypticSerpent extends CardImpl {
 
-    private static final DynamicValue cardsCount = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY);
-
     public CrypticSerpent(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{U}{U}");
 
@@ -31,8 +29,11 @@ public final class CrypticSerpent extends CardImpl {
         this.toughness = new MageInt(5);
 
         // Cryptic Serpent costs {1} less to cast for each instant and sorcery card in your graveyard.
-        this.addAbility(new SimpleStaticAbility(Zone.ALL, new SourceCostReductionForEachCardInGraveyardEffect(new FilterInstantOrSorceryCard()))
-                .addHint(new ValueHint("Instant and sorcery card in your graveyard", cardsCount)));
+        DynamicValue xValue = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_INSTANT_AND_SORCERY);
+        Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionForEachSourceEffect(1, xValue));
+        ability.setRuleAtTheTop(true);
+        ability.addHint(new ValueHint("Instant and sorcery card in your graveyard", xValue));
+        this.addAbility(ability);
     }
 
     public CrypticSerpent(final CrypticSerpent card) {
diff --git a/Mage.Sets/src/mage/cards/d/DreamLeash.java b/Mage.Sets/src/mage/cards/d/DreamLeash.java
index bccc18cffd..8fd1f3da98 100644
--- a/Mage.Sets/src/mage/cards/d/DreamLeash.java
+++ b/Mage.Sets/src/mage/cards/d/DreamLeash.java
@@ -1,7 +1,5 @@
-
 package mage.cards.d;
 
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.common.AttachEffect;
@@ -10,21 +8,22 @@ import mage.abilities.keyword.EnchantAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.SubType;
 import mage.constants.Outcome;
+import mage.constants.SubType;
 import mage.constants.Zone;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
 import mage.target.TargetPermanent;
 
+import java.util.UUID;
+
 /**
- *
  * @author maxlebedev
  */
 public final class DreamLeash extends CardImpl {
 
     public DreamLeash(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{U}{U}");
+        super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}{U}");
         this.subtype.add(SubType.AURA);
 
         // Enchant permanent
@@ -55,7 +54,7 @@ class DreamLeashTarget extends TargetPermanent {
     @Override
     public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
 
-        if(super.canTarget(controllerId, id, source, game)){
+        if (super.canTarget(controllerId, id, source, game)) {
             Permanent permanent = game.getPermanent(id);
             return permanent.isTapped();
         }
diff --git a/Mage.Sets/src/mage/cards/e/Embercleave.java b/Mage.Sets/src/mage/cards/e/Embercleave.java
index 2c0d2baa7d..35851640f0 100644
--- a/Mage.Sets/src/mage/cards/e/Embercleave.java
+++ b/Mage.Sets/src/mage/cards/e/Embercleave.java
@@ -1,13 +1,15 @@
 package mage.cards.e;
 
 import mage.abilities.Ability;
-import mage.abilities.SpellAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.dynamicvalue.DynamicValue;
+import mage.abilities.dynamicvalue.common.AttackingCreatureCount;
 import mage.abilities.effects.common.AttachEffect;
 import mage.abilities.effects.common.continuous.BoostEquippedEffect;
 import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect;
-import mage.abilities.effects.common.cost.CostModificationEffectImpl;
+import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect;
+import mage.abilities.hint.ValueHint;
 import mage.abilities.keyword.DoubleStrikeAbility;
 import mage.abilities.keyword.EquipAbility;
 import mage.abilities.keyword.FlashAbility;
@@ -15,12 +17,8 @@ import mage.abilities.keyword.TrampleAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.*;
-import mage.filter.FilterPermanent;
-import mage.filter.common.FilterControlledCreaturePermanent;
-import mage.filter.predicate.permanent.AttackingPredicate;
-import mage.game.Game;
+import mage.filter.StaticFilters;
 import mage.target.common.TargetControlledCreaturePermanent;
-import mage.util.CardUtil;
 
 import java.util.UUID;
 
@@ -39,7 +37,10 @@ public final class Embercleave extends CardImpl {
         this.addAbility(FlashAbility.getInstance());
 
         // This spell costs {1} less to cast for each attacking creature you control.
-        this.addAbility(new SimpleStaticAbility(Zone.ALL, new EmbercleaveCostReductionEffect()));
+        DynamicValue xValue = new AttackingCreatureCount(StaticFilters.FILTER_PERMANENT_CREATURE_CONTROLLED);
+        this.addAbility(new SimpleStaticAbility(Zone.ALL,
+                new SpellCostReductionForEachSourceEffect(1, xValue)
+        ).addHint(new ValueHint("Attacking creature you control", xValue)));
 
         // When Embercleave enters the battlefield, attach it to target creature you control.
         Ability ability = new EntersBattlefieldTriggeredAbility(new AttachEffect(
@@ -71,41 +72,3 @@ public final class Embercleave extends CardImpl {
         return new Embercleave(this);
     }
 }
-
-class EmbercleaveCostReductionEffect extends CostModificationEffectImpl {
-
-    private static final FilterPermanent filter = new FilterControlledCreaturePermanent();
-
-    static {
-        filter.add(AttackingPredicate.instance);
-    }
-
-    EmbercleaveCostReductionEffect() {
-        super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.REDUCE_COST);
-        staticText = "This spell costs {1} less to cast for each attacking creature you control";
-    }
-
-    private EmbercleaveCostReductionEffect(EmbercleaveCostReductionEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        int reductionAmount = game.getBattlefield().count(filter, source.getSourceId(), source.getControllerId(), game);
-        CardUtil.reduceCost(abilityToModify, reductionAmount);
-        return true;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        if ((abilityToModify instanceof SpellAbility) && abilityToModify.getSourceId().equals(source.getSourceId())) {
-            return game.getCard(abilityToModify.getSourceId()) != null;
-        }
-        return false;
-    }
-
-    @Override
-    public EmbercleaveCostReductionEffect copy() {
-        return new EmbercleaveCostReductionEffect(this);
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/e/EmryLurkerOfTheLoch.java b/Mage.Sets/src/mage/cards/e/EmryLurkerOfTheLoch.java
index b1cc1188f4..bebd901a61 100644
--- a/Mage.Sets/src/mage/cards/e/EmryLurkerOfTheLoch.java
+++ b/Mage.Sets/src/mage/cards/e/EmryLurkerOfTheLoch.java
@@ -2,21 +2,21 @@ package mage.cards.e;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.SpellAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.costs.common.TapSourceCost;
+import mage.abilities.dynamicvalue.common.ArtifactYouControlCount;
 import mage.abilities.effects.AsThoughEffectImpl;
 import mage.abilities.effects.common.PutTopCardOfLibraryIntoGraveControllerEffect;
-import mage.abilities.effects.common.cost.CostModificationEffectImpl;
+import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect;
+import mage.abilities.hint.common.ArtifactYouControlHint;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.*;
 import mage.filter.StaticFilters;
 import mage.game.Game;
 import mage.target.common.TargetCardInYourGraveyard;
-import mage.util.CardUtil;
 
 import java.util.UUID;
 
@@ -35,7 +35,9 @@ public final class EmryLurkerOfTheLoch extends CardImpl {
         this.toughness = new MageInt(2);
 
         // This spell costs {1} less to cast for each artifact you control.
-        this.addAbility(new SimpleStaticAbility(Zone.ALL, new EmryLurkerOfTheLochCostReductionEffect()));
+        this.addAbility(new SimpleStaticAbility(Zone.ALL,
+                new SpellCostReductionForEachSourceEffect(1, ArtifactYouControlCount.instance)
+        ).addHint(ArtifactYouControlHint.instance));
 
         // When Emry, Lurker of the Loch enters the battlefield, put the top four cards of your library into your graveyard.
         this.addAbility(new EntersBattlefieldTriggeredAbility(
@@ -58,40 +60,6 @@ public final class EmryLurkerOfTheLoch extends CardImpl {
     }
 }
 
-class EmryLurkerOfTheLochCostReductionEffect extends CostModificationEffectImpl {
-
-    EmryLurkerOfTheLochCostReductionEffect() {
-        super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.REDUCE_COST);
-        staticText = "This spell costs {1} less to cast for each artifact you control";
-    }
-
-    private EmryLurkerOfTheLochCostReductionEffect(final EmryLurkerOfTheLochCostReductionEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        int reductionAmount = game.getBattlefield().count(
-                StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT,
-                source.getSourceId(), source.getControllerId(), game
-        );
-        CardUtil.reduceCost(abilityToModify, reductionAmount);
-        return true;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        return abilityToModify instanceof SpellAbility
-                && abilityToModify.getSourceId().equals(source.getSourceId())
-                && game.getCard(abilityToModify.getSourceId()) != null;
-    }
-
-    @Override
-    public EmryLurkerOfTheLochCostReductionEffect copy() {
-        return new EmryLurkerOfTheLochCostReductionEffect(this);
-    }
-}
-
 class EmryLurkerOfTheLochPlayEffect extends AsThoughEffectImpl {
 
     EmryLurkerOfTheLochPlayEffect() {
diff --git a/Mage.Sets/src/mage/cards/f/FoulTongueShriek.java b/Mage.Sets/src/mage/cards/f/FoulTongueShriek.java
index fda2829170..725a760e57 100644
--- a/Mage.Sets/src/mage/cards/f/FoulTongueShriek.java
+++ b/Mage.Sets/src/mage/cards/f/FoulTongueShriek.java
@@ -1,28 +1,26 @@
-
 package mage.cards.f;
 
-import java.util.UUID;
 import mage.abilities.Ability;
-import mage.abilities.dynamicvalue.common.AttackingFilterCreatureCount;
+import mage.abilities.dynamicvalue.common.AttackingCreatureCount;
 import mage.abilities.effects.OneShotEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.Outcome;
-import mage.constants.TargetController;
-import mage.filter.common.FilterCreaturePermanent;
+import mage.filter.StaticFilters;
 import mage.game.Game;
 import mage.players.Player;
 import mage.target.common.TargetOpponent;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class FoulTongueShriek extends CardImpl {
 
     public FoulTongueShriek(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{B}");
+        super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}");
 
         // Target opponent loses 1 life for each attacking creature you control. You gain that much life.
         this.getSpellAbility().addEffect(new FoulTongueShriekEffect());
@@ -41,12 +39,7 @@ public final class FoulTongueShriek extends CardImpl {
 }
 
 class FoulTongueShriekEffect extends OneShotEffect {
-    
-    private static final FilterCreaturePermanent filter = new FilterCreaturePermanent();
-    static {
-        filter.add(TargetController.YOU.getControllerPredicate());
-    }
-    
+
     public FoulTongueShriekEffect() {
         super(Outcome.Benefit);
         this.staticText = "Target opponent loses 1 life for each attacking creature you control. You gain that much life";
@@ -66,7 +59,7 @@ class FoulTongueShriekEffect extends OneShotEffect {
         Player controller = game.getPlayer(source.getControllerId());
         Player targetOpponent = game.getPlayer(getTargetPointer().getFirst(game, source));
         if (controller != null && targetOpponent != null) {
-            int amount = new AttackingFilterCreatureCount(filter).calculate(game, source, this);
+            int amount = new AttackingCreatureCount(StaticFilters.FILTER_PERMANENT_CREATURES_CONTROLLED).calculate(game, source, this);
             if (amount > 0) {
                 targetOpponent.loseLife(amount, game, false);
                 controller.gainLife(amount, game, source);
diff --git a/Mage.Sets/src/mage/cards/g/GateColossus.java b/Mage.Sets/src/mage/cards/g/GateColossus.java
index e2387b7ccd..000c9e2692 100644
--- a/Mage.Sets/src/mage/cards/g/GateColossus.java
+++ b/Mage.Sets/src/mage/cards/g/GateColossus.java
@@ -5,9 +5,11 @@ import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldAllTriggeredAbility;
 import mage.abilities.common.SimpleEvasionAbility;
 import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.dynamicvalue.common.GateYouControlCount;
 import mage.abilities.effects.common.PutOnLibrarySourceEffect;
 import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesSourceEffect;
 import mage.abilities.effects.common.cost.CostModificationEffectImpl;
+import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect;
 import mage.abilities.hint.common.GateYouControlHint;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -39,8 +41,10 @@ public final class GateColossus extends CardImpl {
         this.toughness = new MageInt(8);
 
         // This spell costs {1} less to cast for each Gate you control.
-        this.addAbility(new SimpleStaticAbility(Zone.ALL, new GateColossusCostReductionEffect())
-                .addHint(GateYouControlHint.instance));
+        this.addAbility(new SimpleStaticAbility(Zone.ALL,
+                new SpellCostReductionForEachSourceEffect(1, GateYouControlCount.instance))
+                .addHint(GateYouControlHint.instance)
+        );
 
         // Gate Colossus can't be blocked by creatures with power 2 or less.
         this.addAbility(new SimpleEvasionAbility(new CantBeBlockedByCreaturesSourceEffect(filter, Duration.WhileOnBattlefield)));
diff --git a/Mage.Sets/src/mage/cards/g/GatekeeperGargoyle.java b/Mage.Sets/src/mage/cards/g/GatekeeperGargoyle.java
index 43a3218777..b112771820 100644
--- a/Mage.Sets/src/mage/cards/g/GatekeeperGargoyle.java
+++ b/Mage.Sets/src/mage/cards/g/GatekeeperGargoyle.java
@@ -29,7 +29,7 @@ public final class GatekeeperGargoyle extends CardImpl {
         // Flying
         this.addAbility(FlyingAbility.getInstance());
 
-        // Gargoyle Guardian enters the battlefield with a +1/+1 counter on it for each Gate you control.
+        // Gatekeeper Gargoyle enters the battlefield with a +1/+1 counter on it for each Gate you control.
         this.addAbility(new EntersBattlefieldAbility(
                 new AddCountersSourceEffect(
                         CounterType.P1P1.createInstance(),
diff --git a/Mage.Sets/src/mage/cards/g/GearseekerSerpent.java b/Mage.Sets/src/mage/cards/g/GearseekerSerpent.java
index 6d0991c962..8b955be607 100644
--- a/Mage.Sets/src/mage/cards/g/GearseekerSerpent.java
+++ b/Mage.Sets/src/mage/cards/g/GearseekerSerpent.java
@@ -1,18 +1,19 @@
 package mage.cards.g;
 
 import mage.MageInt;
-import mage.abilities.Ability;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.dynamicvalue.common.ArtifactYouControlCount;
 import mage.abilities.effects.common.combat.CantBeBlockedSourceEffect;
-import mage.abilities.effects.common.cost.CostModificationEffectImpl;
+import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect;
+import mage.abilities.hint.common.ArtifactYouControlHint;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.*;
-import mage.filter.common.FilterControlledPermanent;
-import mage.game.Game;
-import mage.util.CardUtil;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.SubType;
+import mage.constants.Zone;
 
 import java.util.UUID;
 
@@ -28,7 +29,9 @@ public final class GearseekerSerpent extends CardImpl {
         this.toughness = new MageInt(6);
 
         // Gearseeker Serpent costs {1} less to cast for each artifact you control
-        this.addAbility(new SimpleStaticAbility(Zone.ALL, new GearseekerSerpentCostReductionEffect()));
+        this.addAbility(new SimpleStaticAbility(Zone.ALL,
+                new SpellCostReductionForEachSourceEffect(1, ArtifactYouControlCount.instance)
+        ).addHint(ArtifactYouControlHint.instance));
 
         // 5U: Gearseeker Serpent can't be blocked this turn.
         this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD,
@@ -45,38 +48,3 @@ public final class GearseekerSerpent extends CardImpl {
         return new GearseekerSerpent(this);
     }
 }
-
-class GearseekerSerpentCostReductionEffect extends CostModificationEffectImpl {
-
-    private static final FilterControlledPermanent filter = new FilterControlledPermanent();
-
-    static {
-        filter.add(CardType.ARTIFACT.getPredicate());
-    }
-
-    public GearseekerSerpentCostReductionEffect() {
-        super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.REDUCE_COST);
-        staticText = "This spell costs {1} less to cast for each artifact you control";
-    }
-
-    protected GearseekerSerpentCostReductionEffect(final GearseekerSerpentCostReductionEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        int count = game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(), game).size();
-        CardUtil.reduceCost(abilityToModify, count);
-        return true;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        return abilityToModify.getSourceId().equals(source.getSourceId());
-    }
-
-    @Override
-    public GearseekerSerpentCostReductionEffect copy() {
-        return new GearseekerSerpentCostReductionEffect(this);
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/g/Ghoultree.java b/Mage.Sets/src/mage/cards/g/Ghoultree.java
index 63a9982da0..ae65b24830 100644
--- a/Mage.Sets/src/mage/cards/g/Ghoultree.java
+++ b/Mage.Sets/src/mage/cards/g/Ghoultree.java
@@ -1,9 +1,12 @@
 package mage.cards.g;
 
-import java.util.UUID;
 import mage.MageInt;
+import mage.abilities.Ability;
 import mage.abilities.common.SimpleStaticAbility;
-import mage.abilities.effects.common.cost.SourceCostReductionForEachCardInGraveyardEffect;
+import mage.abilities.dynamicvalue.DynamicValue;
+import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount;
+import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect;
+import mage.abilities.hint.ValueHint;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
@@ -11,8 +14,9 @@ import mage.constants.SubType;
 import mage.constants.Zone;
 import mage.filter.StaticFilters;
 
+import java.util.UUID;
+
 /**
- *
  * @author North
  */
 public final class Ghoultree extends CardImpl {
@@ -26,7 +30,11 @@ public final class Ghoultree extends CardImpl {
         this.toughness = new MageInt(10);
 
         // Ghoultree costs {1} less to cast for each creature card in your graveyard.
-        this.addAbility(new SimpleStaticAbility(Zone.ALL, new SourceCostReductionForEachCardInGraveyardEffect(StaticFilters.FILTER_CARD_CREATURE)));
+        DynamicValue xValue = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_CREATURE);
+        Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionForEachSourceEffect(1, xValue));
+        ability.setRuleAtTheTop(true);
+        ability.addHint(new ValueHint("Creature card in your graveyard", xValue));
+        this.addAbility(ability);
     }
 
     public Ghoultree(final Ghoultree card) {
diff --git a/Mage.Sets/src/mage/cards/g/GlaiveOfTheGuildpact.java b/Mage.Sets/src/mage/cards/g/GlaiveOfTheGuildpact.java
index 160e3ddda0..69eaad0167 100644
--- a/Mage.Sets/src/mage/cards/g/GlaiveOfTheGuildpact.java
+++ b/Mage.Sets/src/mage/cards/g/GlaiveOfTheGuildpact.java
@@ -29,10 +29,7 @@ public final class GlaiveOfTheGuildpact extends CardImpl {
         this.subtype.add(SubType.EQUIPMENT);
 
         // Equipped creature gets +1/+0 for each Gate you control and has vigilance and menace.
-        Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(
-                GateYouControlCount.instance,
-                StaticValue.get(0)
-        ).setText("Equipped creature gets +1/+0 for each Gate you control"));
+        Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(GateYouControlCount.instance, StaticValue.get(0)));
         ability.addEffect(new GainAbilityAttachedEffect(
                 VigilanceAbility.getInstance(), AttachmentType.EQUIPMENT
         ).setText("and has vigilance"));
diff --git a/Mage.Sets/src/mage/cards/h/HoldTheGates.java b/Mage.Sets/src/mage/cards/h/HoldTheGates.java
index 83d95b9eaf..1cdda2a3b1 100644
--- a/Mage.Sets/src/mage/cards/h/HoldTheGates.java
+++ b/Mage.Sets/src/mage/cards/h/HoldTheGates.java
@@ -13,7 +13,7 @@ import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.Duration;
 import mage.constants.Zone;
-import mage.filter.common.FilterControlledCreaturePermanent;
+import mage.filter.StaticFilters;
 
 import java.util.UUID;
 
@@ -25,13 +25,14 @@ public final class HoldTheGates extends CardImpl {
     public HoldTheGates(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}");
 
-
         // Creatures you control get +0/+1 for each Gate you control and have vigilance.
         Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD,
                 new BoostControlledEffect(StaticValue.get(0), GateYouControlCount.instance, Duration.WhileOnBattlefield)
-                        .setText("Creatures you control get +0/+1 for each Gate you control "));
-        ability.addEffect(new GainAbilityControlledEffect(VigilanceAbility.getInstance(), Duration.WhileOnBattlefield, new FilterControlledCreaturePermanent("Creatures"))
-                .setText("and have vigilance"));
+        );
+        ability.addEffect(
+                new GainAbilityControlledEffect(VigilanceAbility.getInstance(), Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURES_CONTROLLED)
+                        .setText("and have vigilance")
+        );
         ability.addHint(GateYouControlHint.instance);
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/k/KhalniHydra.java b/Mage.Sets/src/mage/cards/k/KhalniHydra.java
index b0d7f0a841..f37225ddcf 100644
--- a/Mage.Sets/src/mage/cards/k/KhalniHydra.java
+++ b/Mage.Sets/src/mage/cards/k/KhalniHydra.java
@@ -1,48 +1,52 @@
-
 package mage.cards.k;
 
-import java.util.Iterator;
-import java.util.UUID;
 import mage.MageInt;
 import mage.ObjectColor;
-import mage.abilities.Ability;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.costs.mana.ManaCost;
-import mage.abilities.effects.OneShotEffect;
+import mage.abilities.costs.mana.ManaCosts;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.dynamicvalue.DynamicValue;
+import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
+import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect;
+import mage.abilities.hint.ValueHint;
 import mage.abilities.keyword.TrampleAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.SubType;
-import mage.constants.Outcome;
 import mage.constants.Zone;
 import mage.filter.common.FilterControlledCreaturePermanent;
 import mage.filter.predicate.mageobject.ColorPredicate;
-import mage.game.Game;
+
+import java.util.UUID;
 
 /**
- *
  * @author maurer.it_at_gmail.com
  */
 public final class KhalniHydra extends CardImpl {
 
-    private static final FilterControlledCreaturePermanent filter;
+    private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("green creature you control");
 
     static {
-        filter = new FilterControlledCreaturePermanent();
         filter.add(new ColorPredicate(ObjectColor.GREEN));
     }
 
     public KhalniHydra(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{G}{G}{G}{G}{G}{G}{G}{G}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}{G}{G}{G}{G}{G}{G}{G}");
         this.subtype.add(SubType.HYDRA);
 
         this.power = new MageInt(8);
         this.toughness = new MageInt(8);
-        
+
         // This spell costs {G} less to cast for each green creature you control.
-        this.addAbility(new SimpleStaticAbility(Zone.ALL, new KhalniHydraCostReductionEffect()));
-        
+        ManaCosts<ManaCost> manaReduce = new ManaCostsImpl<>("{G}");
+        DynamicValue xValue = new PermanentsOnBattlefieldCount(filter);
+        this.addAbility(new SimpleStaticAbility(Zone.ALL,
+                new SpellCostReductionForEachSourceEffect(manaReduce, xValue))
+                .addHint(new ValueHint("Green creature you control", xValue))
+        );
+
         // Trample
         this.addAbility(TrampleAbility.getInstance());
     }
@@ -51,47 +55,8 @@ public final class KhalniHydra extends CardImpl {
         super(card);
     }
 
-    @Override
-    public void adjustCosts(Ability ability, Game game) {
-        super.adjustCosts(ability, game);
-        int reductionAmount = game.getBattlefield().count(filter,  ability.getSourceId(), ability.getControllerId(), game);
-        Iterator<ManaCost> iter = ability.getManaCostsToPay().iterator();
-
-        while ( reductionAmount > 0 && iter.hasNext() ) {
-            ManaCost manaCostEntry = iter.next();
-            if (manaCostEntry.getMana().getGreen() > 0) { // in case another effect adds additional mana cost
-                iter.remove();
-                reductionAmount--;
-            }
-        }
-    }
-
     @Override
     public KhalniHydra copy() {
         return new KhalniHydra(this);
     }
 }
-
-class KhalniHydraCostReductionEffect extends OneShotEffect {
-    private static final String effectText = "{this} costs {G} less to cast for each green creature you control";
-
-    KhalniHydraCostReductionEffect ( ) {
-        super(Outcome.Benefit);
-        this.staticText = effectText;
-    }
-
-    KhalniHydraCostReductionEffect ( KhalniHydraCostReductionEffect effect ) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source) {
-        return false;
-    }
-
-    @Override
-    public KhalniHydraCostReductionEffect copy() {
-        return new KhalniHydraCostReductionEffect(this);
-    }
-
-}
diff --git a/Mage.Sets/src/mage/cards/m/Molderhulk.java b/Mage.Sets/src/mage/cards/m/Molderhulk.java
index 9f65632312..9f38fec52a 100644
--- a/Mage.Sets/src/mage/cards/m/Molderhulk.java
+++ b/Mage.Sets/src/mage/cards/m/Molderhulk.java
@@ -1,25 +1,28 @@
 package mage.cards.m;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.dynamicvalue.DynamicValue;
+import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount;
 import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
-import mage.abilities.effects.common.cost.SourceCostReductionForEachCardInGraveyardEffect;
-import mage.constants.SubType;
+import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect;
+import mage.abilities.hint.ValueHint;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.AbilityWord;
 import mage.constants.CardType;
+import mage.constants.SubType;
 import mage.constants.Zone;
 import mage.filter.FilterCard;
 import mage.filter.StaticFilters;
 import mage.filter.common.FilterLandCard;
 import mage.target.common.TargetCardInYourGraveyard;
 
+import java.util.UUID;
+
 /**
- *
  * @author TheElk801
  */
 public final class Molderhulk extends CardImpl {
@@ -36,8 +39,11 @@ public final class Molderhulk extends CardImpl {
         this.toughness = new MageInt(6);
 
         // Undergrowth — This spell costs {1} less to cast for each creature card in your graveyard.
-        Ability ability = new SimpleStaticAbility(Zone.ALL, new SourceCostReductionForEachCardInGraveyardEffect(StaticFilters.FILTER_CARD_CREATURE));
+        DynamicValue xValue = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_CREATURE);
+        Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionForEachSourceEffect(1, xValue));
         ability.setAbilityWord(AbilityWord.UNDERGROWTH);
+        ability.setRuleAtTheTop(true);
+        ability.addHint(new ValueHint("Creature card in your graveyard", xValue));
         this.addAbility(ability);
 
         // When Molderhulk enters the battlefield, return target land card from your graveyard to the battlefield.
diff --git a/Mage.Sets/src/mage/cards/n/NemesisOfMortals.java b/Mage.Sets/src/mage/cards/n/NemesisOfMortals.java
index 45bcc9b02d..4f96bbbc4c 100644
--- a/Mage.Sets/src/mage/cards/n/NemesisOfMortals.java
+++ b/Mage.Sets/src/mage/cards/n/NemesisOfMortals.java
@@ -1,12 +1,14 @@
 package mage.cards.n;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.dynamicvalue.DynamicValue;
+import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.cost.CostModificationEffectImpl;
-import mage.abilities.effects.common.cost.SourceCostReductionForEachCardInGraveyardEffect;
+import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect;
+import mage.abilities.hint.ValueHint;
 import mage.abilities.keyword.MonstrosityAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -16,6 +18,8 @@ import mage.game.Game;
 import mage.players.Player;
 import mage.util.CardUtil;
 
+import java.util.UUID;
+
 /**
  * @author LevelX2
  */
@@ -29,8 +33,10 @@ public final class NemesisOfMortals extends CardImpl {
         this.toughness = new MageInt(5);
 
         // Nemesis of Mortals costs {1} less to cast for each creature card in your graveyard.
-        Ability ability = new SimpleStaticAbility(Zone.ALL, new SourceCostReductionForEachCardInGraveyardEffect(StaticFilters.FILTER_CARD_CREATURE));
+        DynamicValue xValue = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_CREATURE);
+        Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionForEachSourceEffect(1, xValue));
         ability.setRuleAtTheTop(true);
+        ability.addHint(new ValueHint("Creature card in your graveyard", xValue));
         this.addAbility(ability);
 
         // {7}{G}{G}: Monstrosity 5.  This ability costs {1} less to activate for each creature card in your graveyard.
diff --git a/Mage.Sets/src/mage/cards/o/OreScaleGuardian.java b/Mage.Sets/src/mage/cards/o/OreScaleGuardian.java
index cb8c54a0f6..95d09d2f8e 100644
--- a/Mage.Sets/src/mage/cards/o/OreScaleGuardian.java
+++ b/Mage.Sets/src/mage/cards/o/OreScaleGuardian.java
@@ -3,7 +3,10 @@ package mage.cards.o;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleStaticAbility;
-import mage.abilities.effects.common.cost.SourceCostReductionForEachCardInGraveyardEffect;
+import mage.abilities.dynamicvalue.DynamicValue;
+import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount;
+import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect;
+import mage.abilities.hint.ValueHint;
 import mage.abilities.keyword.FlyingAbility;
 import mage.abilities.keyword.HasteAbility;
 import mage.cards.CardImpl;
@@ -28,10 +31,10 @@ public final class OreScaleGuardian extends CardImpl {
         this.toughness = new MageInt(4);
 
         // This spell costs {1} less to cast for each land card in your graveyard.
-        Ability ability = new SimpleStaticAbility(
-                Zone.ALL, new SourceCostReductionForEachCardInGraveyardEffect(StaticFilters.FILTER_CARD_LAND)
-        );
+        DynamicValue xValue = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_LAND);
+        Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionForEachSourceEffect(1, xValue));
         ability.setRuleAtTheTop(true);
+        ability.addHint(new ValueHint("Land card in your graveyard", xValue));
         this.addAbility(ability);
 
         // Flying
diff --git a/Mage.Sets/src/mage/cards/t/TheCauldronOfEternity.java b/Mage.Sets/src/mage/cards/t/TheCauldronOfEternity.java
index e4a8026e2e..a1d627c1de 100644
--- a/Mage.Sets/src/mage/cards/t/TheCauldronOfEternity.java
+++ b/Mage.Sets/src/mage/cards/t/TheCauldronOfEternity.java
@@ -1,25 +1,25 @@
 package mage.cards.t;
 
-import mage.MageObject;
 import mage.abilities.Ability;
-import mage.abilities.SpellAbility;
 import mage.abilities.common.ActivateAsSorceryActivatedAbility;
 import mage.abilities.common.DiesCreatureTriggeredAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.costs.common.PayLifeCost;
 import mage.abilities.costs.common.TapSourceCost;
 import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.dynamicvalue.DynamicValue;
+import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount;
 import mage.abilities.effects.common.PutOnLibraryTargetEffect;
 import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
-import mage.abilities.effects.common.cost.CostModificationEffectImpl;
+import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect;
+import mage.abilities.hint.ValueHint;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.*;
+import mage.constants.CardType;
+import mage.constants.SuperType;
+import mage.constants.Zone;
 import mage.filter.StaticFilters;
-import mage.game.Game;
-import mage.players.Player;
 import mage.target.common.TargetCardInYourGraveyard;
-import mage.util.CardUtil;
 
 import java.util.UUID;
 
@@ -34,7 +34,11 @@ public final class TheCauldronOfEternity extends CardImpl {
         this.addSuperType(SuperType.LEGENDARY);
 
         // This spell costs {2} less to cast for each creature card in your graveyard.
-        this.addAbility(new SimpleStaticAbility(Zone.ALL, new TheCauldronOfEternityCostReductionEffect()));
+        DynamicValue xValue = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_CREATURE);
+        Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionForEachSourceEffect(2, xValue));
+        ability.setRuleAtTheTop(true);
+        ability.addHint(new ValueHint("Creature card in your graveyard", xValue));
+        this.addAbility(ability);
 
         // Whenever a creature you control dies, put it on the bottom of its owner's library.
         this.addAbility(new DiesCreatureTriggeredAbility(
@@ -43,7 +47,7 @@ public final class TheCauldronOfEternity extends CardImpl {
         ));
 
         // {2}{B}, {T}, Pay 2 life: Return target creature card from your graveyard to the battlefield. Activate this ability only any time you could cast a sorcery.
-        Ability ability = new ActivateAsSorceryActivatedAbility(
+        ability = new ActivateAsSorceryActivatedAbility(
                 Zone.BATTLEFIELD, new ReturnFromGraveyardToBattlefieldTargetEffect(), new ManaCostsImpl("{2}{B}")
         );
         ability.addCost(new TapSourceCost());
@@ -61,44 +65,3 @@ public final class TheCauldronOfEternity extends CardImpl {
         return new TheCauldronOfEternity(this);
     }
 }
-
-class TheCauldronOfEternityCostReductionEffect extends CostModificationEffectImpl {
-
-    TheCauldronOfEternityCostReductionEffect() {
-        super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.REDUCE_COST);
-        staticText = "This spell costs {2} less to cast for each creature card in your graveyard";
-    }
-
-    private TheCauldronOfEternityCostReductionEffect(final TheCauldronOfEternityCostReductionEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        Player player = game.getPlayer(source.getControllerId());
-        if (player == null) {
-            return false;
-        }
-        int reductionAmount = player
-                .getGraveyard()
-                .getCards(game)
-                .stream()
-                .filter(MageObject::isCreature)
-                .mapToInt(card -> 2)
-                .sum();
-        CardUtil.reduceCost(abilityToModify, reductionAmount);
-        return true;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        return abilityToModify instanceof SpellAbility
-                && abilityToModify.getSourceId().equals(source.getSourceId())
-                && game.getCard(abilityToModify.getSourceId()) != null;
-    }
-
-    @Override
-    public TheCauldronOfEternityCostReductionEffect copy() {
-        return new TheCauldronOfEternityCostReductionEffect(this);
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/t/TheCircleOfLoyalty.java b/Mage.Sets/src/mage/cards/t/TheCircleOfLoyalty.java
index ef80797958..6acd774073 100644
--- a/Mage.Sets/src/mage/cards/t/TheCircleOfLoyalty.java
+++ b/Mage.Sets/src/mage/cards/t/TheCircleOfLoyalty.java
@@ -1,24 +1,23 @@
 package mage.cards.t;
 
 import mage.abilities.Ability;
-import mage.abilities.SpellAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.common.SpellCastControllerTriggeredAbility;
 import mage.abilities.costs.common.TapSourceCost;
 import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.dynamicvalue.DynamicValue;
+import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.abilities.effects.common.continuous.BoostControlledEffect;
-import mage.abilities.effects.common.cost.CostModificationEffectImpl;
+import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect;
+import mage.abilities.hint.ValueHint;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.*;
-import mage.filter.FilterPermanent;
 import mage.filter.FilterSpell;
 import mage.filter.common.FilterControlledPermanent;
-import mage.game.Game;
 import mage.game.permanent.token.KnightToken;
-import mage.util.CardUtil;
 
 import java.util.UUID;
 
@@ -27,10 +26,16 @@ import java.util.UUID;
  */
 public final class TheCircleOfLoyalty extends CardImpl {
 
-    private static final FilterSpell filter = new FilterSpell("a legendary spell");
+    private static final FilterSpell filterLegendary = new FilterSpell("a legendary spell");
 
     static {
-        filter.add(SuperType.LEGENDARY.getPredicate());
+        filterLegendary.add(SuperType.LEGENDARY.getPredicate());
+    }
+
+    static final FilterControlledPermanent filterKnight = new FilterControlledPermanent("Knight you control");
+
+    static {
+        filterKnight.add(SubType.KNIGHT.getPredicate());
     }
 
     public TheCircleOfLoyalty(UUID ownerId, CardSetInfo setInfo) {
@@ -39,7 +44,10 @@ public final class TheCircleOfLoyalty extends CardImpl {
         this.addSuperType(SuperType.LEGENDARY);
 
         // This spell costs {1} less to cast for each Knight you control.
-        this.addAbility(new SimpleStaticAbility(Zone.ALL, new TheCircleOfLoyaltyCostReductionEffect()));
+        DynamicValue xValue = new PermanentsOnBattlefieldCount(filterKnight);
+        this.addAbility(new SimpleStaticAbility(
+                Zone.ALL, new SpellCostReductionForEachSourceEffect(1, xValue)
+        ).addHint(new ValueHint("Knight you control", xValue)));
 
         // Creatures you control get +1/+1.
         this.addAbility(new SimpleStaticAbility(
@@ -48,7 +56,7 @@ public final class TheCircleOfLoyalty extends CardImpl {
 
         // Whenever you cast a legendary spell, create a 2/2 white Knight creature token with vigilance.
         this.addAbility(new SpellCastControllerTriggeredAbility(
-                new CreateTokenEffect(new KnightToken()), filter, false
+                new CreateTokenEffect(new KnightToken()), filterLegendary, false
         ));
 
         // {3}{W}, {T}: Create a 2/2 white Knight creature token with vigilance.
@@ -67,37 +75,4 @@ public final class TheCircleOfLoyalty extends CardImpl {
     public TheCircleOfLoyalty copy() {
         return new TheCircleOfLoyalty(this);
     }
-}
-
-class TheCircleOfLoyaltyCostReductionEffect extends CostModificationEffectImpl {
-
-    private static final FilterPermanent filter = new FilterControlledPermanent(SubType.KNIGHT);
-
-    TheCircleOfLoyaltyCostReductionEffect() {
-        super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.REDUCE_COST);
-        staticText = "This spell costs {1} less to cast for each Knight you control";
-    }
-
-    private TheCircleOfLoyaltyCostReductionEffect(final TheCircleOfLoyaltyCostReductionEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        int reductionAmount = game.getBattlefield().count(filter, source.getSourceId(), source.getControllerId(), game);
-        CardUtil.reduceCost(abilityToModify, reductionAmount);
-        return true;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        return abilityToModify instanceof SpellAbility
-                && abilityToModify.getSourceId().equals(source.getSourceId())
-                && game.getCard(abilityToModify.getSourceId()) != null;
-    }
-
-    @Override
-    public TheCircleOfLoyaltyCostReductionEffect copy() {
-        return new TheCircleOfLoyaltyCostReductionEffect(this);
-    }
-}
+}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/t/TheMagicMirror.java b/Mage.Sets/src/mage/cards/t/TheMagicMirror.java
index 9090a80225..2238d81cfc 100644
--- a/Mage.Sets/src/mage/cards/t/TheMagicMirror.java
+++ b/Mage.Sets/src/mage/cards/t/TheMagicMirror.java
@@ -4,11 +4,13 @@ import mage.abilities.Ability;
 import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.dynamicvalue.DynamicValue;
+import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount;
 import mage.abilities.dynamicvalue.common.CountersSourceCount;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.abilities.effects.common.continuous.MaximumHandSizeControllerEffect;
-import mage.abilities.effects.common.cost.SourceCostReductionForEachCardInGraveyardEffect;
+import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect;
 import mage.abilities.effects.common.counter.AddCountersSourceEffect;
+import mage.abilities.hint.ValueHint;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.*;
@@ -30,10 +32,11 @@ public final class TheMagicMirror extends CardImpl {
         this.addSuperType(SuperType.LEGENDARY);
 
         // This spell costs {1} less to cast for each instant and sorcery card in your graveyard.
-        this.addAbility(new SimpleStaticAbility(
-                Zone.ALL, new SourceCostReductionForEachCardInGraveyardEffect(
-                StaticFilters.FILTER_CARD_INSTANT_AND_SORCERY
-        )).setRuleAtTheTop(true));
+        DynamicValue xValue = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_INSTANT_AND_SORCERY);
+        Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionForEachSourceEffect(1, xValue));
+        ability.setRuleAtTheTop(true);
+        ability.addHint(new ValueHint("Instant and sorcery card in your graveyard", xValue));
+        this.addAbility(ability);
 
         // You have no maximum hand size.
         this.addAbility(new SimpleStaticAbility(new MaximumHandSizeControllerEffect(
@@ -42,7 +45,7 @@ public final class TheMagicMirror extends CardImpl {
         )));
 
         // At the beginning of your upkeep, put a knowledge counter on The Magic Mirror, then draw a card for each knowledge counter on The Magic Mirror.
-        Ability ability = new BeginningOfUpkeepTriggeredAbility(
+        ability = new BeginningOfUpkeepTriggeredAbility(
                 new AddCountersSourceEffect(CounterType.KNOWLEDGE.createInstance())
                         .setText("put a knowledge counter on {this},"),
                 TargetController.YOU, false
diff --git a/Mage.Sets/src/mage/cards/t/TorgaarFamineIncarnate.java b/Mage.Sets/src/mage/cards/t/TorgaarFamineIncarnate.java
index 9cdaf57aba..18238ef627 100644
--- a/Mage.Sets/src/mage/cards/t/TorgaarFamineIncarnate.java
+++ b/Mage.Sets/src/mage/cards/t/TorgaarFamineIncarnate.java
@@ -1,7 +1,5 @@
-
 package mage.cards.t;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.SpellAbility;
@@ -13,21 +11,16 @@ import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.cost.CostModificationEffectImpl;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.CardType;
-import mage.constants.CostModificationType;
-import mage.constants.Duration;
-import mage.constants.Outcome;
-import mage.constants.SubType;
-import mage.constants.SuperType;
-import mage.constants.Zone;
+import mage.constants.*;
 import mage.filter.StaticFilters;
 import mage.game.Game;
 import mage.players.Player;
 import mage.target.TargetPlayer;
 import mage.util.CardUtil;
 
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class TorgaarFamineIncarnate extends CardImpl {
@@ -107,8 +100,16 @@ class TorgaarFamineIncarnateEffectCostReductionEffect extends CostModificationEf
         SpellAbility spellAbility = (SpellAbility) abilityToModify;
         for (Cost cost : spellAbility.getCosts()) {
             if (cost instanceof SacrificeXTargetCost) {
-                int reduction = ((SacrificeXTargetCost) cost).getAmount();
-                CardUtil.adjustCost(spellAbility, reduction * 2);
+                if (game.inCheckPlayableState()) {
+                    // allows to cast in getPlayable
+                    int reduction = ((SacrificeXTargetCost) cost).getMaxValue(spellAbility, game);
+                    CardUtil.adjustCost(spellAbility, reduction * 2);
+                } else {
+                    // real cast
+                    int reduction = ((SacrificeXTargetCost) cost).getAmount();
+                    CardUtil.adjustCost(spellAbility, reduction * 2);
+                }
+
                 break;
             }
         }
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostReduceForEachTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostReduceForEachTest.java
new file mode 100644
index 0000000000..8303f02800
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostReduceForEachTest.java
@@ -0,0 +1,140 @@
+package org.mage.test.cards.cost.modification;
+
+import mage.constants.PhaseStep;
+import mage.constants.Zone;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps;
+
+/**
+ * @author JayDi85
+ */
+public class CostReduceForEachTest extends CardTestPlayerBaseWithAIHelps {
+
+    @Test
+    public void test_AncientStoneIdol_Attacking() {
+        // {10}
+        // Flash
+        // This spell costs {1} less to cast for each attacking creature.
+        addCard(Zone.HAND, playerA, "Ancient Stone Idol", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Swamp", 10 - 2);
+        addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 2); // give 2 cost reduction
+
+        // before
+        checkPlayableAbility("before attack", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Ancient Stone Idol", false);
+
+        // prepare for attack
+        attack(1, playerA, "Balduvian Bears");
+        attack(1, playerA, "Balduvian Bears");
+
+        // on attack
+        checkPlayableAbility("on attack", 1, PhaseStep.DECLARE_BLOCKERS, playerA, "Cast Ancient Stone Idol", true);
+        castSpell(1, PhaseStep.DECLARE_BLOCKERS, playerA, "Ancient Stone Idol");
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerA, "Ancient Stone Idol", 1);
+    }
+
+    @Test
+    public void test_AncientStoneIdol_AttackingWithSacrifice() {
+        // The total cost to cast a spell is locked in before you pay that cost. For example, if you control five attacking
+        // creatures, including one you can sacrifice to add {C} to your mana pool, Ancient Stone Idol costs {5} to cast.
+        // Then you can sacrifice the creature when you activate mana abilities just before paying the cost, and it still
+        // costs only {5} to cast.
+        // (2018-07-13)
+
+        // {10}
+        // Flash
+        // This spell costs {1} less to cast for each attacking creature.
+        addCard(Zone.HAND, playerA, "Ancient Stone Idol", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Swamp", 10 - 4);
+        //
+        // Sacrifice Blood Pet: Add {B}.
+        addCard(Zone.BATTLEFIELD, playerA, "Blood Pet", 2); // give 2 cost reduction + can be sacrificed as 2 mana
+
+        // before
+        checkPlayableAbility("before attack", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Ancient Stone Idol", false);
+
+        // prepare for attack
+        attack(1, playerA, "Blood Pet");
+        attack(1, playerA, "Blood Pet");
+
+        // on attack (must automaticly sacrifice creatures as mana pay)
+        checkPlayableAbility("on attack", 1, PhaseStep.DECLARE_BLOCKERS, playerA, "Cast Ancient Stone Idol", true);
+        castSpell(1, PhaseStep.DECLARE_BLOCKERS, playerA, "Ancient Stone Idol");
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerA, "Ancient Stone Idol", 1);
+        assertGraveyardCount(playerA, "Blood Pet", 2);
+    }
+
+    @Test
+    public void test_KhalniHydra_ColorReduce() {
+        // {G}{G}{G}{G}{G}{G}{G}{G}
+        // This spell costs {G} less to cast for each green creature you control.
+        addCard(Zone.HAND, playerA, "Khalni Hydra", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 8 - 2);
+        addCard(Zone.HAND, playerA, "Balduvian Bears", 2); // give 2 cost reduction
+
+        checkPlayableAbility("no cost reduction 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Khalni Hydra", false);
+
+        // prepare creatures for reduce
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears");
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears");
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
+        checkPlayableAbility("no cost reduction 2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Khalni Hydra", false);
+
+        // can cast on next turn
+        castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Khalni Hydra");
+
+        setStrictChooseMode(true);
+        setStopAt(3, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerA, "Khalni Hydra", 1);
+    }
+
+    @Test
+    public void test_TorgaarFamineIncarnate_SacrificeXTargets() {
+        // {6}{B}{B}
+        // As an additional cost to cast this spell, you may sacrifice any number of creatures.
+        // This spell costs {2} less to cast for each creature sacrificed this way.
+        // When Torgaar, Famine Incarnate enters the battlefield, up to one target player's life total becomes half their starting life total, rounded down.
+        addCard(Zone.HAND, playerA, "Torgaar, Famine Incarnate", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Swamp", 8 - 4 - 2);
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 2);
+        addCard(Zone.HAND, playerA, "Balduvian Bears", 2); // give 4 cost reduction on sacrifice
+
+        checkPlayableAbility("no cost reduction 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Torgaar, Famine Incarnate", false);
+
+        // prepare creatures for reduce
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears");
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears");
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
+        checkPlayableAbility("no cost reduction 2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Torgaar, Famine Incarnate", false);
+
+        // can cast on next turn
+        checkPlayableAbility("must reduce", 3, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Torgaar, Famine Incarnate", true);
+        castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Torgaar, Famine Incarnate");
+        setChoice(playerA, "X=2"); // two creatures sacrifice
+        setChoice(playerA, "Balduvian Bears");
+        setChoice(playerA, "Balduvian Bears");
+        addTarget(playerA, playerB); // target player for half life
+
+        setStrictChooseMode(true);
+        setStopAt(3, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerA, "Torgaar, Famine Incarnate", 1);
+        assertLife(playerB, 20 / 2);
+    }
+}
diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
index 71ff178235..c3ae20419e 100644
--- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
+++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
@@ -1663,16 +1663,20 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
 
     public void attack(int turnNum, TestPlayer player, String attacker) {
         //Assert.assertNotEquals("", attacker);
+        assertAliaseSupportInActivateCommand(attacker, false); // it uses old special notation like card_name:index
         player.addAction(turnNum, PhaseStep.DECLARE_ATTACKERS, "attack:" + attacker);
     }
 
     public void attack(int turnNum, TestPlayer player, String attacker, TestPlayer defendingPlayer) {
         //Assert.assertNotEquals("", attacker);
+        assertAliaseSupportInActivateCommand(attacker, false); // it uses old special notation like card_name:index
         player.addAction(turnNum, PhaseStep.DECLARE_ATTACKERS, "attack:" + attacker + "$defendingPlayer=" + defendingPlayer.getName());
     }
 
     public void attack(int turnNum, TestPlayer player, String attacker, String planeswalker) {
         //Assert.assertNotEquals("", attacker);
+        assertAliaseSupportInActivateCommand(attacker, false); // it uses old special notation like card_name:index
+        assertAliaseSupportInActivateCommand(planeswalker, false);
         player.addAction(turnNum, PhaseStep.DECLARE_ATTACKERS, new StringBuilder("attack:").append(attacker).append("$planeswalker=").append(planeswalker).toString());
     }
 
@@ -1683,6 +1687,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     public void block(int turnNum, TestPlayer player, String blocker, String attacker) {
         //Assert.assertNotEquals("", blocker);
         //Assert.assertNotEquals("", attacker);
+        assertAliaseSupportInActivateCommand(blocker, false); // it uses old special notation like card_name:index
+        assertAliaseSupportInActivateCommand(attacker, false);
         player.addAction(turnNum, PhaseStep.DECLARE_BLOCKERS, "block:" + blocker + '$' + attacker);
     }
 
diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/AttackingCreatureCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/AttackingCreatureCount.java
index fa4ba981a5..5b19df74a5 100644
--- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/AttackingCreatureCount.java
+++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/AttackingCreatureCount.java
@@ -1,38 +1,60 @@
-
 package mage.abilities.dynamicvalue.common;
 
 import mage.abilities.Ability;
 import mage.abilities.dynamicvalue.DynamicValue;
 import mage.abilities.effects.Effect;
+import mage.filter.common.FilterCreaturePermanent;
 import mage.game.Game;
 import mage.game.combat.CombatGroup;
+import mage.game.permanent.Permanent;
+
+import java.util.UUID;
 
 /**
- *
  * @author LevelX2
  */
 public class AttackingCreatureCount implements DynamicValue {
 
     private String message;
+    private FilterCreaturePermanent filter;
 
     public AttackingCreatureCount() {
         this("attacking creature");
     }
 
+    public AttackingCreatureCount(FilterCreaturePermanent filter) {
+        this(filter, "attacking " + filter.getMessage());
+    }
+
     public AttackingCreatureCount(String message) {
+        this(null, message);
+    }
+
+    public AttackingCreatureCount(FilterCreaturePermanent filter, String message) {
         this.message = message;
+        this.filter = filter;
     }
 
     public AttackingCreatureCount(final AttackingCreatureCount dynamicValue) {
         super();
         this.message = dynamicValue.message;
+        this.filter = dynamicValue.filter;
     }
 
     @Override
     public int calculate(Game game, Ability sourceAbility, Effect effect) {
         int count = 0;
         for (CombatGroup combatGroup : game.getCombat().getGroups()) {
-            count += combatGroup.getAttackers().size();
+            for (UUID permId : combatGroup.getAttackers()) {
+                if (filter != null) {
+                    Permanent attacker = game.getPermanent(permId);
+                    if (attacker != null && filter.match(attacker, sourceAbility.getSourceId(), sourceAbility.getControllerId(), game)) {
+                        count++;
+                    }
+                } else {
+                    count++;
+                }
+            }
         }
         return count;
     }
diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/AttackingFilterCreatureCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/AttackingFilterCreatureCount.java
deleted file mode 100644
index 5cee8d7544..0000000000
--- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/AttackingFilterCreatureCount.java
+++ /dev/null
@@ -1,65 +0,0 @@
-
-package mage.abilities.dynamicvalue.common;
-
-import java.util.UUID;
-import mage.abilities.Ability;
-import mage.abilities.dynamicvalue.DynamicValue;
-import mage.abilities.effects.Effect;
-import mage.filter.common.FilterCreaturePermanent;
-import mage.game.Game;
-import mage.game.combat.CombatGroup;
-import mage.game.permanent.Permanent;
-
-/**
- *
- * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com)
- */
-public class AttackingFilterCreatureCount implements DynamicValue {
-
-    private FilterCreaturePermanent filter;
-    private String message;
-    
-    public AttackingFilterCreatureCount(FilterCreaturePermanent filter) {
-        this(filter, "attacking creature");
-    }
-
-    public AttackingFilterCreatureCount(FilterCreaturePermanent filter, String message) {
-        this.filter = filter;
-        this.message = message;
-    }
-
-    public AttackingFilterCreatureCount(final AttackingFilterCreatureCount dynamicValue) {
-        super();
-        this.message = dynamicValue.message;
-        this.filter = dynamicValue.filter;
-    }
-
-    @Override
-    public int calculate(Game game, Ability sourceAbility, Effect effect) {
-        int count = 0;
-        for (CombatGroup combatGroup : game.getCombat().getGroups()) {
-            for (UUID permId : combatGroup.getAttackers()) {
-                Permanent attacker = game.getPermanent(permId);
-                if (filter.match(attacker, sourceAbility.getSourceId(), sourceAbility.getControllerId(), game)) {                    
-                    count++;
-                }
-            }
-        }
-        return count;
-    }
-
-    @Override
-    public AttackingFilterCreatureCount copy() {
-        return new AttackingFilterCreatureCount(this);
-    }
-
-    @Override
-    public String getMessage() {
-        return message;
-    }
-
-    @Override
-    public String toString() {
-        return "X";
-    }
-}
diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/GateYouControlCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/GateYouControlCount.java
index 2045cdfac2..61a06c39c9 100644
--- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/GateYouControlCount.java
+++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/GateYouControlCount.java
@@ -32,11 +32,11 @@ public enum GateYouControlCount implements DynamicValue {
 
     @Override
     public String toString() {
-        return "X";
+        return "1"; // uses "for each" effects, so must be 1, not X
     }
 
     @Override
     public String getMessage() {
-        return "gate you control";
+        return "Gate you control";
     }
 }
diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostControlledEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostControlledEffect.java
index 901fd63335..8466445ba7 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostControlledEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostControlledEffect.java
@@ -1,7 +1,5 @@
-
 package mage.abilities.effects.common.continuous;
 
-import java.util.Iterator;
 import mage.MageObjectReference;
 import mage.abilities.Ability;
 import mage.abilities.dynamicvalue.DynamicValue;
@@ -16,8 +14,9 @@ import mage.filter.common.FilterCreaturePermanent;
 import mage.game.Game;
 import mage.game.permanent.Permanent;
 
+import java.util.Iterator;
+
 /**
- *
  * @author BetaSteward_at_googlemail.com
  */
 public class BoostControlledEffect extends ContinuousEffectImpl {
@@ -56,10 +55,10 @@ public class BoostControlledEffect extends ContinuousEffectImpl {
      * @param power
      * @param toughness
      * @param duration
-     * @param filter AnotherPredicate is not working, you need to use the
-     * excludeSource option
-     * @param lockedIn if true, power and toughness will be calculated only
-     * once, when the ability resolves
+     * @param filter        AnotherPredicate is not working, you need to use the
+     *                      excludeSource option
+     * @param lockedIn      if true, power and toughness will be calculated only
+     *                      once, when the ability resolves
      * @param excludeSource
      */
     public BoostControlledEffect(DynamicValue power, DynamicValue toughness, Duration duration, FilterCreaturePermanent filter, boolean excludeSource, boolean lockedIn) {
@@ -105,7 +104,7 @@ public class BoostControlledEffect extends ContinuousEffectImpl {
     @Override
     public boolean apply(Game game, Ability source) {
         if (this.affectedObjectsSet) {
-            for (Iterator<MageObjectReference> it = affectedObjectList.iterator(); it.hasNext();) {
+            for (Iterator<MageObjectReference> it = affectedObjectList.iterator(); it.hasNext(); ) {
                 Permanent permanent = it.next().getPermanent(game);
                 if (permanent != null) {
                     permanent.addPower(power.calculate(game, source, this));
@@ -126,7 +125,6 @@ public class BoostControlledEffect extends ContinuousEffectImpl {
     }
 
     private void setText() {
-        String message = null;
         StringBuilder sb = new StringBuilder();
         if (excludeSource) {
             sb.append("other ");
@@ -150,6 +148,9 @@ public class BoostControlledEffect extends ContinuousEffectImpl {
         sb.append(t);
 
         sb.append((duration == Duration.EndOfTurn ? " until end of turn" : ""));
+
+        // where X
+        String message = null;
         if (t.equals("X")) {
             message = toughness.getMessage();
         } else if (p.equals("X")) {
@@ -158,6 +159,17 @@ public class BoostControlledEffect extends ContinuousEffectImpl {
         if (message != null && !message.isEmpty()) {
             sb.append(", where X is ").append(message);
         }
+
+        // for each
+        if (message == null) {
+            message = toughness.getMessage();
+            if (message.isEmpty()) {
+                message = power.getMessage();
+            }
+            if (!message.isEmpty()) {
+                sb.append(" for each " + message);
+            }
+        }
         staticText = sb.toString();
     }
 
diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/CostModificationEffectImpl.java b/Mage/src/main/java/mage/abilities/effects/common/cost/CostModificationEffectImpl.java
index 53af0aca6b..a24c93acf8 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/cost/CostModificationEffectImpl.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/cost/CostModificationEffectImpl.java
@@ -12,6 +12,12 @@ import mage.game.Game;
 /**
  * Simple implementation of a {@link CostModificationEffect} offering simplified
  * construction to setup the object for use by the mage framework.
+ * <p>
+ * WARNING, if you implement custom effect and it can works on stack only (e.g. it need spell's targets to check) then
+ * use different apply code:
+ * - one for get playable mode before spell puts on stack (apply maximum possible cost reduction, use game.inCheckPlayableState()).
+ * - one for normal mode after spell puts on stack (apply real cost reduction)
+ * Example: TorgaarFamineIncarnate
  *
  * @author maurer.it_at_gmail.com
  */
diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/SourceCostReductionForEachCardInGraveyardEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/SourceCostReductionForEachCardInGraveyardEffect.java
deleted file mode 100644
index ab50288e85..0000000000
--- a/Mage/src/main/java/mage/abilities/effects/common/cost/SourceCostReductionForEachCardInGraveyardEffect.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package mage.abilities.effects.common.cost;
-
-import mage.abilities.Ability;
-import mage.abilities.SpellAbility;
-import mage.constants.CostModificationType;
-import mage.constants.Duration;
-import mage.constants.Outcome;
-import mage.filter.FilterCard;
-import mage.filter.StaticFilters;
-import mage.game.Game;
-import mage.players.Player;
-import mage.util.CardUtil;
-
-/**
- * @author Styxo
- */
-public class SourceCostReductionForEachCardInGraveyardEffect extends CostModificationEffectImpl {
-
-    private FilterCard filter;
-
-    public SourceCostReductionForEachCardInGraveyardEffect() {
-        this(StaticFilters.FILTER_CARD);
-    }
-
-    public SourceCostReductionForEachCardInGraveyardEffect(FilterCard filter) {
-        super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.REDUCE_COST);
-        this.filter = filter;
-        staticText = "{this} costs {1} less to cast for each " + filter.getMessage() + " in your graveyard";
-    }
-
-    private SourceCostReductionForEachCardInGraveyardEffect(SourceCostReductionForEachCardInGraveyardEffect effect) {
-        super(effect);
-        this.filter = effect.filter.copy();
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        Player player = game.getPlayer(source.getControllerId());
-        if (player != null) {
-            int reductionAmount = player.getGraveyard().count(filter, game);
-            CardUtil.reduceCost(abilityToModify, reductionAmount);
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        if ((abilityToModify instanceof SpellAbility) && abilityToModify.getSourceId().equals(source.getSourceId())) {
-            return game.getCard(abilityToModify.getSourceId()) != null;
-        }
-        return false;
-    }
-
-    @Override
-    public SourceCostReductionForEachCardInGraveyardEffect copy() {
-        return new SourceCostReductionForEachCardInGraveyardEffect(this);
-    }
-}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionForEachSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionForEachSourceEffect.java
new file mode 100644
index 0000000000..0719d6aee7
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionForEachSourceEffect.java
@@ -0,0 +1,93 @@
+package mage.abilities.effects.common.cost;
+
+import mage.abilities.Ability;
+import mage.abilities.SpellAbility;
+import mage.abilities.costs.mana.ManaCost;
+import mage.abilities.costs.mana.ManaCosts;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.dynamicvalue.DynamicValue;
+import mage.constants.CostModificationType;
+import mage.constants.Duration;
+import mage.constants.Outcome;
+import mage.game.Game;
+import mage.util.CardUtil;
+
+/**
+ * @author JayDi85
+ */
+public class SpellCostReductionForEachSourceEffect extends CostModificationEffectImpl {
+
+    private final DynamicValue eachAmount;
+    private ManaCosts<ManaCost> reduceManaCosts;
+    private final int reduceGenericMana;
+
+    public SpellCostReductionForEachSourceEffect(int reduceGenericMana, DynamicValue eachAmount) {
+        super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST);
+        this.eachAmount = eachAmount;
+        this.reduceManaCosts = null;
+        this.reduceGenericMana = reduceGenericMana;
+
+        StringBuilder sb = new StringBuilder();
+        sb.append("this spell costs {")
+                .append(this.reduceGenericMana)
+                .append("} less to cast for each ")
+                .append(this.eachAmount.getMessage());
+        this.staticText = sb.toString();
+    }
+
+    public SpellCostReductionForEachSourceEffect(ManaCosts<ManaCost> reduceManaCosts, DynamicValue eachAmount) {
+        super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST);
+        this.eachAmount = eachAmount;
+        this.reduceManaCosts = reduceManaCosts;
+        this.reduceGenericMana = 0;
+
+        StringBuilder sb = new StringBuilder();
+        sb.append("this spell costs ");
+        for (String manaSymbol : reduceManaCosts.getSymbols()) {
+            sb.append(manaSymbol);
+        }
+        sb.append(" less to cast for each ").append(this.eachAmount.getMessage());
+        this.staticText = sb.toString();
+    }
+
+
+    protected SpellCostReductionForEachSourceEffect(final SpellCostReductionForEachSourceEffect effect) {
+        super(effect);
+        this.eachAmount = effect.eachAmount;
+        this.reduceManaCosts = effect.reduceManaCosts;
+        this.reduceGenericMana = effect.reduceGenericMana;
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source, Ability abilityToModify) {
+        int needReduceAmount = eachAmount.calculate(game, source, this);
+        if (needReduceAmount > 0) {
+            if (reduceManaCosts != null) {
+                // color reduce
+                ManaCosts<ManaCost> needReduceMana = new ManaCostsImpl<>();
+                for (int i = 0; i <= needReduceAmount; i++) {
+                    needReduceMana.add(reduceManaCosts.copy());
+                }
+                CardUtil.adjustCost((SpellAbility) abilityToModify, needReduceMana, false);
+            } else {
+                // generic reduce
+                CardUtil.reduceCost(abilityToModify, needReduceAmount * this.reduceGenericMana);
+            }
+        }
+
+        return true;
+    }
+
+    @Override
+    public boolean applies(Ability abilityToModify, Ability source, Game game) {
+        if (abilityToModify.getSourceId().equals(source.getSourceId()) && (abilityToModify instanceof SpellAbility)) {
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public SpellCostReductionForEachSourceEffect copy() {
+        return new SpellCostReductionForEachSourceEffect(this);
+    }
+}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionSourceEffect.java
index a1910b5116..c6bf32504e 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionSourceEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionSourceEffect.java
@@ -37,11 +37,10 @@ public class SpellCostReductionSourceEffect extends CostModificationEffectImpl {
         for (String manaSymbol : manaCostsToReduce.getSymbols()) {
             sb.append(manaSymbol);
         }
-        sb.append(" less");
+        sb.append(" less to cast");
         if (this.condition != null) {
-            sb.append(" to if ").append(this.condition.toString());
+            sb.append(" if ").append(this.condition.toString());
         }
-
         this.staticText = sb.toString();
     }
 
diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionSourceForOpponentsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionSourceForOpponentsEffect.java
deleted file mode 100644
index 6b6509870a..0000000000
--- a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellCostReductionSourceForOpponentsEffect.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package mage.abilities.effects.common.cost;
-
-import mage.abilities.Ability;
-import mage.abilities.SpellAbility;
-import mage.constants.CostModificationType;
-import mage.constants.Duration;
-import mage.constants.Outcome;
-import mage.game.Game;
-import mage.util.CardUtil;
-
-public class SpellCostReductionSourceForOpponentsEffect extends CostModificationEffectImpl {
-
-    public SpellCostReductionSourceForOpponentsEffect() {
-        this("undaunted <i>(This spell costs {1} less to cast for each opponent.)</i>");
-    }
-
-    public SpellCostReductionSourceForOpponentsEffect(String newStaticText) {
-        super(Duration.Custom, Outcome.Benefit, CostModificationType.REDUCE_COST);
-        staticText = newStaticText;
-    }
-
-    public SpellCostReductionSourceForOpponentsEffect(final SpellCostReductionSourceForOpponentsEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        int count = game.getOpponents(source.getControllerId()).size();
-        CardUtil.reduceCost(abilityToModify, count);
-        return true;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        return abilityToModify instanceof SpellAbility && abilityToModify.getSourceId().equals(source.getSourceId());
-    }
-
-    @Override
-    public SpellCostReductionSourceForOpponentsEffect copy() {
-        return new SpellCostReductionSourceForOpponentsEffect(this);
-    }
-}
diff --git a/Mage/src/main/java/mage/abilities/keyword/UndauntedAbility.java b/Mage/src/main/java/mage/abilities/keyword/UndauntedAbility.java
index 52061608d3..72f76d60f0 100644
--- a/Mage/src/main/java/mage/abilities/keyword/UndauntedAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/UndauntedAbility.java
@@ -1,12 +1,8 @@
-/*
- * To change this license header, choose License Headers in Project Properties.
- * To change this template file, choose Tools | Templates
- * and open the template in the editor.
- */
 package mage.abilities.keyword;
 
 import mage.abilities.common.SimpleStaticAbility;
-import mage.abilities.effects.common.cost.SpellCostReductionSourceForOpponentsEffect;
+import mage.abilities.dynamicvalue.common.OpponentsCount;
+import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect;
 import mage.constants.Zone;
 
 /**
@@ -15,7 +11,7 @@ import mage.constants.Zone;
 public class UndauntedAbility extends SimpleStaticAbility {
 
     public UndauntedAbility() {
-        super(Zone.ALL, new SpellCostReductionSourceForOpponentsEffect("undaunted <i>(This spell costs {1} less to cast for each opponent.)</i>"));
+        super(Zone.ALL, new SpellCostReductionForEachSourceEffect(1, OpponentsCount.instance));
         setRuleAtTheTop(true);
     }
 
@@ -28,4 +24,8 @@ public class UndauntedAbility extends SimpleStaticAbility {
         return new UndauntedAbility(this);
     }
 
+    @Override
+    public String getRule() {
+        return "undaunted <i>(This spell costs {1} less to cast for each opponent.)</i>";
+    }
 }
\ No newline at end of file

From 90965802d0b43014203a1d01b4e13f7112a863c9 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Mon, 29 Jun 2020 15:39:58 +0400
Subject: [PATCH 549/586] Cost increasing effects - refactor, removed redundant
 custom effects (related to #6684 and #6698);

---
 .../src/mage/cards/a/AlabasterLeech.java      |  12 +-
 .../src/mage/cards/a/AndraditeLeech.java      |  18 +--
 Mage.Sets/src/mage/cards/a/AuraOfSilence.java |  66 +++------
 Mage.Sets/src/mage/cards/c/Chill.java         |  13 +-
 Mage.Sets/src/mage/cards/d/DampingSphere.java |  17 +--
 Mage.Sets/src/mage/cards/d/Derelor.java       |  16 +-
 .../src/mage/cards/d/DovinHandOfControl.java  |  59 ++------
 Mage.Sets/src/mage/cards/f/FerozsBan.java     |  18 +--
 .../mage/cards/g/GeistFueledScarecrow.java    |  15 +-
 Mage.Sets/src/mage/cards/g/Gloom.java         |  22 +--
 Mage.Sets/src/mage/cards/g/Glowrider.java     |  62 ++------
 .../src/mage/cards/g/GodPharaohsStatue.java   |  50 +------
 .../mage/cards/g/GrandArbiterAugustinIV.java  |  45 +-----
 Mage.Sets/src/mage/cards/h/HighSeas.java      |  15 +-
 Mage.Sets/src/mage/cards/i/IriniSengir.java   |  21 ++-
 Mage.Sets/src/mage/cards/j/JadeLeech.java     |  12 +-
 .../src/mage/cards/l/LodestoneGolem.java      |  62 ++------
 Mage.Sets/src/mage/cards/r/RubyLeech.java     |  12 +-
 Mage.Sets/src/mage/cards/s/SapphireLeech.java |  12 +-
 .../src/mage/cards/s/SphereOfResistance.java  |  15 +-
 Mage.Sets/src/mage/cards/s/Squeeze.java       |  21 ++-
 .../mage/cards/t/ThaliaGuardianOfThraben.java |  59 ++------
 .../src/mage/cards/t/ThornOfAmethyst.java     |  14 +-
 Mage.Sets/src/mage/cards/v/VrynWingmare.java  |  61 ++------
 .../ConditionalCostModificationTest.java      |   6 +-
 .../cost/SpellsCostIncreasementAllEffect.java |  66 ---------
 ...pellsCostIncreasementControllerEffect.java |  90 ------------
 .../cost/SpellsCostIncreasingAllEffect.java   | 139 ++++++++++++++++++
 28 files changed, 363 insertions(+), 655 deletions(-)
 delete mode 100644 Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostIncreasementAllEffect.java
 delete mode 100644 Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostIncreasementControllerEffect.java
 create mode 100644 Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostIncreasingAllEffect.java

diff --git a/Mage.Sets/src/mage/cards/a/AlabasterLeech.java b/Mage.Sets/src/mage/cards/a/AlabasterLeech.java
index 63e2ad4d6a..cfdf0d916e 100644
--- a/Mage.Sets/src/mage/cards/a/AlabasterLeech.java
+++ b/Mage.Sets/src/mage/cards/a/AlabasterLeech.java
@@ -1,22 +1,22 @@
-
 package mage.cards.a;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.ObjectColor;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
-import mage.abilities.effects.common.cost.SpellsCostIncreasementControllerEffect;
+import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.SubType;
+import mage.constants.TargetController;
 import mage.constants.Zone;
 import mage.filter.FilterCard;
 import mage.filter.predicate.mageobject.ColorPredicate;
 
+import java.util.UUID;
+
 /**
- *
  * @author LoneFox
  */
 public final class AlabasterLeech extends CardImpl {
@@ -28,14 +28,14 @@ public final class AlabasterLeech extends CardImpl {
     }
 
     public AlabasterLeech(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{W}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}");
         this.subtype.add(SubType.LEECH);
         this.power = new MageInt(1);
         this.toughness = new MageInt(3);
 
         // White spells you cast cost {W} more to cast.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
-            new SpellsCostIncreasementControllerEffect(filter, new ManaCostsImpl("{W}"))));
+                new SpellsCostIncreasingAllEffect(new ManaCostsImpl("{W}"), filter, TargetController.YOU)));
     }
 
     public AlabasterLeech(final AlabasterLeech card) {
diff --git a/Mage.Sets/src/mage/cards/a/AndraditeLeech.java b/Mage.Sets/src/mage/cards/a/AndraditeLeech.java
index 0102b19379..3d91d654f5 100644
--- a/Mage.Sets/src/mage/cards/a/AndraditeLeech.java
+++ b/Mage.Sets/src/mage/cards/a/AndraditeLeech.java
@@ -1,25 +1,21 @@
-
 package mage.cards.a;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.ObjectColor;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.common.continuous.BoostSourceEffect;
-import mage.abilities.effects.common.cost.SpellsCostIncreasementControllerEffect;
+import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.CardType;
-import mage.constants.Duration;
-import mage.constants.SubType;
-import mage.constants.Zone;
+import mage.constants.*;
 import mage.filter.FilterCard;
 import mage.filter.predicate.mageobject.ColorPredicate;
 
+import java.util.UUID;
+
 /**
- *
  * @author LoneFox
  */
 public final class AndraditeLeech extends CardImpl {
@@ -31,17 +27,17 @@ public final class AndraditeLeech extends CardImpl {
     }
 
     public AndraditeLeech(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{B}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}");
         this.subtype.add(SubType.LEECH);
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
 
         // Black spells you cast cost {B} more to cast.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
-            new SpellsCostIncreasementControllerEffect(filter, new ManaCostsImpl("{B}"))));
+                new SpellsCostIncreasingAllEffect(new ManaCostsImpl("{B}"), filter, TargetController.YOU)));
         // {B}: Andradite Leech gets +1/+1 until end of turn.
         this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD,
-            new BoostSourceEffect(1, 1, Duration.EndOfTurn), new ManaCostsImpl("{B}")));
+                new BoostSourceEffect(1, 1, Duration.EndOfTurn), new ManaCostsImpl("{B}")));
     }
 
     public AndraditeLeech(final AndraditeLeech card) {
diff --git a/Mage.Sets/src/mage/cards/a/AuraOfSilence.java b/Mage.Sets/src/mage/cards/a/AuraOfSilence.java
index 78336fd491..23ae3bcc87 100644
--- a/Mage.Sets/src/mage/cards/a/AuraOfSilence.java
+++ b/Mage.Sets/src/mage/cards/a/AuraOfSilence.java
@@ -1,35 +1,42 @@
-
 package mage.cards.a;
 
 import mage.abilities.Ability;
-import mage.abilities.SpellAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.costs.common.SacrificeSourceCost;
 import mage.abilities.effects.common.DestroyTargetEffect;
-import mage.abilities.effects.common.cost.CostModificationEffectImpl;
-import mage.cards.Card;
+import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.*;
+import mage.constants.CardType;
+import mage.constants.TargetController;
+import mage.constants.Zone;
+import mage.filter.FilterCard;
 import mage.filter.StaticFilters;
-import mage.game.Game;
+import mage.filter.predicate.Predicates;
 import mage.target.TargetPermanent;
-import mage.util.CardUtil;
 
 import java.util.UUID;
 
 /**
- *
  * @author emerald000
  */
 public final class AuraOfSilence extends CardImpl {
+
+    private static final FilterCard filter = new FilterCard("Artifact and enchantment spells");
+
+    static {
+        filter.add(Predicates.or(
+                CardType.ARTIFACT.getPredicate(),
+                CardType.ENCHANTMENT.getPredicate()));
+    }
+
     public AuraOfSilence(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{W}{W}");
+        super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}{W}");
 
         // Artifact and enchantment spells your opponents cast cost {2} more to cast.
-        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new AuraOfSilenceCostModificationEffect()));
-        
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostIncreasingAllEffect(2, filter, TargetController.OPPONENT)));
+
         // Sacrifice Aura of Silence: Destroy target artifact or enchantment.
         Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new SacrificeSourceCost());
         ability.addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT));
@@ -44,41 +51,4 @@ public final class AuraOfSilence extends CardImpl {
     public AuraOfSilence copy() {
         return new AuraOfSilence(this);
     }
-}
-
-class AuraOfSilenceCostModificationEffect extends CostModificationEffectImpl {
-
-    AuraOfSilenceCostModificationEffect() {
-        super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST);
-        staticText = "Artifact and enchantment spells your opponents cast cost {2} more to cast";
-    }
-
-    AuraOfSilenceCostModificationEffect(AuraOfSilenceCostModificationEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        SpellAbility spellAbility = (SpellAbility) abilityToModify;
-        CardUtil.adjustCost(spellAbility, -2);
-        return true;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        if (abilityToModify instanceof SpellAbility) {
-            if (game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) {
-                Card card = game.getCard(abilityToModify.getSourceId());
-                if (card != null && (card.isArtifact() || card.isEnchantment())) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public AuraOfSilenceCostModificationEffect copy() {
-        return new AuraOfSilenceCostModificationEffect(this);
-    }
 }
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/c/Chill.java b/Mage.Sets/src/mage/cards/c/Chill.java
index 557a0a2a47..083999cc7c 100644
--- a/Mage.Sets/src/mage/cards/c/Chill.java
+++ b/Mage.Sets/src/mage/cards/c/Chill.java
@@ -1,33 +1,34 @@
-
 package mage.cards.c;
 
-import java.util.UUID;
 import mage.ObjectColor;
 import mage.abilities.common.SimpleStaticAbility;
-import mage.abilities.effects.common.cost.SpellsCostIncreasementAllEffect;
+import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
+import mage.constants.TargetController;
 import mage.constants.Zone;
 import mage.filter.FilterCard;
 import mage.filter.predicate.mageobject.ColorPredicate;
 
+import java.util.UUID;
+
 /**
- *
  * @author Quercitron
  */
 public final class Chill extends CardImpl {
 
     private static final FilterCard filter = new FilterCard("Red spells");
+
     static {
         filter.add(new ColorPredicate(ObjectColor.RED));
     }
 
     public Chill(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{U}");
+        super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}");
 
         // Red spells cost {2} more to cast.
-        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostIncreasementAllEffect(filter, 2)));
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostIncreasingAllEffect(2, filter, TargetController.ANY)));
     }
 
     public Chill(final Chill card) {
diff --git a/Mage.Sets/src/mage/cards/d/DampingSphere.java b/Mage.Sets/src/mage/cards/d/DampingSphere.java
index b4780d80d7..c2177cc690 100644
--- a/Mage.Sets/src/mage/cards/d/DampingSphere.java
+++ b/Mage.Sets/src/mage/cards/d/DampingSphere.java
@@ -1,19 +1,15 @@
-
 package mage.cards.d;
 
-import java.util.UUID;
 import mage.MageObject;
 import mage.Mana;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.ReplacementEffectImpl;
-import mage.abilities.effects.common.cost.SpellsCostIncreasementAllEffect;
+import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.CardType;
-import mage.constants.Duration;
-import mage.constants.Outcome;
-import mage.constants.Zone;
+import mage.constants.*;
+import mage.filter.FilterCard;
 import mage.game.Game;
 import mage.game.events.GameEvent;
 import mage.game.events.GameEvent.EventType;
@@ -21,8 +17,9 @@ import mage.game.events.ManaEvent;
 import mage.util.CardUtil;
 import mage.watchers.common.CastSpellLastTurnWatcher;
 
+import java.util.UUID;
+
 /**
- *
  * @author L_J
  */
 public final class DampingSphere extends CardImpl {
@@ -90,10 +87,10 @@ class DampingSphereReplacementEffect extends ReplacementEffectImpl {
     }
 }
 
-class DampingSphereIncreasementAllEffect extends SpellsCostIncreasementAllEffect {
+class DampingSphereIncreasementAllEffect extends SpellsCostIncreasingAllEffect {
 
     DampingSphereIncreasementAllEffect() {
-        super(0);
+        super(1, new FilterCard(), TargetController.ANY);
         this.staticText = "Each spell a player casts costs {1} more to cast for each other spell that player has cast this turn";
     }
 
diff --git a/Mage.Sets/src/mage/cards/d/Derelor.java b/Mage.Sets/src/mage/cards/d/Derelor.java
index 6d1ddddc00..e6984a51e6 100644
--- a/Mage.Sets/src/mage/cards/d/Derelor.java
+++ b/Mage.Sets/src/mage/cards/d/Derelor.java
@@ -1,41 +1,41 @@
-
 package mage.cards.d;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.ObjectColor;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
-import mage.abilities.effects.common.cost.SpellsCostIncreasementControllerEffect;
+import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.SubType;
+import mage.constants.TargetController;
 import mage.constants.Zone;
 import mage.filter.FilterCard;
 import mage.filter.predicate.mageobject.ColorPredicate;
 
+import java.util.UUID;
+
 /**
- *
  * @author Quercitron
  */
 public final class Derelor extends CardImpl {
 
     private static final FilterCard filter = new FilterCard("Black spells");
-    
+
     static {
         filter.add(new ColorPredicate(ObjectColor.BLACK));
     }
-    
+
     public Derelor(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}");
         this.subtype.add(SubType.THRULL);
 
         this.power = new MageInt(4);
         this.toughness = new MageInt(4);
 
         // Black spells you cast cost {B} more to cast.
-        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostIncreasementControllerEffect(filter, new ManaCostsImpl<>("{B}"))));
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostIncreasingAllEffect(new ManaCostsImpl<>("{B}"), filter, TargetController.YOU)));
     }
 
     public Derelor(final Derelor card) {
diff --git a/Mage.Sets/src/mage/cards/d/DovinHandOfControl.java b/Mage.Sets/src/mage/cards/d/DovinHandOfControl.java
index 34eaf023bc..dc14c627a5 100644
--- a/Mage.Sets/src/mage/cards/d/DovinHandOfControl.java
+++ b/Mage.Sets/src/mage/cards/d/DovinHandOfControl.java
@@ -2,30 +2,35 @@ package mage.cards.d;
 
 import mage.abilities.Ability;
 import mage.abilities.LoyaltyAbility;
-import mage.abilities.SpellAbility;
 import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.common.PreventDamageByTargetEffect;
 import mage.abilities.effects.common.PreventDamageToTargetEffect;
-import mage.abilities.effects.common.cost.CostModificationEffectImpl;
-import mage.cards.Card;
+import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.*;
+import mage.filter.FilterCard;
 import mage.filter.StaticFilters;
-import mage.game.Game;
+import mage.filter.predicate.Predicates;
 import mage.target.TargetPermanent;
-import mage.util.CardUtil;
 
 import java.util.UUID;
-import mage.abilities.mana.ManaAbility;
-import mage.game.stack.Spell;
 
 /**
  * @author TheElk801
  */
 public final class DovinHandOfControl extends CardImpl {
 
+    private static final FilterCard filter = new FilterCard("Artifact, instant, and sorcery spells");
+
+    static {
+        filter.add(Predicates.or(
+                CardType.ARTIFACT.getPredicate(),
+                CardType.INSTANT.getPredicate(),
+                CardType.SORCERY.getPredicate()));
+    }
+
     public DovinHandOfControl(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{2}{W/U}");
 
@@ -34,7 +39,7 @@ public final class DovinHandOfControl extends CardImpl {
         this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5));
 
         // Artifact, instant, and sorcery spells your opponents cast cost {1} more to cast.
-        this.addAbility(new SimpleStaticAbility(new DovinHandOfControlEffect()));
+        this.addAbility(new SimpleStaticAbility(new SpellsCostIncreasingAllEffect(1, filter, TargetController.OPPONENT)));
 
         // -1: Until your next turn, prevent all damage that would be dealt to and dealt by target permanent an opponent controls.
         Ability ability = new LoyaltyAbility(new PreventDamageToTargetEffect(
@@ -55,40 +60,4 @@ public final class DovinHandOfControl extends CardImpl {
     public DovinHandOfControl copy() {
         return new DovinHandOfControl(this);
     }
-}
-
-class DovinHandOfControlEffect extends CostModificationEffectImpl {
-
-    DovinHandOfControlEffect() {
-        super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST);
-        staticText = "Artifact, instant, and sorcery spells your opponents cast cost {1} more to cast";
-    }
-
-    private DovinHandOfControlEffect(DovinHandOfControlEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        SpellAbility spellAbility = (SpellAbility) abilityToModify;
-        CardUtil.adjustCost(spellAbility, -1);
-        return true;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        Card card = game.getCard(abilityToModify.getSourceId());
-        if (!(abilityToModify instanceof SpellAbility)) {
-            return false;
-        }
-        return card != null
-                && (card.isInstantOrSorcery()
-                || card.isArtifact())
-                && game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId());
-    }
-
-    @Override
-    public DovinHandOfControlEffect copy() {
-        return new DovinHandOfControlEffect(this);
-    }
-}
+}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/f/FerozsBan.java b/Mage.Sets/src/mage/cards/f/FerozsBan.java
index bda78ea60a..52a6ed4995 100644
--- a/Mage.Sets/src/mage/cards/f/FerozsBan.java
+++ b/Mage.Sets/src/mage/cards/f/FerozsBan.java
@@ -1,29 +1,29 @@
 package mage.cards.f;
 
-import java.util.UUID;
 import mage.abilities.common.SimpleStaticAbility;
-import mage.abilities.effects.Effect;
-import mage.abilities.effects.common.cost.SpellsCostIncreasementAllEffect;
+import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
+import mage.constants.TargetController;
 import mage.constants.Zone;
-import mage.filter.StaticFilters;
+import mage.filter.FilterCard;
+import mage.filter.common.FilterCreatureCard;
+
+import java.util.UUID;
 
 /**
- *
  * @author LoneFox
- *
  */
 public final class FerozsBan extends CardImpl {
 
+    private static final FilterCard filter = new FilterCreatureCard("Creature spells");
+
     public FerozsBan(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{6}");
 
         // Creature spells cost {2} more to cast.
-        Effect effect = new SpellsCostIncreasementAllEffect(StaticFilters.FILTER_CARD_CREATURE, 2);
-        effect.setText("Creature spells cost {2} more to cast.");
-        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect));
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostIncreasingAllEffect(2, filter, TargetController.ANY)));
     }
 
     public FerozsBan(final FerozsBan card) {
diff --git a/Mage.Sets/src/mage/cards/g/GeistFueledScarecrow.java b/Mage.Sets/src/mage/cards/g/GeistFueledScarecrow.java
index 2393e2b07b..b64c673afb 100644
--- a/Mage.Sets/src/mage/cards/g/GeistFueledScarecrow.java
+++ b/Mage.Sets/src/mage/cards/g/GeistFueledScarecrow.java
@@ -1,24 +1,23 @@
-
 package mage.cards.g;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.common.SimpleStaticAbility;
-import mage.abilities.costs.mana.ManaCostsImpl;
-import mage.abilities.effects.common.cost.SpellsCostIncreasementControllerEffect;
+import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.SubType;
+import mage.constants.TargetController;
 import mage.constants.Zone;
 import mage.filter.FilterCard;
 
+import java.util.UUID;
+
 /**
- *
  * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com)
  */
 public final class GeistFueledScarecrow extends CardImpl {
-    
+
     private static final FilterCard filter = new FilterCard("Creature spells");
 
     static {
@@ -26,14 +25,14 @@ public final class GeistFueledScarecrow extends CardImpl {
     }
 
     public GeistFueledScarecrow(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{4}");
+        super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{4}");
         this.subtype.add(SubType.SCARECROW);
         this.power = new MageInt(4);
         this.toughness = new MageInt(4);
 
         // Creature spells you cast cost {1} more to cast.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
-            new SpellsCostIncreasementControllerEffect(filter, new ManaCostsImpl("{1}"))));
+                new SpellsCostIncreasingAllEffect(1, filter, TargetController.YOU)));
     }
 
     public GeistFueledScarecrow(final GeistFueledScarecrow card) {
diff --git a/Mage.Sets/src/mage/cards/g/Gloom.java b/Mage.Sets/src/mage/cards/g/Gloom.java
index 43a47d98b6..8df36787ea 100644
--- a/Mage.Sets/src/mage/cards/g/Gloom.java
+++ b/Mage.Sets/src/mage/cards/g/Gloom.java
@@ -1,13 +1,11 @@
-
 package mage.cards.g;
 
-import java.util.UUID;
 import mage.MageObject;
 import mage.ObjectColor;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.common.cost.CostModificationEffectImpl;
-import mage.abilities.effects.common.cost.SpellsCostIncreasementAllEffect;
+import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.*;
@@ -16,23 +14,25 @@ import mage.filter.predicate.mageobject.ColorPredicate;
 import mage.game.Game;
 import mage.util.CardUtil;
 
+import java.util.UUID;
+
 /**
- *
  * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com)
  */
 public final class Gloom extends CardImpl {
-    
+
     private static final FilterCard filter = new FilterCard("White spells");
+
     static {
         filter.add(new ColorPredicate(ObjectColor.WHITE));
     }
 
     public Gloom(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{B}");
+        super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}");
 
         // White spells cost {3} more to cast.
-        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostIncreasementAllEffect(filter, 3)));
-        
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostIncreasingAllEffect(3, filter, TargetController.ANY)));
+
         // Activated abilities of white enchantments cost {3} more to activate.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GloomCostIncreaseEffect()));
     }
@@ -63,12 +63,12 @@ class GloomCostIncreaseEffect extends CostModificationEffectImpl {
         CardUtil.increaseCost(abilityToModify, 3);
         return true;
     }
-    
+
     @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {        
+    public boolean applies(Ability abilityToModify, Ability source, Game game) {
         boolean isWhiteEnchantment = false;
         boolean isActivated = abilityToModify.getAbilityType() == AbilityType.ACTIVATED;
-        if (isActivated) {            
+        if (isActivated) {
             MageObject permanent = game.getPermanent(abilityToModify.getSourceId());
             if (permanent != null) {
                 isWhiteEnchantment = permanent.isEnchantment() && permanent.getColor(game).isWhite();
diff --git a/Mage.Sets/src/mage/cards/g/Glowrider.java b/Mage.Sets/src/mage/cards/g/Glowrider.java
index 3ff7663897..7948e732c9 100644
--- a/Mage.Sets/src/mage/cards/g/Glowrider.java
+++ b/Mage.Sets/src/mage/cards/g/Glowrider.java
@@ -1,25 +1,30 @@
-
 package mage.cards.g;
 
-import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.Ability;
-import mage.abilities.SpellAbility;
 import mage.abilities.common.SimpleStaticAbility;
-import mage.abilities.effects.common.cost.CostModificationEffectImpl;
-import mage.cards.Card;
+import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.*;
-import mage.game.Game;
-import mage.util.CardUtil;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.TargetController;
+import mage.constants.Zone;
+import mage.filter.FilterCard;
+import mage.filter.predicate.Predicates;
+
+import java.util.UUID;
 
 /**
- *
  * @author fireshoes
  */
 public final class Glowrider extends CardImpl {
 
+    private static final FilterCard filter = new FilterCard("Noncreature spells");
+
+    static {
+        filter.add(Predicates.not(CardType.CREATURE.getPredicate()));
+    }
+
     public Glowrider(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}");
         this.subtype.add(SubType.HUMAN);
@@ -28,7 +33,7 @@ public final class Glowrider extends CardImpl {
         this.toughness = new MageInt(1);
 
         // Noncreature spells cost {1} more to cast.
-        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GlowriderCostReductionEffect()));
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostIncreasingAllEffect(1, filter, TargetController.ANY)));
     }
 
     public Glowrider(final Glowrider card) {
@@ -40,38 +45,3 @@ public final class Glowrider extends CardImpl {
         return new Glowrider(this);
     }
 }
-
-class GlowriderCostReductionEffect extends CostModificationEffectImpl {
-
-    GlowriderCostReductionEffect() {
-        super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST);
-        staticText = "Noncreature spells cost {1} more to cast";
-    }
-
-    GlowriderCostReductionEffect(GlowriderCostReductionEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        CardUtil.increaseCost(abilityToModify, 1);
-        return true;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        if (abilityToModify instanceof SpellAbility) {
-            Card card = game.getCard(abilityToModify.getSourceId());
-            if (card != null && !card.isCreature()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public GlowriderCostReductionEffect copy() {
-        return new GlowriderCostReductionEffect(this);
-    }
-
-}
diff --git a/Mage.Sets/src/mage/cards/g/GodPharaohsStatue.java b/Mage.Sets/src/mage/cards/g/GodPharaohsStatue.java
index 2e2c67444d..1fbd05ac20 100644
--- a/Mage.Sets/src/mage/cards/g/GodPharaohsStatue.java
+++ b/Mage.Sets/src/mage/cards/g/GodPharaohsStatue.java
@@ -1,16 +1,15 @@
 package mage.cards.g;
 
-import mage.abilities.Ability;
-import mage.abilities.SpellAbility;
 import mage.abilities.common.BeginningOfEndStepTriggeredAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.common.LoseLifeOpponentsEffect;
-import mage.abilities.effects.common.cost.CostModificationEffectImpl;
+import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.*;
-import mage.game.Game;
-import mage.util.CardUtil;
+import mage.constants.CardType;
+import mage.constants.SuperType;
+import mage.constants.TargetController;
+import mage.filter.FilterCard;
 
 import java.util.UUID;
 
@@ -25,7 +24,7 @@ public final class GodPharaohsStatue extends CardImpl {
         this.addSuperType(SuperType.LEGENDARY);
 
         // Spells your opponents cast cost {2} more to cast.
-        this.addAbility(new SimpleStaticAbility(new GodPharaohsStatueEffect()));
+        this.addAbility(new SimpleStaticAbility(new SpellsCostIncreasingAllEffect(2, new FilterCard("Spells"), TargetController.OPPONENT)));
 
         // At the beginning of your end step, each opponent loses 1 life.
         this.addAbility(new BeginningOfEndStepTriggeredAbility(
@@ -42,40 +41,3 @@ public final class GodPharaohsStatue extends CardImpl {
         return new GodPharaohsStatue(this);
     }
 }
-
-class GodPharaohsStatueEffect extends CostModificationEffectImpl {
-
-    private static final String effectText = "Spells your opponents cast cost {2} more to cast";
-
-    GodPharaohsStatueEffect() {
-        super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST);
-        staticText = effectText;
-    }
-
-    private GodPharaohsStatueEffect(GodPharaohsStatueEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        SpellAbility spellAbility = (SpellAbility) abilityToModify;
-        CardUtil.adjustCost(spellAbility, -2);
-        return true;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        if (abilityToModify instanceof SpellAbility) {
-            if (game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public GodPharaohsStatueEffect copy() {
-        return new GodPharaohsStatueEffect(this);
-    }
-
-}
diff --git a/Mage.Sets/src/mage/cards/g/GrandArbiterAugustinIV.java b/Mage.Sets/src/mage/cards/g/GrandArbiterAugustinIV.java
index 9c247eb3a7..518db6f2ab 100644
--- a/Mage.Sets/src/mage/cards/g/GrandArbiterAugustinIV.java
+++ b/Mage.Sets/src/mage/cards/g/GrandArbiterAugustinIV.java
@@ -2,18 +2,14 @@ package mage.cards.g;
 
 import mage.MageInt;
 import mage.ObjectColor;
-import mage.abilities.Ability;
-import mage.abilities.SpellAbility;
 import mage.abilities.common.SimpleStaticAbility;
-import mage.abilities.effects.common.cost.CostModificationEffectImpl;
+import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect;
 import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.*;
 import mage.filter.FilterCard;
 import mage.filter.predicate.mageobject.ColorPredicate;
-import mage.game.Game;
-import mage.util.CardUtil;
 
 import java.util.UUID;
 
@@ -46,7 +42,7 @@ public final class GrandArbiterAugustinIV extends CardImpl {
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filterBlue, 1)));
 
         // Spells your opponents cast cost {1} more to cast.
-        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GrandArbiterAugustinIVCostIncreaseEffect()));
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostIncreasingAllEffect(1, new FilterCard("Spells"), TargetController.OPPONENT)));
     }
 
     public GrandArbiterAugustinIV(final GrandArbiterAugustinIV card) {
@@ -58,40 +54,3 @@ public final class GrandArbiterAugustinIV extends CardImpl {
         return new GrandArbiterAugustinIV(this);
     }
 }
-
-class GrandArbiterAugustinIVCostIncreaseEffect extends CostModificationEffectImpl {
-
-    private static final String effectText = "Spells your opponents cast cost {1} more to cast";
-
-    GrandArbiterAugustinIVCostIncreaseEffect() {
-        super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST);
-        staticText = effectText;
-    }
-
-    GrandArbiterAugustinIVCostIncreaseEffect(GrandArbiterAugustinIVCostIncreaseEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        SpellAbility spellAbility = (SpellAbility) abilityToModify;
-        CardUtil.adjustCost(spellAbility, -1);
-        return true;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        if (abilityToModify instanceof SpellAbility) {
-            if (game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public GrandArbiterAugustinIVCostIncreaseEffect copy() {
-        return new GrandArbiterAugustinIVCostIncreaseEffect(this);
-    }
-
-}
diff --git a/Mage.Sets/src/mage/cards/h/HighSeas.java b/Mage.Sets/src/mage/cards/h/HighSeas.java
index 412c56ce64..652a5c6f17 100644
--- a/Mage.Sets/src/mage/cards/h/HighSeas.java
+++ b/Mage.Sets/src/mage/cards/h/HighSeas.java
@@ -1,35 +1,36 @@
-
 package mage.cards.h;
 
-import java.util.UUID;
 import mage.ObjectColor;
 import mage.abilities.common.SimpleStaticAbility;
-import mage.abilities.effects.common.cost.SpellsCostIncreasementAllEffect;
+import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
+import mage.constants.TargetController;
 import mage.constants.Zone;
 import mage.filter.common.FilterCreatureCard;
 import mage.filter.predicate.Predicates;
 import mage.filter.predicate.mageobject.ColorPredicate;
 
+import java.util.UUID;
+
 /**
- *
  * @author fireshoes
  */
 public final class HighSeas extends CardImpl {
-    
+
     private static final FilterCreatureCard filter = new FilterCreatureCard("Red creature spells and green creature spells");
+
     static {
         filter.add(Predicates.or(new ColorPredicate(ObjectColor.RED),
                 (new ColorPredicate(ObjectColor.GREEN))));
     }
 
     public HighSeas(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{U}");
+        super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}");
 
         // Red creature spells and green creature spells cost {1} more to cast.
-        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostIncreasementAllEffect(filter, 1)));
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostIncreasingAllEffect(1, filter, TargetController.ANY)));
     }
 
     public HighSeas(final HighSeas card) {
diff --git a/Mage.Sets/src/mage/cards/i/IriniSengir.java b/Mage.Sets/src/mage/cards/i/IriniSengir.java
index 9ba91444fd..8d485f939f 100644
--- a/Mage.Sets/src/mage/cards/i/IriniSengir.java
+++ b/Mage.Sets/src/mage/cards/i/IriniSengir.java
@@ -1,35 +1,32 @@
-
 package mage.cards.i;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.ObjectColor;
 import mage.abilities.common.SimpleStaticAbility;
-import mage.abilities.effects.common.cost.SpellsCostIncreasementAllEffect;
+import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.CardType;
-import mage.constants.SubType;
-import mage.constants.SuperType;
-import mage.constants.Zone;
+import mage.constants.*;
 import mage.filter.common.FilterEnchantmentCard;
 import mage.filter.predicate.Predicates;
 import mage.filter.predicate.mageobject.ColorPredicate;
 
+import java.util.UUID;
+
 /**
- *
  * @author fireshoes
  */
 public final class IriniSengir extends CardImpl {
-    
+
     private static final FilterEnchantmentCard filter = new FilterEnchantmentCard("Green enchantment spells and white enchantment spells");
+
     static {
         filter.add(Predicates.or(new ColorPredicate(ObjectColor.GREEN),
                 (new ColorPredicate(ObjectColor.WHITE))));
     }
 
     public IriniSengir(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{B}{B}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{B}");
         addSuperType(SuperType.LEGENDARY);
         this.subtype.add(SubType.VAMPIRE);
         this.subtype.add(SubType.DWARF);
@@ -37,7 +34,9 @@ public final class IriniSengir extends CardImpl {
         this.toughness = new MageInt(2);
 
         // Green enchantment spells and white enchantment spells cost {2} more to cast.
-        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostIncreasementAllEffect(filter, 2)));
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
+                new SpellsCostIncreasingAllEffect(2, filter, TargetController.ANY))
+        );
     }
 
     public IriniSengir(final IriniSengir card) {
diff --git a/Mage.Sets/src/mage/cards/j/JadeLeech.java b/Mage.Sets/src/mage/cards/j/JadeLeech.java
index 80bfff6d4c..d5a8a6dab3 100644
--- a/Mage.Sets/src/mage/cards/j/JadeLeech.java
+++ b/Mage.Sets/src/mage/cards/j/JadeLeech.java
@@ -1,22 +1,22 @@
-
 package mage.cards.j;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.ObjectColor;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
-import mage.abilities.effects.common.cost.SpellsCostIncreasementControllerEffect;
+import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.SubType;
+import mage.constants.TargetController;
 import mage.constants.Zone;
 import mage.filter.FilterCard;
 import mage.filter.predicate.mageobject.ColorPredicate;
 
+import java.util.UUID;
+
 /**
- *
  * @author LoneFox
  */
 public final class JadeLeech extends CardImpl {
@@ -28,14 +28,14 @@ public final class JadeLeech extends CardImpl {
     }
 
     public JadeLeech(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{G}{G}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{G}");
         this.subtype.add(SubType.LEECH);
         this.power = new MageInt(5);
         this.toughness = new MageInt(5);
 
         // Green spells you cast cost {G} more to cast.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
-            new SpellsCostIncreasementControllerEffect(filter, new ManaCostsImpl("{G}"))));
+                new SpellsCostIncreasingAllEffect(new ManaCostsImpl("{G}"), filter, TargetController.YOU)));
     }
 
     public JadeLeech(final JadeLeech card) {
diff --git a/Mage.Sets/src/mage/cards/l/LodestoneGolem.java b/Mage.Sets/src/mage/cards/l/LodestoneGolem.java
index 3121068d07..0a75868ada 100644
--- a/Mage.Sets/src/mage/cards/l/LodestoneGolem.java
+++ b/Mage.Sets/src/mage/cards/l/LodestoneGolem.java
@@ -1,30 +1,30 @@
-
 package mage.cards.l;
 
-import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.Ability;
-import mage.abilities.SpellAbility;
 import mage.abilities.common.SimpleStaticAbility;
-import mage.abilities.costs.mana.GenericManaCost;
-import mage.abilities.effects.common.cost.CostModificationEffectImpl;
-import mage.cards.Card;
+import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.SubType;
-import mage.constants.CostModificationType;
-import mage.constants.Duration;
-import mage.constants.Outcome;
+import mage.constants.TargetController;
 import mage.constants.Zone;
-import mage.game.Game;
+import mage.filter.FilterCard;
+import mage.filter.predicate.Predicates;
+
+import java.util.UUID;
 
 /**
- *
  * @author jeffwadsworth
  */
 public final class LodestoneGolem extends CardImpl {
 
+    private static final FilterCard filter = new FilterCard("Nonartifact spells");
+
+    static {
+        filter.add(Predicates.not(CardType.ARTIFACT.getPredicate()));
+    }
+
     public LodestoneGolem(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{4}");
         this.subtype.add(SubType.GOLEM);
@@ -33,7 +33,7 @@ public final class LodestoneGolem extends CardImpl {
         this.toughness = new MageInt(3);
 
         // Nonartifact spells cost {1} more to cast.
-        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new LodestoneGolemCostReductionEffect()));
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostIncreasingAllEffect(1, filter, TargetController.ANY)));
     }
 
     public LodestoneGolem(final LodestoneGolem card) {
@@ -45,39 +45,3 @@ public final class LodestoneGolem extends CardImpl {
         return new LodestoneGolem(this);
     }
 }
-
-class LodestoneGolemCostReductionEffect extends CostModificationEffectImpl {
-
-    LodestoneGolemCostReductionEffect() {
-        super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST);
-        staticText = "Nonartifact spells cost {1} more to cast";
-    }
-
-    LodestoneGolemCostReductionEffect(LodestoneGolemCostReductionEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        SpellAbility spellAbility = (SpellAbility) abilityToModify;
-        spellAbility.getManaCostsToPay().add(new GenericManaCost(1));
-        return true;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        if (abilityToModify instanceof SpellAbility) {
-            Card card = game.getCard(abilityToModify.getSourceId());
-            if (card != null && !card.isArtifact()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public LodestoneGolemCostReductionEffect copy() {
-        return new LodestoneGolemCostReductionEffect(this);
-    }
-
-}
diff --git a/Mage.Sets/src/mage/cards/r/RubyLeech.java b/Mage.Sets/src/mage/cards/r/RubyLeech.java
index 13638d55da..ba11fe901b 100644
--- a/Mage.Sets/src/mage/cards/r/RubyLeech.java
+++ b/Mage.Sets/src/mage/cards/r/RubyLeech.java
@@ -1,23 +1,23 @@
-
 package mage.cards.r;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.ObjectColor;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
-import mage.abilities.effects.common.cost.SpellsCostIncreasementControllerEffect;
+import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect;
 import mage.abilities.keyword.FirstStrikeAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.SubType;
+import mage.constants.TargetController;
 import mage.constants.Zone;
 import mage.filter.FilterCard;
 import mage.filter.predicate.mageobject.ColorPredicate;
 
+import java.util.UUID;
+
 /**
- *
  * @author LoneFox
  */
 public final class RubyLeech extends CardImpl {
@@ -29,7 +29,7 @@ public final class RubyLeech extends CardImpl {
     }
 
     public RubyLeech(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{R}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}");
         this.subtype.add(SubType.LEECH);
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
@@ -38,7 +38,7 @@ public final class RubyLeech extends CardImpl {
         this.addAbility(FirstStrikeAbility.getInstance());
         // Red spells you cast cost {R} more to cast.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
-            new SpellsCostIncreasementControllerEffect(filter, new ManaCostsImpl("{R}"))));
+                new SpellsCostIncreasingAllEffect(new ManaCostsImpl("{R}"), filter, TargetController.YOU)));
     }
 
     public RubyLeech(final RubyLeech card) {
diff --git a/Mage.Sets/src/mage/cards/s/SapphireLeech.java b/Mage.Sets/src/mage/cards/s/SapphireLeech.java
index 8b7e1386d7..31d8772e27 100644
--- a/Mage.Sets/src/mage/cards/s/SapphireLeech.java
+++ b/Mage.Sets/src/mage/cards/s/SapphireLeech.java
@@ -1,23 +1,23 @@
-
 package mage.cards.s;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.ObjectColor;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
-import mage.abilities.effects.common.cost.SpellsCostIncreasementControllerEffect;
+import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.SubType;
+import mage.constants.TargetController;
 import mage.constants.Zone;
 import mage.filter.FilterCard;
 import mage.filter.predicate.mageobject.ColorPredicate;
 
+import java.util.UUID;
+
 /**
- *
  * @author LoneFox
  */
 public final class SapphireLeech extends CardImpl {
@@ -29,7 +29,7 @@ public final class SapphireLeech extends CardImpl {
     }
 
     public SapphireLeech(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}");
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}");
         this.subtype.add(SubType.LEECH);
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
@@ -38,7 +38,7 @@ public final class SapphireLeech extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
         // Blue spells you cast cost {U} more to cast.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
-            new SpellsCostIncreasementControllerEffect(filter, new ManaCostsImpl("{U}"))));
+                new SpellsCostIncreasingAllEffect(new ManaCostsImpl("{U}"), filter, TargetController.YOU)));
     }
 
     public SapphireLeech(final SapphireLeech card) {
diff --git a/Mage.Sets/src/mage/cards/s/SphereOfResistance.java b/Mage.Sets/src/mage/cards/s/SphereOfResistance.java
index 0e5c8303fe..c1326f4da8 100644
--- a/Mage.Sets/src/mage/cards/s/SphereOfResistance.java
+++ b/Mage.Sets/src/mage/cards/s/SphereOfResistance.java
@@ -1,25 +1,28 @@
-
 package mage.cards.s;
 
-import java.util.UUID;
 import mage.abilities.common.SimpleStaticAbility;
-import mage.abilities.effects.common.cost.SpellsCostIncreasementAllEffect;
+import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
+import mage.constants.TargetController;
 import mage.constants.Zone;
+import mage.filter.FilterCard;
+
+import java.util.UUID;
 
 /**
- *
  * @author Plopman
  */
 public final class SphereOfResistance extends CardImpl {
 
     public SphereOfResistance(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{2}");
+        super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}");
 
         // Spells cost {1} more to cast.
-        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostIncreasementAllEffect(1)));
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
+                new SpellsCostIncreasingAllEffect(1, new FilterCard("Spells"), TargetController.ANY))
+        );
     }
 
     public SphereOfResistance(final SphereOfResistance card) {
diff --git a/Mage.Sets/src/mage/cards/s/Squeeze.java b/Mage.Sets/src/mage/cards/s/Squeeze.java
index 3dabaa83dc..2605970a04 100644
--- a/Mage.Sets/src/mage/cards/s/Squeeze.java
+++ b/Mage.Sets/src/mage/cards/s/Squeeze.java
@@ -1,35 +1,34 @@
-
 package mage.cards.s;
 
-import java.util.UUID;
 import mage.abilities.common.SimpleStaticAbility;
-import mage.abilities.effects.Effect;
-import mage.abilities.effects.common.cost.SpellsCostIncreasementAllEffect;
+import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
+import mage.constants.TargetController;
 import mage.constants.Zone;
 import mage.filter.FilterCard;
 
+import java.util.UUID;
+
 /**
- *
  * @author fireshoes
  */
 public final class Squeeze extends CardImpl {
-    
+
     private static final FilterCard filter = new FilterCard("Sorcery spells");
-    
+
     static {
         filter.add(CardType.SORCERY.getPredicate());
     }
 
     public Squeeze(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{U}");
+        super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}");
 
         // Sorcery spells cost {3} more to cast.
-        Effect effect = new SpellsCostIncreasementAllEffect(filter, 3);
-        effect.setText("Sorcery spells cost {3} more to cast");
-        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect));
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
+                new SpellsCostIncreasingAllEffect(3, filter, TargetController.ANY))
+        );
     }
 
     public Squeeze(final Squeeze card) {
diff --git a/Mage.Sets/src/mage/cards/t/ThaliaGuardianOfThraben.java b/Mage.Sets/src/mage/cards/t/ThaliaGuardianOfThraben.java
index 4176be6c9c..985e87e5de 100644
--- a/Mage.Sets/src/mage/cards/t/ThaliaGuardianOfThraben.java
+++ b/Mage.Sets/src/mage/cards/t/ThaliaGuardianOfThraben.java
@@ -1,26 +1,28 @@
-
 package mage.cards.t;
 
-import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.Ability;
-import mage.abilities.SpellAbility;
 import mage.abilities.common.SimpleStaticAbility;
-import mage.abilities.effects.common.cost.CostModificationEffectImpl;
+import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect;
 import mage.abilities.keyword.FirstStrikeAbility;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.*;
-import mage.game.Game;
-import mage.util.CardUtil;
+import mage.filter.FilterCard;
+import mage.filter.predicate.Predicates;
+
+import java.util.UUID;
 
 /**
- *
  * @author BetaSteward
  */
 public final class ThaliaGuardianOfThraben extends CardImpl {
 
+    private static final FilterCard filter = new FilterCard("Noncreature spells");
+
+    static {
+        filter.add(Predicates.not(CardType.CREATURE.getPredicate()));
+    }
+
     public ThaliaGuardianOfThraben(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}");
         addSuperType(SuperType.LEGENDARY);
@@ -33,7 +35,7 @@ public final class ThaliaGuardianOfThraben extends CardImpl {
         this.addAbility(FirstStrikeAbility.getInstance());
 
         // Noncreature spells cost {1} more to cast.
-        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ThaliaGuardianOfThrabenCostReductionEffect()));
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostIncreasingAllEffect(1, filter, TargetController.ANY)));
 
     }
 
@@ -45,39 +47,4 @@ public final class ThaliaGuardianOfThraben extends CardImpl {
     public ThaliaGuardianOfThraben copy() {
         return new ThaliaGuardianOfThraben(this);
     }
-}
-
-class ThaliaGuardianOfThrabenCostReductionEffect extends CostModificationEffectImpl {
-
-    ThaliaGuardianOfThrabenCostReductionEffect() {
-        super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST);
-        staticText = "Noncreature spells cost {1} more to cast";
-    }
-
-    ThaliaGuardianOfThrabenCostReductionEffect(ThaliaGuardianOfThrabenCostReductionEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        CardUtil.increaseCost(abilityToModify, 1);
-        return true;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        if (abilityToModify instanceof SpellAbility) {
-            Card card = game.getCard(abilityToModify.getSourceId());
-            if (card != null && !card.isCreature()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public ThaliaGuardianOfThrabenCostReductionEffect copy() {
-        return new ThaliaGuardianOfThrabenCostReductionEffect(this);
-    }
-
-}
+}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/t/ThornOfAmethyst.java b/Mage.Sets/src/mage/cards/t/ThornOfAmethyst.java
index 3de6135ad2..78eed2b2e1 100644
--- a/Mage.Sets/src/mage/cards/t/ThornOfAmethyst.java
+++ b/Mage.Sets/src/mage/cards/t/ThornOfAmethyst.java
@@ -1,31 +1,33 @@
-
 package mage.cards.t;
 
-import java.util.UUID;
 import mage.abilities.common.SimpleStaticAbility;
-import mage.abilities.effects.common.cost.SpellsCostIncreasementAllEffect;
+import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
+import mage.constants.TargetController;
 import mage.constants.Zone;
 import mage.filter.FilterCard;
 import mage.filter.predicate.Predicates;
 
+import java.util.UUID;
+
 /**
- *
  * @author Plopman
  */
 public final class ThornOfAmethyst extends CardImpl {
 
     private static final FilterCard filter = new FilterCard("Noncreature spells");
+
     static {
         filter.add(Predicates.not(CardType.CREATURE.getPredicate()));
     }
+
     public ThornOfAmethyst(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{2}");
+        super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}");
 
         // Noncreature spells cost {1} more to cast.
-        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostIncreasementAllEffect(filter, 1)));
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostIncreasingAllEffect(1, filter, TargetController.ANY)));
     }
 
     public ThornOfAmethyst(final ThornOfAmethyst card) {
diff --git a/Mage.Sets/src/mage/cards/v/VrynWingmare.java b/Mage.Sets/src/mage/cards/v/VrynWingmare.java
index e35cf3c18e..8ecdac26b4 100644
--- a/Mage.Sets/src/mage/cards/v/VrynWingmare.java
+++ b/Mage.Sets/src/mage/cards/v/VrynWingmare.java
@@ -1,31 +1,31 @@
-
 package mage.cards.v;
 
-import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.Ability;
-import mage.abilities.SpellAbility;
 import mage.abilities.common.SimpleStaticAbility;
-import mage.abilities.effects.common.cost.CostModificationEffectImpl;
+import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect;
 import mage.abilities.keyword.FlyingAbility;
-import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.constants.SubType;
-import mage.constants.CostModificationType;
-import mage.constants.Duration;
-import mage.constants.Outcome;
+import mage.constants.TargetController;
 import mage.constants.Zone;
-import mage.game.Game;
-import mage.util.CardUtil;
+import mage.filter.FilterCard;
+import mage.filter.predicate.Predicates;
+
+import java.util.UUID;
 
 /**
- *
  * @author fireshoes
  */
 public final class VrynWingmare extends CardImpl {
 
+    private static final FilterCard filter = new FilterCard("Noncreature spells");
+
+    static {
+        filter.add(Predicates.not(CardType.CREATURE.getPredicate()));
+    }
+
     public VrynWingmare(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}");
         this.subtype.add(SubType.PEGASUS);
@@ -36,7 +36,7 @@ public final class VrynWingmare extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
 
         // Noncreature spells cost {1} more to cast.
-        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new VrynWingmareCostReductionEffect()));
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostIncreasingAllEffect(1, filter, TargetController.ANY)));
     }
 
     public VrynWingmare(final VrynWingmare card) {
@@ -48,38 +48,3 @@ public final class VrynWingmare extends CardImpl {
         return new VrynWingmare(this);
     }
 }
-
-class VrynWingmareCostReductionEffect extends CostModificationEffectImpl {
-
-    VrynWingmareCostReductionEffect() {
-        super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST);
-        staticText = "Noncreature spells cost {1} more to cast";
-    }
-
-    VrynWingmareCostReductionEffect(VrynWingmareCostReductionEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        CardUtil.increaseCost(abilityToModify, 1);
-        return true;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        if (abilityToModify instanceof SpellAbility) {
-            Card card = game.getCard(abilityToModify.getSourceId());
-            if (card != null && !card.isCreature()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public VrynWingmareCostReductionEffect copy() {
-        return new VrynWingmareCostReductionEffect(this);
-    }
-
-}
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ConditionalCostModificationTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ConditionalCostModificationTest.java
index 8dbe972ea3..71a9242073 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ConditionalCostModificationTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ConditionalCostModificationTest.java
@@ -5,10 +5,12 @@ import mage.abilities.condition.common.MyTurnCondition;
 import mage.abilities.condition.common.NotMyTurnCondition;
 import mage.abilities.decorator.ConditionalCostModificationEffect;
 import mage.abilities.effects.common.cost.AbilitiesCostReductionControllerEffect;
-import mage.abilities.effects.common.cost.SpellsCostIncreasementAllEffect;
+import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect;
 import mage.abilities.keyword.EquipAbility;
 import mage.constants.PhaseStep;
+import mage.constants.TargetController;
 import mage.constants.Zone;
+import mage.filter.FilterCard;
 import org.junit.Test;
 import org.mage.test.serverside.base.CardTestPlayerBase;
 
@@ -113,7 +115,7 @@ public class ConditionalCostModificationTest extends CardTestPlayerBase {
                 new ConditionalCostModificationEffect(
                         new AbilitiesCostReductionControllerEffect(EquipAbility.class, "equip"),
                         NotMyTurnCondition.instance,
-                        new SpellsCostIncreasementAllEffect(1),
+                        new SpellsCostIncreasingAllEffect(1, new FilterCard(), TargetController.ANY),
                         ""
                 )
         ));
diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostIncreasementAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostIncreasementAllEffect.java
deleted file mode 100644
index cceda07b55..0000000000
--- a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostIncreasementAllEffect.java
+++ /dev/null
@@ -1,66 +0,0 @@
-
-package mage.abilities.effects.common.cost;
-
-import mage.abilities.Ability;
-import mage.abilities.SpellAbility;
-import mage.cards.Card;
-import mage.constants.CostModificationType;
-import mage.constants.Duration;
-import mage.constants.Outcome;
-import mage.filter.FilterCard;
-import mage.game.Game;
-import mage.game.stack.Spell;
-import mage.util.CardUtil;
-
-/**
- *
- * @author Plopman
- */
-public class SpellsCostIncreasementAllEffect extends CostModificationEffectImpl {
-
-    private FilterCard filter;
-    private int amount;
-
-    public SpellsCostIncreasementAllEffect(int amount) {
-        this(new FilterCard("Spells"), amount);
-    }
-
-    public SpellsCostIncreasementAllEffect(FilterCard filter, int amount) {
-        super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST);
-        this.filter = filter;
-        this.amount = amount;
-        this.staticText = new StringBuilder(filter.getMessage()).append(" cost {").append(amount).append("} more to cast").toString();
-    }
-
-    protected SpellsCostIncreasementAllEffect(SpellsCostIncreasementAllEffect effect) {
-        super(effect);
-        this.filter = effect.filter;
-        this.amount = effect.amount;
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        CardUtil.increaseCost(abilityToModify, this.amount);
-        return true;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        if (abilityToModify instanceof SpellAbility) {
-            Spell spell = (Spell) game.getStack().getStackObject(abilityToModify.getId());
-            if (spell != null) {
-                return this.filter.match(spell, game);
-            } else {
-                // used at least for flashback ability because Flashback ability doesn't use stack
-                Card sourceCard = game.getCard(abilityToModify.getSourceId());
-                return sourceCard != null && this.filter.match(sourceCard, game);
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public SpellsCostIncreasementAllEffect copy() {
-        return new SpellsCostIncreasementAllEffect(this);
-    }
-}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostIncreasementControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostIncreasementControllerEffect.java
deleted file mode 100644
index 7023fc7884..0000000000
--- a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostIncreasementControllerEffect.java
+++ /dev/null
@@ -1,90 +0,0 @@
-
-package mage.abilities.effects.common.cost;
-
-import mage.abilities.Ability;
-import mage.abilities.SpellAbility;
-import mage.abilities.costs.mana.ManaCost;
-import mage.abilities.costs.mana.ManaCosts;
-import mage.cards.Card;
-import mage.constants.CostModificationType;
-import mage.constants.Duration;
-import mage.constants.Outcome;
-import mage.filter.FilterCard;
-import mage.game.Game;
-import mage.game.stack.Spell;
-import mage.util.CardUtil;
-
-/**
- *
- * @author Quercitron
- */
-public class SpellsCostIncreasementControllerEffect extends CostModificationEffectImpl {
-
-    private final FilterCard filter;
-    private final int amount;
-    private ManaCosts<ManaCost> manaCostsToIncrease = null;
-
-    public SpellsCostIncreasementControllerEffect(FilterCard filter, ManaCosts<ManaCost> manaCostsToReduce) {
-        super(Duration.WhileOnBattlefield, Outcome.Detriment, CostModificationType.INCREASE_COST);
-        this.filter = filter;
-        this.amount = 0;
-        this.manaCostsToIncrease = manaCostsToReduce;
-
-        StringBuilder sb = new StringBuilder();
-        sb.append(filter.getMessage()).append(" you cast cost ");
-        for (String manaSymbol : manaCostsToReduce.getSymbols()) {
-            sb.append(manaSymbol);
-        }
-        sb.append(" more to cast");
-        this.staticText = sb.toString();
-    }
-
-    public SpellsCostIncreasementControllerEffect(FilterCard filter, int amount) {
-        super(Duration.WhileOnBattlefield, Outcome.Detriment, CostModificationType.INCREASE_COST);
-        this.filter = filter;
-        this.amount = amount;
-
-        StringBuilder sb = new StringBuilder();
-        sb.append(filter.getMessage()).append(" you cast cost {").append(amount).append("} more to cast");
-        this.staticText = sb.toString();
-    }
-
-    protected SpellsCostIncreasementControllerEffect(SpellsCostIncreasementControllerEffect effect) {
-        super(effect);
-        this.filter = effect.filter;
-        this.amount = effect.amount;
-        this.manaCostsToIncrease = effect.manaCostsToIncrease;
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        if (manaCostsToIncrease != null) {
-            CardUtil.increaseCost((SpellAbility) abilityToModify, manaCostsToIncrease);
-        } else {
-            CardUtil.increaseCost(abilityToModify, this.amount);
-        }
-        return true;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        if (abilityToModify instanceof SpellAbility) {
-            if (abilityToModify.isControlledBy(source.getControllerId())) {
-                Spell spell = (Spell) game.getStack().getStackObject(abilityToModify.getId());
-                if (spell != null) {
-                    return this.filter.match(spell, game);
-                } else {
-                    // used at least for flashback ability because Flashback ability doesn't use stack
-                    Card sourceCard = game.getCard(abilityToModify.getSourceId());
-                    return sourceCard != null && this.filter.match(sourceCard, game);
-                }
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public SpellsCostIncreasementControllerEffect copy() {
-        return new SpellsCostIncreasementControllerEffect(this);
-    }
-}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostIncreasingAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostIncreasingAllEffect.java
new file mode 100644
index 0000000000..9786f8c077
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostIncreasingAllEffect.java
@@ -0,0 +1,139 @@
+package mage.abilities.effects.common.cost;
+
+import mage.abilities.Ability;
+import mage.abilities.SpellAbility;
+import mage.abilities.costs.mana.ManaCost;
+import mage.abilities.costs.mana.ManaCosts;
+import mage.cards.Card;
+import mage.constants.CostModificationType;
+import mage.constants.Duration;
+import mage.constants.Outcome;
+import mage.constants.TargetController;
+import mage.filter.FilterCard;
+import mage.game.Game;
+import mage.game.stack.Spell;
+import mage.players.Player;
+import mage.util.CardUtil;
+
+/**
+ * @author JayDi85
+ */
+public class SpellsCostIncreasingAllEffect extends CostModificationEffectImpl {
+
+    private final FilterCard filter;
+    private final TargetController targetController;
+    private final int increaseGenericCost;
+    private ManaCosts<ManaCost> increaseManaCosts;
+
+    public SpellsCostIncreasingAllEffect(int increaseGenericCost, FilterCard filter, TargetController targetController) {
+        super(Duration.WhileOnBattlefield, Outcome.Detriment, CostModificationType.INCREASE_COST);
+        this.filter = filter;
+        this.targetController = targetController;
+        this.increaseGenericCost = increaseGenericCost;
+        this.increaseManaCosts = null;
+
+        setText();
+    }
+
+    public SpellsCostIncreasingAllEffect(ManaCosts<ManaCost> increaseManaCosts, FilterCard filter, TargetController targetController) {
+        super(Duration.WhileOnBattlefield, Outcome.Detriment, CostModificationType.INCREASE_COST);
+        this.filter = filter;
+        this.targetController = targetController;
+        this.increaseGenericCost = 0;
+        this.increaseManaCosts = increaseManaCosts;
+
+        setText();
+    }
+
+    private void setText() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(filter.getMessage());
+        switch (this.targetController) {
+            case YOU:
+                sb.append(" you cast");
+                break;
+            case OPPONENT:
+                sb.append(" your opponents cast");
+                break;
+            case ANY:
+                break;
+            default:
+                throw new IllegalArgumentException("Unsupported TargetController " + this.targetController);
+        }
+
+        sb.append(" cost ");
+        if (this.increaseManaCosts != null) {
+            for (String manaSymbol : this.increaseManaCosts.getSymbols()) {
+                sb.append(manaSymbol);
+            }
+        } else {
+            sb.append("{").append(increaseGenericCost).append("}");
+        }
+
+        sb.append(" more to cast");
+        this.staticText = sb.toString();
+    }
+
+    protected SpellsCostIncreasingAllEffect(SpellsCostIncreasingAllEffect effect) {
+        super(effect);
+        this.filter = effect.filter;
+        this.targetController = effect.targetController;
+        this.increaseGenericCost = effect.increaseGenericCost;
+        this.increaseManaCosts = effect.increaseManaCosts;
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source, Ability abilityToModify) {
+        if (increaseManaCosts != null) {
+            CardUtil.increaseCost((SpellAbility) abilityToModify, increaseManaCosts);
+        } else {
+            CardUtil.increaseCost(abilityToModify, this.increaseGenericCost);
+        }
+        return true;
+    }
+
+    @Override
+    public boolean applies(Ability abilityToModify, Ability source, Game game) {
+        if (abilityToModify instanceof SpellAbility) {
+            Player abilityController = game.getPlayer(abilityToModify.getControllerId());
+            Player sourceController = game.getPlayer(source.getControllerId());
+            if (abilityController == null || sourceController == null) {
+                return false;
+            }
+
+            switch (this.targetController) {
+                case YOU:
+                    if (!sourceController.getId().equals(abilityController.getId())) {
+                        return false;
+                    }
+                    break;
+                case OPPONENT:
+                    if (!sourceController.hasOpponent(abilityController.getId(), game)) {
+                        return false;
+                    }
+                    break;
+                case ANY:
+                    break;
+                default:
+                    throw new IllegalArgumentException("Unsupported TargetController " + this.targetController);
+            }
+
+            Spell spell = (Spell) game.getStack().getStackObject(abilityToModify.getId());
+            if (spell != null) {
+                // real cast with put on stack
+                return this.filter.match(spell, game);
+            } else {
+                // get playable and other staff without put on stack
+                // used at least for flashback ability because Flashback ability doesn't use stack
+                Card sourceCard = game.getCard(abilityToModify.getSourceId());
+                return sourceCard != null && this.filter.match(sourceCard, game);
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public SpellsCostIncreasingAllEffect copy() {
+        return new SpellsCostIncreasingAllEffect(this);
+    }
+}

From 5661bb1bfed3c6b4c5a280cb834b6ee3cef69041 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Mon, 29 Jun 2020 14:50:46 +0200
Subject: [PATCH 550/586] * Some minor code changes.

---
 Mage.Sets/src/mage/cards/i/Invigorate.java                  | 5 ++---
 .../src/main/java/mage/abilities/keyword/EmergeAbility.java | 6 +++---
 .../main/java/mage/abilities/keyword/SpectacleAbility.java  | 2 +-
 Mage/src/main/java/mage/game/GameState.java                 | 2 +-
 4 files changed, 7 insertions(+), 8 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/i/Invigorate.java b/Mage.Sets/src/mage/cards/i/Invigorate.java
index ad4e2d3ab9..dddb541810 100644
--- a/Mage.Sets/src/mage/cards/i/Invigorate.java
+++ b/Mage.Sets/src/mage/cards/i/Invigorate.java
@@ -1,4 +1,3 @@
-
 package mage.cards.i;
 
 import java.util.UUID;
@@ -9,7 +8,7 @@ import mage.abilities.effects.common.continuous.BoostTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.Duration;
+import mage.constants.Duration; 
 import mage.constants.SubType;
 import mage.filter.FilterPermanent;
 import mage.target.common.TargetCreaturePermanent;
@@ -29,9 +28,9 @@ public final class Invigorate extends CardImpl {
     public Invigorate(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{G}");
 
-
         // If you control a Forest, rather than pay Invigorate's mana cost, you may have an opponent gain 3 life.
         this.addAbility(new AlternativeCostSourceAbility(new GainLifeOpponentCost(3), new PermanentsOnTheBattlefieldCondition(filter)));    
+        
         // Target creature gets +4/+4 until end of turn.
         this.getSpellAbility().addEffect(new BoostTargetEffect(4,4,Duration.EndOfTurn));
         this.getSpellAbility().addTarget(new TargetCreaturePermanent());
diff --git a/Mage/src/main/java/mage/abilities/keyword/EmergeAbility.java b/Mage/src/main/java/mage/abilities/keyword/EmergeAbility.java
index 805c78ff46..8045bc39bb 100644
--- a/Mage/src/main/java/mage/abilities/keyword/EmergeAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/EmergeAbility.java
@@ -28,7 +28,8 @@ public class EmergeAbility extends SpellAbility {
 
     public EmergeAbility(Card card, ManaCosts<ManaCost> emergeCost) {
         super(card.getSpellAbility());
-        this.newId();
+        this.emergeCost = emergeCost.copy();
+        this.newId(); // Why is this neccessary? will create new id anyway or not?
         this.setCardName(card.getName() + " with emerge");
         zone = Zone.HAND;
         spellAbilityType = SpellAbilityType.BASE_ALTERNATE;
@@ -37,8 +38,7 @@ public class EmergeAbility extends SpellAbility {
         this.getManaCostsToPay().clear();
         this.addManaCost(emergeCost.copy());
 
-        this.setRuleAtTheTop(true);
-        this.emergeCost = emergeCost.copy();
+        this.setRuleAtTheTop(true);        
     }
 
     public EmergeAbility(final EmergeAbility ability) {
diff --git a/Mage/src/main/java/mage/abilities/keyword/SpectacleAbility.java b/Mage/src/main/java/mage/abilities/keyword/SpectacleAbility.java
index 9cb4a763a7..3dbcbc2a67 100644
--- a/Mage/src/main/java/mage/abilities/keyword/SpectacleAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/SpectacleAbility.java
@@ -20,7 +20,7 @@ public class SpectacleAbility extends SpellAbility {
 
     public static final String SPECTACLE_ACTIVATION_VALUE_KEY = "spectacleActivation";
 
-    private String rule;
+    private final String rule;
 
     public SpectacleAbility(Card card, ManaCost spectacleCosts) {
         super(card.getSpellAbility());
diff --git a/Mage/src/main/java/mage/game/GameState.java b/Mage/src/main/java/mage/game/GameState.java
index 04f1e5d2a2..13b084f3f1 100644
--- a/Mage/src/main/java/mage/game/GameState.java
+++ b/Mage/src/main/java/mage/game/GameState.java
@@ -867,7 +867,7 @@ public class GameState implements Serializable, Copyable<GameState> {
     }
 
     /**
-     * Abilities that are applied to other objects or applie for a certain time
+     * Abilities that are applied to other objects or applied for a certain time
      * span
      *
      * @param ability

From d1e31140cc6b94691ac3eeeeb7544aade9a4cf7d Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Mon, 29 Jun 2020 14:54:29 +0200
Subject: [PATCH 551/586] * Fixed a problem that unintended allowed to cast
 spells with alternate cost to cast conditions (fixes #6739).

---
 .../UseAlternateSourceCostsTest.java          | 46 +++++++++++++++++++
 .../costs/AlternativeCostSourceAbility.java   | 12 ++---
 .../main/java/mage/players/PlayerImpl.java    |  3 ++
 3 files changed, 55 insertions(+), 6 deletions(-)

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/alternate/UseAlternateSourceCostsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/alternate/UseAlternateSourceCostsTest.java
index 1ecc0ebc70..fe78134d2c 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/cost/alternate/UseAlternateSourceCostsTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/alternate/UseAlternateSourceCostsTest.java
@@ -145,6 +145,52 @@ public class UseAlternateSourceCostsTest extends CardTestPlayerBase {
         assertAllCommandsUsed();
     }
 
+    @Test
+    public void test_Playable_WithOpponentGainingLive() {
+        // If you control a Forest, rather than pay Invigorate's mana cost, you may have an opponent gain 3 life.
+        // Target creature gets +4/+4 until end of turn.        
+        addCard(Zone.HAND, playerA, "Invigorate"); // Instant {2}{G}
+        addCard(Zone.BATTLEFIELD, playerA, "Forest");
+        addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion");
+        
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Invigorate", "Silvercoat Lion");
+        setChoice(playerA, "Yes"); // use alternative cost
+        addTarget(playerA, playerB); // Opponent to gain live
+        
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+        
+        assertGraveyardCount(playerA, "Invigorate", 1);
+        assertPowerToughness(playerA, "Silvercoat Lion", 6, 6);
+        assertLife(playerB, 23);
+    }
+    
+    @Test
+    public void test_Not_Playable_WithOpponentGainingLive() {
+        // If you control a Forest, rather than pay Invigorate's mana cost, you may have an opponent gain 3 life.
+        // Target creature gets +4/+4 until end of turn.        
+        addCard(Zone.GRAVEYARD, playerA, "Invigorate"); // Instant {2}{G}
+        addCard(Zone.BATTLEFIELD, playerA, "Forest");
+        addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion");
+        addCard(Zone.BATTLEFIELD, playerB, "Forest");
+        
+         // can't see as playable because in graveyard
+        checkPlayableAbility("can't", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Invigorate", false);
+        
+        checkPlayableAbility("can't", 1, PhaseStep.PRECOMBAT_MAIN, playerB, "Cast Invigorate", false);
+        
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.BEGIN_COMBAT);
+        execute();
+        assertAllCommandsUsed();
+        
+        assertGraveyardCount(playerA, "Invigorate", 1);
+        assertPowerToughness(playerA, "Silvercoat Lion", 2, 2);
+        assertLife(playerB, 20);
+    }
+    
     @Test
     @Ignore // TODO: make test to check combo of alternative cost and cost reduction effects
     public void test_Playable_WithCostReduction() {
diff --git a/Mage/src/main/java/mage/abilities/costs/AlternativeCostSourceAbility.java b/Mage/src/main/java/mage/abilities/costs/AlternativeCostSourceAbility.java
index 25ba6f8f4f..7fc082a423 100644
--- a/Mage/src/main/java/mage/abilities/costs/AlternativeCostSourceAbility.java
+++ b/Mage/src/main/java/mage/abilities/costs/AlternativeCostSourceAbility.java
@@ -151,11 +151,11 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter
                     for (AlternativeCost2 alternateCost : alternativeCostsToCheck) {
                         alternateCost.activate();
                         for (Iterator it = ((Costs) alternateCost).iterator(); it.hasNext(); ) {
-                            Cost costDeailed = (Cost) it.next();
-                            if (costDeailed instanceof ManaCost) {
-                                ability.getManaCostsToPay().add((ManaCost) costDeailed.copy());
-                            } else {
-                                ability.getCosts().add(costDeailed.copy());
+                            Cost costDetailed = (Cost) it.next();
+                            if (costDetailed instanceof ManaCost) {
+                                ability.getManaCostsToPay().add((ManaCost) costDetailed.copy());
+                            } else if (costDetailed != null) {
+                                ability.getCosts().add(costDetailed.copy());
                             }
                         }
                     }
@@ -222,7 +222,7 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter
                     sb.append("pay ");
                 }
                 String text = alternativeCost.getText(true);
-                sb.append(Character.toLowerCase(text.charAt(0)) + text.substring(1));
+                sb.append(Character.toLowerCase(text.charAt(0))).append(text.substring(1));
             }
             ++numberCosts;
         }
diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java
index 0cba9775d2..7842874ad7 100644
--- a/Mage/src/main/java/mage/players/PlayerImpl.java
+++ b/Mage/src/main/java/mage/players/PlayerImpl.java
@@ -3207,6 +3207,9 @@ public abstract class PlayerImpl implements Player, Serializable {
         // check "can play" condition as affected controller (BUT play from not own hand zone must be checked as original controller)
         // must check all abilities, not activated only
         for (Ability ability : candidateAbilities) {
+            if (!(ability instanceof ActivatedAbility)) {
+                continue;
+            }
             boolean isPlaySpell = (ability instanceof SpellAbility);
             boolean isPlayLand = (ability instanceof PlayLandAbility);
 

From 370d821dbf21457847314b5fda2d3d662c14b37d Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Mon, 29 Jun 2020 15:01:53 +0200
Subject: [PATCH 552/586] * Gadrak, the Crown-Scourge - Fixed wrong condition
 for attack restriction (fixes #6743).

---
 Mage.Sets/src/mage/cards/g/GadrakTheCrownScourge.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Mage.Sets/src/mage/cards/g/GadrakTheCrownScourge.java b/Mage.Sets/src/mage/cards/g/GadrakTheCrownScourge.java
index 2449c94373..2fd15a1465 100644
--- a/Mage.Sets/src/mage/cards/g/GadrakTheCrownScourge.java
+++ b/Mage.Sets/src/mage/cards/g/GadrakTheCrownScourge.java
@@ -31,7 +31,7 @@ import java.util.UUID;
 public final class GadrakTheCrownScourge extends CardImpl {
 
     private static final Condition condition = new PermanentsOnTheBattlefieldCondition(
-            StaticFilters.FILTER_PERMANENT_ARTIFACT, ComparisonType.MORE_THAN, 3
+            StaticFilters.FILTER_PERMANENT_ARTIFACT, ComparisonType.FEWER_THAN, 4
     );
 
     public GadrakTheCrownScourge(UUID ownerId, CardSetInfo setInfo) {

From 34dbf7b67e59a140c51c1cb8a8ca71277ba16742 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Mon, 29 Jun 2020 15:13:46 +0200
Subject: [PATCH 553/586] * Peer into the Abyss - Fixed that the numbers were
 not rounded up (#6646).

---
 Mage.Sets/src/mage/cards/p/PeerIntoTheAbyss.java | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/p/PeerIntoTheAbyss.java b/Mage.Sets/src/mage/cards/p/PeerIntoTheAbyss.java
index 7f5e8372ea..e27f70146b 100644
--- a/Mage.Sets/src/mage/cards/p/PeerIntoTheAbyss.java
+++ b/Mage.Sets/src/mage/cards/p/PeerIntoTheAbyss.java
@@ -58,8 +58,8 @@ class PeerIntoTheAbyssEffect extends OneShotEffect {
         if (player == null) {
             return false;
         }
-        player.drawCards((int) Math.ceil(player.getLibrary().size() / 2), source.getSourceId(), game);
-        player.loseLife((int) Math.ceil(player.getLife() / 2), game, false);
+        player.drawCards((int) Math.ceil(player.getLibrary().size() / 2.0), source.getSourceId(), game);
+        player.loseLife((int) Math.ceil(player.getLife() / 2.0), game, false);
         return true;
     }
 }
\ No newline at end of file

From accbf7a3a717c234b2ecddef86f3a456cf2ba5e3 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Mon, 29 Jun 2020 15:16:51 +0200
Subject: [PATCH 554/586] * Read the Tides - Fixed number of maximal possible
 targets from 1 to 2 (#6646).

---
 Mage.Sets/src/mage/cards/r/ReadTheTides.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Mage.Sets/src/mage/cards/r/ReadTheTides.java b/Mage.Sets/src/mage/cards/r/ReadTheTides.java
index d3073cfd94..1c02d51448 100644
--- a/Mage.Sets/src/mage/cards/r/ReadTheTides.java
+++ b/Mage.Sets/src/mage/cards/r/ReadTheTides.java
@@ -24,7 +24,7 @@ public final class ReadTheTides extends CardImpl {
 
         // • Return up to two target creatures to their owners' hands.
         Mode mode = new Mode(new ReturnToHandTargetEffect());
-        mode.addTarget(new TargetCreaturePermanent(0, 1));
+        mode.addTarget(new TargetCreaturePermanent(0, 2));
         this.getSpellAbility().addMode(mode);
     }
 

From c39bcf3d5adae73efc23cf3e903ed3deab9a82cd Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Mon, 29 Jun 2020 15:25:55 +0200
Subject: [PATCH 555/586] * Primal Might - Handled fizzled message for optional
 fight effect (#6646).

---
 .../mage/abilities/effects/common/FightTargetsEffect.java     | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/Mage/src/main/java/mage/abilities/effects/common/FightTargetsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/FightTargetsEffect.java
index 94fa24457f..6661533d8e 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/FightTargetsEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/FightTargetsEffect.java
@@ -34,6 +34,7 @@ public class FightTargetsEffect extends OneShotEffect {
         if (card != null) {
             UUID target1Id = null;
             UUID target2Id = null;
+            boolean secondTargetOptional = false;
             // first target is in target pointer, second target is a normal target
             if (source.getTargets().size() < 2) {
                 if (!source.getTargets().get(0).isLegal(source, game)) {
@@ -48,6 +49,7 @@ public class FightTargetsEffect extends OneShotEffect {
             } else if (source.getTargets().get(0).isLegal(source, game) && source.getTargets().get(1).isLegal(source, game)) {
                 target1Id = source.getTargets().get(0).getFirstTarget();
                 target2Id = source.getTargets().get(1).getFirstTarget();
+                secondTargetOptional = source.getTargets().get(1).getMinNumberOfTargets() == 0;
             }
             Permanent creature1 = game.getPermanent(target1Id);
             Permanent creature2 = game.getPermanent(target2Id);
@@ -57,7 +59,7 @@ public class FightTargetsEffect extends OneShotEffect {
                     return creature1.fight(creature2, source, game);
                 }
             }
-            if (!game.isSimulation()) {
+            if (!game.isSimulation() && !secondTargetOptional) {
                 game.informPlayers(card.getName() + " has been fizzled.");
             }
         }

From 0bc7008130ee0784348f9a8276933c7b00234f6c Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Mon, 29 Jun 2020 16:57:59 +0200
Subject: [PATCH 556/586] * Peer into the Abyss - Fixed that the effects were
 applied to the controller instead of the target player (#6646).

---
 Mage.Sets/src/mage/cards/p/PeerIntoTheAbyss.java | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/p/PeerIntoTheAbyss.java b/Mage.Sets/src/mage/cards/p/PeerIntoTheAbyss.java
index e27f70146b..8db533e2ef 100644
--- a/Mage.Sets/src/mage/cards/p/PeerIntoTheAbyss.java
+++ b/Mage.Sets/src/mage/cards/p/PeerIntoTheAbyss.java
@@ -54,12 +54,12 @@ class PeerIntoTheAbyssEffect extends OneShotEffect {
 
     @Override
     public boolean apply(Game game, Ability source) {
-        Player player = game.getPlayer(source.getControllerId());
-        if (player == null) {
+        Player targetPlayer = game.getPlayer(getTargetPointer().getFirst(game, source));
+        if (targetPlayer == null) {
             return false;
         }
-        player.drawCards((int) Math.ceil(player.getLibrary().size() / 2.0), source.getSourceId(), game);
-        player.loseLife((int) Math.ceil(player.getLife() / 2.0), game, false);
+        targetPlayer.drawCards((int) Math.ceil(targetPlayer.getLibrary().size() / 2.0), source.getSourceId(), game);
+        targetPlayer.loseLife((int) Math.ceil(targetPlayer.getLife() / 2.0), game, false);
         return true;
     }
 }
\ No newline at end of file

From a5d10d6a5df2d59f6b7b95bdde4e27bc3eb24bf6 Mon Sep 17 00:00:00 2001
From: arcox <10953229+arcox@users.noreply.github.com>
Date: Mon, 29 Jun 2020 15:37:43 +0000
Subject: [PATCH 557/586] Implement Liege of the Hollows from WTH (#5489)
 (#6727)

---
 .../src/mage/cards/l/LiegeOfTheHollows.java   | 88 +++++++++++++++++++
 Mage.Sets/src/mage/sets/Weatherlight.java     |  3 +-
 2 files changed, 90 insertions(+), 1 deletion(-)
 create mode 100644 Mage.Sets/src/mage/cards/l/LiegeOfTheHollows.java

diff --git a/Mage.Sets/src/mage/cards/l/LiegeOfTheHollows.java b/Mage.Sets/src/mage/cards/l/LiegeOfTheHollows.java
new file mode 100644
index 0000000000..0d95599b09
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/l/LiegeOfTheHollows.java
@@ -0,0 +1,88 @@
+package mage.cards.l;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.effects.Effect;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.CreateTokenTargetEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Outcome;
+import mage.constants.SubType;
+import mage.game.Game;
+import mage.game.permanent.token.SquirrelToken;
+import mage.players.Player;
+import mage.target.targetpointer.FixedTarget;
+import mage.util.ManaUtil;
+
+import java.util.UUID;
+
+/**
+ * @author arcox
+ */
+public final class LiegeOfTheHollows extends CardImpl {
+
+    public LiegeOfTheHollows(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{G}");
+        this.subtype.add(SubType.SPIRIT);
+
+        this.power = new MageInt(3);
+        this.toughness = new MageInt(4);
+
+        // When Liege of the Hollows dies, each player may pay any amount of mana.
+        // Then each player creates a number of 1/1 green Squirrel creature tokens equal to the amount of mana they paid this way.
+        this.addAbility(new DiesTriggeredAbility(new LiegeOfTheHollowsEffect()));
+    }
+
+    public LiegeOfTheHollows(final LiegeOfTheHollows card) {
+        super(card);
+    }
+
+    @Override
+    public LiegeOfTheHollows copy() {
+        return new LiegeOfTheHollows(this);
+    }
+}
+
+class LiegeOfTheHollowsEffect extends OneShotEffect {
+
+    public LiegeOfTheHollowsEffect() {
+        super(Outcome.Detriment);
+        this.staticText = "each player may pay any amount of mana. Then each player creates a number "
+                + "of 1/1 green Squirrel creature tokens equal to the amount of mana they paid this way";
+    }
+
+    public LiegeOfTheHollowsEffect(final LiegeOfTheHollowsEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public LiegeOfTheHollowsEffect copy() {
+        return new LiegeOfTheHollowsEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Player controller = game.getPlayer(source.getControllerId());
+        if (controller != null) {
+            for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
+                Player player = game.getPlayer(playerId);
+                if (player != null) {
+                    int numSquirrels = ManaUtil.playerPaysXGenericMana(false, "Liege of the Hollows", player, source, game);
+                    if (numSquirrels > 0) {
+                        Effect effect = new CreateTokenTargetEffect(new SquirrelToken(), numSquirrels);
+                        effect.setTargetPointer(new FixedTarget(playerId));
+                        effect.apply(game, source);
+                    }
+                }
+            }
+
+            // prevent undo
+            controller.resetStoredBookmark(game);
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/Weatherlight.java b/Mage.Sets/src/mage/sets/Weatherlight.java
index 5f906dddc8..5112b953c6 100644
--- a/Mage.Sets/src/mage/sets/Weatherlight.java
+++ b/Mage.Sets/src/mage/sets/Weatherlight.java
@@ -66,7 +66,7 @@ public final class Weatherlight extends ExpansionSet {
         cards.add(new SetCardInfo("Cinder Giant", 93, Rarity.UNCOMMON, mage.cards.c.CinderGiant.class));
         cards.add(new SetCardInfo("Cinder Wall", 94, Rarity.COMMON, mage.cards.c.CinderWall.class));
         cards.add(new SetCardInfo("Cloud Djinn", 36, Rarity.UNCOMMON, mage.cards.c.CloudDjinn.class));
-        cards.add(new SetCardInfo("Coils of the Medusa", 65, Rarity.COMMON, mage.cards.c.CoilsOfTheMedusa.class));
+        cards.add(new SetCardInfo("Coils of the Medusa", 65, Rarity.COMMON, mage.cards.c.CoilsOfTheMedusa.class));
         cards.add(new SetCardInfo("Cone of Flame", 95, Rarity.UNCOMMON, mage.cards.c.ConeOfFlame.class));
         cards.add(new SetCardInfo("Debt of Loyalty", 11, Rarity.RARE, mage.cards.d.DebtOfLoyalty.class));
         cards.add(new SetCardInfo("Dense Foliage", 124, Rarity.RARE, mage.cards.d.DenseFoliage.class));
@@ -114,6 +114,7 @@ public final class Weatherlight extends ExpansionSet {
         cards.add(new SetCardInfo("Kithkin Armor", 19, Rarity.COMMON, mage.cards.k.KithkinArmor.class));
         cards.add(new SetCardInfo("Lava Hounds", 109, Rarity.UNCOMMON, mage.cards.l.LavaHounds.class));
         cards.add(new SetCardInfo("Lava Storm", 110, Rarity.COMMON, mage.cards.l.LavaStorm.class));
+        cards.add(new SetCardInfo("Liege of the Hollows", 131, Rarity.RARE, mage.cards.l.LiegeOfTheHollows.class));
         cards.add(new SetCardInfo("Llanowar Behemoth", 132, Rarity.UNCOMMON, mage.cards.l.LlanowarBehemoth.class));
         cards.add(new SetCardInfo("Llanowar Druid", 133, Rarity.COMMON, mage.cards.l.LlanowarDruid.class));
         cards.add(new SetCardInfo("Llanowar Sentinel", 134, Rarity.COMMON, mage.cards.l.LlanowarSentinel.class));

From d1cbfc51e11c7217bf956ee8448c11a2629892e9 Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Mon, 29 Jun 2020 10:51:34 -0500
Subject: [PATCH 558/586] - Refactored DiesTriggeredAbility to
 DiesSourceTriggeredAbility

---
 .../src/mage/cards/a/AbnormalEndurance.java   |  4 +-
 Mage.Sets/src/mage/cards/a/AbuJafar.java      |  4 +-
 .../src/mage/cards/a/AbyssalGatekeeper.java   |  4 +-
 .../src/mage/cards/a/AbzanSkycaptain.java     |  4 +-
 Mage.Sets/src/mage/cards/a/AcademyRector.java |  4 +-
 Mage.Sets/src/mage/cards/a/AccursedWitch.java |  4 +-
 .../src/mage/cards/a/AkkiBlizzardHerder.java  |  4 +-
 .../src/mage/cards/a/AlabasterDragon.java     |  4 +-
 .../src/mage/cards/a/AncientStoneIdol.java    |  4 +-
 Mage.Sets/src/mage/cards/a/AngelOfFury.java   |  4 +-
 Mage.Sets/src/mage/cards/a/AnodetLurker.java  |  4 +-
 .../src/mage/cards/a/AphettoVulture.java      |  4 +-
 .../src/mage/cards/a/ArashinSovereign.java    |  4 +-
 .../mage/cards/a/ArchonOfFallingStars.java    |  4 +-
 .../src/mage/cards/a/ArchonOfJustice.java     |  4 +-
 Mage.Sets/src/mage/cards/a/ArcticNishoba.java |  4 +-
 Mage.Sets/src/mage/cards/a/ArenaRector.java   |  4 +-
 .../src/mage/cards/a/AshcloudPhoenix.java     |  4 +-
 .../src/mage/cards/a/AshenSkinZubera.java     |  4 +-
 .../src/mage/cards/a/AttendantOfVraska.java   |  4 +-
 Mage.Sets/src/mage/cards/a/AuraThief.java     |  4 +-
 .../src/mage/cards/a/AuspiciousAncestor.java  |  4 +-
 Mage.Sets/src/mage/cards/a/AvenFisher.java    |  4 +-
 Mage.Sets/src/mage/cards/a/AvengingAngel.java |  4 +-
 .../src/mage/cards/b/BantSojourners.java      |  4 +-
 Mage.Sets/src/mage/cards/b/Barishi.java       |  4 +-
 Mage.Sets/src/mage/cards/b/BarteredCow.java   |  4 +-
 .../src/mage/cards/b/BearerOfTheHeavens.java  |  4 +-
 Mage.Sets/src/mage/cards/b/BibFortuna.java    |  4 +-
 .../src/mage/cards/b/BitterheartWitch.java    |  4 +-
 Mage.Sets/src/mage/cards/b/BlackCat.java      |  4 +-
 Mage.Sets/src/mage/cards/b/BlazingEffigy.java |  4 +-
 Mage.Sets/src/mage/cards/b/Blistergrub.java   |  4 +-
 Mage.Sets/src/mage/cards/b/Blisterpod.java    |  4 +-
 Mage.Sets/src/mage/cards/b/BodySnatcher.java  |  4 +-
 .../src/mage/cards/b/BogardanFirefiend.java   |  4 +-
 .../src/mage/cards/b/BogardanPhoenix.java     |  4 +-
 Mage.Sets/src/mage/cards/b/BrindleShoat.java  |  4 +-
 Mage.Sets/src/mage/cards/b/BrineHag.java      |  4 +-
 .../src/mage/cards/b/BronzehideLion.java      |  4 +-
 .../src/mage/cards/b/BurningEyeZubera.java    |  4 +-
 Mage.Sets/src/mage/cards/b/BywayCourier.java  |  4 +-
 Mage.Sets/src/mage/cards/c/CanopyStalker.java |  4 +-
 .../src/mage/cards/c/CarelessCelebrant.java   |  4 +-
 Mage.Sets/src/mage/cards/c/CarrierThrall.java |  4 +-
 Mage.Sets/src/mage/cards/c/CarrionThrash.java |  4 +-
 Mage.Sets/src/mage/cards/c/Cathodion.java     |  4 +-
 Mage.Sets/src/mage/cards/c/CausticHound.java  |  4 +-
 .../src/mage/cards/c/CavalierOfDawn.java      |  4 +-
 .../src/mage/cards/c/CavalierOfFlame.java     |  4 +-
 .../src/mage/cards/c/CavalierOfGales.java     |  4 +-
 .../src/mage/cards/c/CavalierOfNight.java     |  4 +-
 .../src/mage/cards/c/CavalierOfThorns.java    |  4 +-
 .../src/mage/cards/c/CelestialGatekeeper.java |  4 +-
 .../src/mage/cards/c/CentaurSafeguard.java    |  4 +-
 Mage.Sets/src/mage/cards/c/ChasmSkulker.java  |  4 +-
 .../src/mage/cards/c/CherishedHatchling.java  |  4 +-
 Mage.Sets/src/mage/cards/c/ChildOfAlara.java  |  4 +-
 Mage.Sets/src/mage/cards/c/ChimneyImp.java    |  4 +-
 Mage.Sets/src/mage/cards/c/Chronozoa.java     |  4 +-
 .../src/mage/cards/c/CitywatchSphinx.java     |  4 +-
 Mage.Sets/src/mage/cards/c/CloneShell.java    |  4 +-
 .../src/mage/cards/c/ConclaveCavalier.java    |  4 +-
 .../src/mage/cards/c/ConclaveMentor.java      |  4 +-
 Mage.Sets/src/mage/cards/c/CoreProwler.java   |  4 +-
 Mage.Sets/src/mage/cards/c/CorpseAugur.java   |  4 +-
 Mage.Sets/src/mage/cards/c/CycloneSire.java   |  4 +-
 .../src/mage/cards/c/CyclopeanGiant.java      |  4 +-
 .../src/mage/cards/c/CyclopeanMummy.java      |  4 +-
 Mage.Sets/src/mage/cards/d/DarkRevenant.java  |  4 +-
 .../src/mage/cards/d/DarkslickDrake.java      |  4 +-
 .../src/mage/cards/d/DeadbridgeShaman.java    |  4 +-
 Mage.Sets/src/mage/cards/d/DeadlyGrub.java    |  4 +-
 .../src/mage/cards/d/DeathbloomThallid.java   |  4 +-
 .../src/mage/cards/d/DeathcurseOgre.java      |  4 +-
 .../src/mage/cards/d/DeathpactAngel.java      |  4 +-
 .../src/mage/cards/d/DeathsHeadBuzzard.java   |  4 +-
 .../src/mage/cards/d/DesperateSentry.java     |  4 +-
 .../src/mage/cards/d/DestructorDragon.java    |  4 +-
 .../src/mage/cards/d/DireFleetHoarder.java    |  4 +-
 .../src/mage/cards/d/DiscordantPiper.java     |  4 +-
 .../src/mage/cards/d/DiseaseCarriers.java     |  4 +-
 .../src/mage/cards/d/DoomedDissenter.java     |  4 +-
 .../src/mage/cards/d/DoomedTraveler.java      |  4 +-
 Mage.Sets/src/mage/cards/d/DragonEgg.java     |  4 +-
 .../src/mage/cards/d/DrainpipeVermin.java     |  4 +-
 .../src/mage/cards/d/DreadhordeButcher.java   |  4 +-
 .../mage/cards/d/DrippingTongueZubera.java    |  4 +-
 .../src/mage/cards/d/DriverOfTheDead.java     |  4 +-
 Mage.Sets/src/mage/cards/d/DroidCommando.java |  4 +-
 Mage.Sets/src/mage/cards/d/DuskUrchins.java   |  4 +-
 .../src/mage/cards/d/DutifulAttendant.java    |  4 +-
 Mage.Sets/src/mage/cards/e/ElderCathar.java   |  4 +-
 .../src/mage/cards/e/ElendaTheDuskRose.java   |  4 +-
 .../src/mage/cards/e/ElgaudInquisitor.java    |  4 +-
 .../src/mage/cards/e/ElvishSoultiller.java    |  4 +-
 .../src/mage/cards/e/EmberFistZubera.java     |  4 +-
 .../src/mage/cards/e/EmielTheBlessed.java     |  8 +-
 Mage.Sets/src/mage/cards/e/EnatuGolem.java    |  4 +-
 .../src/mage/cards/e/EndlessCockroaches.java  |  4 +-
 .../src/mage/cards/e/EndlessWhispers.java     |  4 +-
 Mage.Sets/src/mage/cards/e/Epochrasite.java   |  4 +-
 .../src/mage/cards/e/EsperSojourners.java     |  4 +-
 Mage.Sets/src/mage/cards/e/ExiledBoggart.java |  4 +-
 .../src/mage/cards/e/ExultantCultist.java     |  4 +-
 Mage.Sets/src/mage/cards/f/FalseProphet.java  |  4 +-
 Mage.Sets/src/mage/cards/f/FeralProwler.java  |  4 +-
 .../src/mage/cards/f/FesteringGoblin.java     |  4 +-
 .../src/mage/cards/f/FesteringMummy.java      |  4 +-
 Mage.Sets/src/mage/cards/f/FesteringNewt.java |  4 +-
 .../src/mage/cards/f/FiligreeCrawler.java     |  4 +-
 .../src/mage/cards/f/FiligreeFamiliar.java    |  4 +-
 Mage.Sets/src/mage/cards/f/FireSnake.java     |  4 +-
 .../mage/cards/f/FlameWreathedPhoenix.java    |  4 +-
 Mage.Sets/src/mage/cards/f/FleshCarver.java   |  4 +-
 .../src/mage/cards/f/FlightSpellbomb.java     |  4 +-
 .../src/mage/cards/f/FloatingDreamZubera.java |  4 +-
 .../src/mage/cards/f/FootlightFiend.java      |  4 +-
 .../src/mage/cards/f/ForsakenDrifters.java    |  4 +-
 Mage.Sets/src/mage/cards/f/FyndhornDruid.java |  4 +-
 Mage.Sets/src/mage/cards/g/Gamekeeper.java    |  4 +-
 Mage.Sets/src/mage/cards/g/GangOfDevils.java  |  4 +-
 Mage.Sets/src/mage/cards/g/GarrisonCat.java   |  4 +-
 .../mage/cards/g/GerrardWeatherlightHero.java |  4 +-
 .../src/mage/cards/g/GiantAlbatross.java      |  4 +-
 .../src/mage/cards/g/GleamingBarrier.java     |  4 +-
 .../src/mage/cards/g/GoblinArsonist.java      |  4 +-
 .../src/mage/cards/g/GoblinAssaultTeam.java   |  4 +-
 .../src/mage/cards/g/GoblinGardener.java      |  4 +-
 Mage.Sets/src/mage/cards/g/GoblinMasons.java  |  4 +-
 Mage.Sets/src/mage/cards/g/GolgariThug.java   |  4 +-
 Mage.Sets/src/mage/cards/g/Greedo.java        |  4 +-
 .../src/mage/cards/g/GreenwardenOfMurasa.java |  4 +-
 Mage.Sets/src/mage/cards/g/GriefTyrant.java   |  4 +-
 Mage.Sets/src/mage/cards/g/GrimInitiate.java  |  4 +-
 Mage.Sets/src/mage/cards/g/GrimPhysician.java |  4 +-
 .../src/mage/cards/g/GrixisSojourners.java    |  4 +-
 .../mage/cards/g/GuanYuSaintedWarrior.java    |  4 +-
 .../src/mage/cards/g/GuardianAutomaton.java   |  4 +-
 .../mage/cards/h/HaakonStromgaldScourge.java  |  4 +-
 .../mage/cards/h/HallowedSpiritkeeper.java    |  4 +-
 .../src/mage/cards/h/HangarbackWalker.java    |  4 +-
 Mage.Sets/src/mage/cards/h/HarvestHand.java   |  4 +-
 Mage.Sets/src/mage/cards/h/HauntedAngel.java  |  4 +-
 Mage.Sets/src/mage/cards/h/HavocDemon.java    |  4 +-
 Mage.Sets/src/mage/cards/h/Helvault.java      |  4 +-
 .../mage/cards/h/HeraldOfTheDreadhorde.java   |  4 +-
 Mage.Sets/src/mage/cards/h/HighlandGame.java  |  4 +-
 .../src/mage/cards/h/HoardingDragon.java      |  4 +-
 .../mage/cards/h/HomuraHumanAscendant.java    |  4 +-
 Mage.Sets/src/mage/cards/h/HoodedHydra.java   |  4 +-
 .../src/mage/cards/h/HorizonSpellbomb.java    |  4 +-
 .../src/mage/cards/h/HornetHarasser.java      |  4 +-
 Mage.Sets/src/mage/cards/h/HuntedWitness.java |  4 +-
 Mage.Sets/src/mage/cards/h/HurloonShaman.java |  4 +-
 .../src/mage/cards/i/ImmortalPhoenix.java     |  4 +-
 Mage.Sets/src/mage/cards/i/InameAsOne.java    |  4 +-
 .../src/mage/cards/i/InameLifeAspect.java     |  4 +-
 .../src/mage/cards/i/InfectiousHost.java      |  4 +-
 .../src/mage/cards/i/InfernalScarring.java    |  4 +-
 .../src/mage/cards/i/InsidiousBookworms.java  |  4 +-
 Mage.Sets/src/mage/cards/i/IvoryGargoyle.java |  4 +-
 Mage.Sets/src/mage/cards/j/JeskaiSage.java    |  4 +-
 .../src/mage/cards/j/JotunOwlKeeper.java      |  4 +-
 .../src/mage/cards/j/JuganTheRisingStar.java  |  4 +-
 .../src/mage/cards/j/JundSojourners.java      |  4 +-
 Mage.Sets/src/mage/cards/j/JunkDiver.java     |  4 +-
 .../src/mage/cards/k/KeigaTheTideStar.java    |  4 +-
 Mage.Sets/src/mage/cards/k/Kingfisher.java    |  4 +-
 .../mage/cards/k/KinsbaileBorderguard.java    |  4 +-
 .../mage/cards/k/KokushoTheEveningStar.java   |  4 +-
 Mage.Sets/src/mage/cards/l/LawlessBroker.java |  4 +-
 .../mage/cards/l/LeoninOfTheLostPride.java    |  4 +-
 .../src/mage/cards/l/LiegeOfTheHollows.java   |  4 +-
 .../src/mage/cards/l/LifebloodHydra.java      |  4 +-
 .../src/mage/cards/l/LightOfTheLegion.java    |  4 +-
 .../src/mage/cards/l/LivingLightning.java     |  4 +-
 .../src/mage/cards/l/LoathsomeCatoblepas.java |  4 +-
 .../src/mage/cards/l/LockjawSnapper.java      |  4 +-
 Mage.Sets/src/mage/cards/l/LoyalCathar.java   |  4 +-
 Mage.Sets/src/mage/cards/m/MaalfeldTwins.java |  4 +-
 Mage.Sets/src/mage/cards/m/MagmaPhoenix.java  |  4 +-
 Mage.Sets/src/mage/cards/m/MarkerBeetles.java |  4 +-
 .../src/mage/cards/m/MartyrForTheCause.java   |  4 +-
 Mage.Sets/src/mage/cards/m/MartyrOfDusk.java  |  4 +-
 .../src/mage/cards/m/MathasFiendSeeker.java   |  4 +-
 .../src/mage/cards/m/MatterReshaper.java      |  4 +-
 .../src/mage/cards/m/MausoleumGuard.java      |  4 +-
 Mage.Sets/src/mage/cards/m/MerfolkSeer.java   |  4 +-
 .../src/mage/cards/m/MessengerDrake.java      |  4 +-
 Mage.Sets/src/mage/cards/m/MindeyeDrake.java  |  4 +-
 Mage.Sets/src/mage/cards/m/Mindslicer.java    |  4 +-
 .../src/mage/cards/m/MistmoonGriffin.java     |  4 +-
 Mage.Sets/src/mage/cards/m/MitoticSlime.java  |  4 +-
 .../src/mage/cards/m/MoldgrafMonstrosity.java |  4 +-
 .../src/mage/cards/m/MoltenFirebird.java      |  4 +-
 Mage.Sets/src/mage/cards/m/MortisDogs.java    |  4 +-
 Mage.Sets/src/mage/cards/m/MortusStrider.java |  4 +-
 Mage.Sets/src/mage/cards/m/MouseDroid.java    |  4 +-
 .../mage/cards/m/MudbuttonTorchrunner.java    |  4 +-
 .../src/mage/cards/m/MurderousRider.java      |  4 +-
 .../src/mage/cards/m/MycosynthWellspring.java |  4 +-
 Mage.Sets/src/mage/cards/m/MyrMoonvessel.java |  4 +-
 Mage.Sets/src/mage/cards/m/MyrRetriever.java  |  4 +-
 Mage.Sets/src/mage/cards/m/MyrSire.java       |  4 +-
 .../src/mage/cards/n/NayaSojourners.java      |  4 +-
 Mage.Sets/src/mage/cards/n/Necropede.java     |  4 +-
 .../src/mage/cards/n/NihilSpellbomb.java      |  4 +-
 .../src/mage/cards/n/NobleBenefactor.java     |  4 +-
 .../src/mage/cards/n/NocturnalFeeder.java     |  4 +-
 Mage.Sets/src/mage/cards/n/NoxiousDragon.java |  4 +-
 Mage.Sets/src/mage/cards/n/NoxiousToad.java   |  4 +-
 Mage.Sets/src/mage/cards/o/Oculus.java        |  4 +-
 Mage.Sets/src/mage/cards/o/Onulet.java        |  4 +-
 .../src/mage/cards/o/OrcishHellraiser.java    |  4 +-
 .../src/mage/cards/o/OriginSpellbomb.java     |  4 +-
 .../src/mage/cards/p/PalaceFamiliar.java      |  4 +-
 .../src/mage/cards/p/PaladinOfAtonement.java  |  4 +-
 Mage.Sets/src/mage/cards/p/PelakkaWurm.java   |  4 +-
 .../src/mage/cards/p/PenumbraBobcat.java      |  4 +-
 Mage.Sets/src/mage/cards/p/PenumbraKavu.java  |  4 +-
 .../src/mage/cards/p/PenumbraSpider.java      |  4 +-
 Mage.Sets/src/mage/cards/p/PenumbraWurm.java  |  4 +-
 Mage.Sets/src/mage/cards/p/PerilousMyr.java   |  4 +-
 .../src/mage/cards/p/PersonalIncarnation.java |  4 +-
 Mage.Sets/src/mage/cards/p/Phytotitan.java    |  4 +-
 .../src/mage/cards/p/PitchburnDevils.java     |  4 +-
 Mage.Sets/src/mage/cards/p/PlagueDogs.java    |  4 +-
 Mage.Sets/src/mage/cards/p/PlagueSpitter.java |  4 +-
 Mage.Sets/src/mage/cards/p/PollutedDead.java  |  4 +-
 Mage.Sets/src/mage/cards/p/PrimalDruid.java   |  4 +-
 Mage.Sets/src/mage/cards/p/ProteanHulk.java   |  4 +-
 .../src/mage/cards/p/PurpleCrystalCrab.java   |  4 +-
 Mage.Sets/src/mage/cards/r/RebornHero.java    |  4 +-
 Mage.Sets/src/mage/cards/r/ReefWorm.java      |  4 +-
 .../src/mage/cards/r/RekindlingPhoenix.java   |  4 +-
 .../src/mage/cards/r/RelentlessDead.java      |  6 +-
 Mage.Sets/src/mage/cards/r/ReliquaryMonk.java |  4 +-
 .../src/mage/cards/r/ReturnedReveler.java     |  4 +-
 Mage.Sets/src/mage/cards/r/RiptideCrab.java   |  4 +-
 .../src/mage/cards/r/RoaleskApexHybrid.java   |  4 +-
 Mage.Sets/src/mage/cards/r/RocEgg.java        |  4 +-
 Mage.Sets/src/mage/cards/r/RootingKavu.java   |  4 +-
 Mage.Sets/src/mage/cards/r/RotcrownGhoul.java |  4 +-
 .../src/mage/cards/r/RottenheartGhoul.java    |  4 +-
 Mage.Sets/src/mage/cards/r/RuinRat.java       |  4 +-
 .../src/mage/cards/r/RuinationRioter.java     |  4 +-
 Mage.Sets/src/mage/cards/r/RukhEgg.java       |  4 +-
 Mage.Sets/src/mage/cards/r/RunedServitor.java |  4 +-
 Mage.Sets/src/mage/cards/r/Runewing.java      |  4 +-
 .../src/mage/cards/r/RushingTideZubera.java   |  4 +-
 .../mage/cards/r/RyuseiTheFallingStar.java    |  4 +-
 .../src/mage/cards/s/SadisticAugermage.java   |  4 +-
 Mage.Sets/src/mage/cards/s/SalvageDrone.java  |  4 +-
 .../src/mage/cards/s/ScreechingBuzzard.java   |  4 +-
 .../src/mage/cards/s/ScuttlingDoomEngine.java |  4 +-
 Mage.Sets/src/mage/cards/s/SeedGuardian.java  |  4 +-
 Mage.Sets/src/mage/cards/s/SeedguideAsh.java  |  4 +-
 .../src/mage/cards/s/SellSwordBrute.java      |  4 +-
 .../src/mage/cards/s/SerratedScorpion.java    |  4 +-
 Mage.Sets/src/mage/cards/s/SerumRaker.java    |  4 +-
 .../src/mage/cards/s/ServantOfTheScale.java   |  4 +-
 Mage.Sets/src/mage/cards/s/ShaakHerd.java     |  4 +-
 .../src/mage/cards/s/ShamblingGoblin.java     |  4 +-
 .../src/mage/cards/s/ShamblingSwarm.java      |  4 +-
 Mage.Sets/src/mage/cards/s/ShivanPhoenix.java |  4 +-
 Mage.Sets/src/mage/cards/s/Showstopper.java   |  4 +-
 .../src/mage/cards/s/SilentChantZubera.java   |  4 +-
 .../src/mage/cards/s/SilverbackShaman.java    |  4 +-
 .../mage/cards/s/SlaughterhouseBouncer.java   |  4 +-
 .../src/mage/cards/s/SmolderingEfreet.java    |  4 +-
 .../src/mage/cards/s/SolemnSimulacrum.java    |  4 +-
 Mage.Sets/src/mage/cards/s/SoulcageFiend.java |  4 +-
 Mage.Sets/src/mage/cards/s/Soulstinger.java   |  4 +-
 .../src/mage/cards/s/SparringConstruct.java   |  4 +-
 .../src/mage/cards/s/SpinalCentipede.java     |  4 +-
 .../src/mage/cards/s/SpiritOfMalevolence.java |  4 +-
 .../src/mage/cards/s/SproutingThrinax.java    |  4 +-
 .../src/mage/cards/s/SteadfastSentry.java     |  4 +-
 Mage.Sets/src/mage/cards/s/SuChi.java         |  4 +-
 .../src/mage/cards/s/SultaiEmissary.java      |  4 +-
 Mage.Sets/src/mage/cards/s/SummonersEgg.java  |  4 +-
 .../src/mage/cards/s/SupernaturalStamina.java |  4 +-
 .../src/mage/cards/s/SurveillingSprite.java   |  4 +-
 .../src/mage/cards/s/SylvanHierophant.java    |  4 +-
 .../src/mage/cards/s/SymbioticBeast.java      |  4 +-
 Mage.Sets/src/mage/cards/s/SymbioticElf.java  |  4 +-
 Mage.Sets/src/mage/cards/s/SymbioticWurm.java |  4 +-
 Mage.Sets/src/mage/cards/t/Tarpan.java        |  4 +-
 Mage.Sets/src/mage/cards/t/TatteredMummy.java |  4 +-
 Mage.Sets/src/mage/cards/t/TenaciousDead.java |  4 +-
 Mage.Sets/src/mage/cards/t/TheLocustGod.java  |  4 +-
 Mage.Sets/src/mage/cards/t/TheScarabGod.java  |  4 +-
 .../src/mage/cards/t/TheScorpionGod.java      |  4 +-
 Mage.Sets/src/mage/cards/t/ThoughtSponge.java |  4 +-
 Mage.Sets/src/mage/cards/t/TibaltsRager.java  |  4 +-
 .../src/mage/cards/t/TreacherousVampire.java  |  4 +-
 .../src/mage/cards/t/TreacherousWerewolf.java |  4 +-
 .../src/mage/cards/t/TreasureKeeper.java      |  4 +-
 .../src/mage/cards/t/TreeshakerChimera.java   |  4 +-
 .../src/mage/cards/t/TukatongueThallid.java   |  4 +-
 .../src/mage/cards/t/TuktukTheExplorer.java   |  4 +-
 .../src/mage/cards/u/UndeadExecutioner.java   |  4 +-
 .../src/mage/cards/u/UnderworldCerberus.java  |  4 +-
 .../src/mage/cards/u/UnderworldSentinel.java  |  4 +-
 Mage.Sets/src/mage/cards/u/UndyingBeast.java  |  4 +-
 Mage.Sets/src/mage/cards/v/VastwoodHydra.java |  4 +-
 .../src/mage/cards/v/VenerableKnight.java     |  4 +-
 .../src/mage/cards/v/VengeantVampire.java     |  4 +-
 .../src/mage/cards/v/VerdantRebirth.java      |  4 +-
 .../src/mage/cards/v/VeteranExplorer.java     |  4 +-
 Mage.Sets/src/mage/cards/v/VexingSphinx.java  |  4 +-
 .../src/mage/cards/v/VindictiveLich.java      |  4 +-
 .../src/mage/cards/v/ViridianEmissary.java    |  4 +-
 .../src/mage/cards/v/VodalianWarMachine.java  |  4 +-
 .../src/mage/cards/v/VoiceOfResurgence.java   |  4 +-
 Mage.Sets/src/mage/cards/v/VolatileRig.java   |  4 +-
 .../src/mage/cards/w/WantedScoundrels.java    |  4 +-
 .../src/mage/cards/w/WeatherseedTreefolk.java |  4 +-
 Mage.Sets/src/mage/cards/w/WelkinHawk.java    |  4 +-
 .../src/mage/cards/w/WhirlpoolDrake.java      |  4 +-
 .../mage/cards/w/WhisperwoodElemental.java    |  4 +-
 .../src/mage/cards/w/WirewoodHerald.java      |  4 +-
 .../src/mage/cards/w/WorkshopAssistant.java   |  4 +-
 Mage.Sets/src/mage/cards/w/WorldShaper.java   |  4 +-
 .../src/mage/cards/w/WorldspineWurm.java      |  4 +-
 Mage.Sets/src/mage/cards/w/WretchedCamel.java |  4 +-
 .../src/mage/cards/w/WurmcoilEngine.java      |  4 +-
 Mage.Sets/src/mage/cards/y/YavimayaElder.java |  4 +-
 .../src/mage/cards/y/YoseiTheMorningStar.java |  4 +-
 .../src/mage/cards/y/YouthfulScholar.java     |  4 +-
 Mage.Sets/src/mage/cards/z/ZodiacDragon.java  |  4 +-
 .../common/DiesSourceTriggeredAbility.java    | 79 +++++++++++++++++++
 ...tlefieldUnderOwnerControlTargetEffect.java |  2 +-
 .../abilities/keyword/AfflictAbility.java     |  4 +-
 .../abilities/keyword/AfterlifeAbility.java   |  4 +-
 .../keyword/CumulativeUpkeepAbility.java      |  1 -
 .../abilities/keyword/ModularAbility.java     |  4 +-
 .../mage/abilities/keyword/MorphAbility.java  |  3 +-
 .../abilities/keyword/PersistAbility.java     |  4 +-
 .../abilities/keyword/RampageAbility.java     |  4 +-
 .../mage/abilities/keyword/RepairAbility.java |  4 +-
 .../abilities/keyword/SoulshiftAbility.java   |  6 +-
 .../abilities/keyword/UndyingAbility.java     |  6 +-
 .../mage/game/permanent/token/ATATToken.java  |  4 +-
 .../mage/game/permanent/token/DevilToken.java |  4 +-
 .../permanent/token/FesteringGoblinToken.java |  4 +-
 .../token/GarrukCursedHuntsmanToken.java      |  4 +-
 .../permanent/token/NestingDragonToken.java   |  4 +-
 .../mage/game/permanent/token/Ooze2Token.java |  4 +-
 .../permanent/token/ReefWormFishToken.java    |  4 +-
 .../permanent/token/ReefWormWhaleToken.java   |  4 +-
 .../permanent/token/WolfsQuarryToken.java     |  4 +-
 353 files changed, 786 insertions(+), 705 deletions(-)
 create mode 100644 Mage/src/main/java/mage/abilities/common/DiesSourceTriggeredAbility.java

diff --git a/Mage.Sets/src/mage/cards/a/AbnormalEndurance.java b/Mage.Sets/src/mage/cards/a/AbnormalEndurance.java
index 847823acad..1aa6c11826 100644
--- a/Mage.Sets/src/mage/cards/a/AbnormalEndurance.java
+++ b/Mage.Sets/src/mage/cards/a/AbnormalEndurance.java
@@ -1,7 +1,7 @@
 package mage.cards.a;
 
 import java.util.UUID;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect;
 import mage.abilities.effects.common.continuous.BoostTargetEffect;
 import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
@@ -26,7 +26,7 @@ public final class AbnormalEndurance extends CardImpl {
                 .setText("Until end of turn, target creature gets +2/+0")
         );
         getSpellAbility().addEffect(new GainAbilityTargetEffect(
-                new DiesTriggeredAbility(new ReturnSourceFromGraveyardToBattlefieldEffect(true, true), false),
+                new DiesSourceTriggeredAbility(new ReturnSourceFromGraveyardToBattlefieldEffect(true, true), false),
                 Duration.EndOfTurn,
                 "and gains \"When this creature dies, return it to the battlefield tapped under its owner's control.\""
         ));
diff --git a/Mage.Sets/src/mage/cards/a/AbuJafar.java b/Mage.Sets/src/mage/cards/a/AbuJafar.java
index 1435c02087..b83054c00e 100644
--- a/Mage.Sets/src/mage/cards/a/AbuJafar.java
+++ b/Mage.Sets/src/mage/cards/a/AbuJafar.java
@@ -3,7 +3,7 @@ package mage.cards.a;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DestroyAllEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -31,7 +31,7 @@ public final class AbuJafar extends CardImpl {
                                  new BlockingAttackerIdPredicate(this.getId())));
         
         // When Abu Ja'far dies, destroy all creatures blocking or blocked by it. They can't be regenerated.        
-        this.addAbility(new DiesTriggeredAbility(new DestroyAllEffect(filter, true), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new DestroyAllEffect(filter, true), false));
     }
 
     public AbuJafar(final AbuJafar card) {
diff --git a/Mage.Sets/src/mage/cards/a/AbyssalGatekeeper.java b/Mage.Sets/src/mage/cards/a/AbyssalGatekeeper.java
index cb8017787d..2219dcb20e 100644
--- a/Mage.Sets/src/mage/cards/a/AbyssalGatekeeper.java
+++ b/Mage.Sets/src/mage/cards/a/AbyssalGatekeeper.java
@@ -3,7 +3,7 @@ package mage.cards.a;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.SacrificeAllEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -24,7 +24,7 @@ public final class AbyssalGatekeeper extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Abyssal Gatekeeper dies, each player sacrifices a creature.
-        this.addAbility(new DiesTriggeredAbility(new SacrificeAllEffect(1, new FilterControlledCreaturePermanent("creature"))));
+        this.addAbility(new DiesSourceTriggeredAbility(new SacrificeAllEffect(1, new FilterControlledCreaturePermanent("creature"))));
     }
 
     public AbyssalGatekeeper(final AbyssalGatekeeper card) {
diff --git a/Mage.Sets/src/mage/cards/a/AbzanSkycaptain.java b/Mage.Sets/src/mage/cards/a/AbzanSkycaptain.java
index 72dd6ba011..50faba04db 100644
--- a/Mage.Sets/src/mage/cards/a/AbzanSkycaptain.java
+++ b/Mage.Sets/src/mage/cards/a/AbzanSkycaptain.java
@@ -3,7 +3,7 @@ package mage.cards.a;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.keyword.BolsterEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
@@ -27,7 +27,7 @@ public final class AbzanSkycaptain extends CardImpl {
         // Flying
         this.addAbility(FlyingAbility.getInstance());
         // When Abzan Captain dies, bolster 2.
-        this.addAbility(new DiesTriggeredAbility(new BolsterEffect(2)));
+        this.addAbility(new DiesSourceTriggeredAbility(new BolsterEffect(2)));
     }
 
     public AbzanSkycaptain(final AbzanSkycaptain card) {
diff --git a/Mage.Sets/src/mage/cards/a/AcademyRector.java b/Mage.Sets/src/mage/cards/a/AcademyRector.java
index e20df2f5ed..2c519616d3 100644
--- a/Mage.Sets/src/mage/cards/a/AcademyRector.java
+++ b/Mage.Sets/src/mage/cards/a/AcademyRector.java
@@ -3,7 +3,7 @@ package mage.cards.a;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.costs.common.ExileSourceFromGraveCost;
 import mage.abilities.effects.common.DoIfCostPaid;
 import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect;
@@ -29,7 +29,7 @@ public final class AcademyRector extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Academy Rector dies, you may exile it. If you do, search your library for an enchantment card, put that card onto the battlefield, then shuffle your library.
-        this.addAbility(new DiesTriggeredAbility(
+        this.addAbility(new DiesSourceTriggeredAbility(
                 new DoIfCostPaid(
                         new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(new FilterEnchantmentCard())),
                         new ExileSourceFromGraveCost(),
diff --git a/Mage.Sets/src/mage/cards/a/AccursedWitch.java b/Mage.Sets/src/mage/cards/a/AccursedWitch.java
index aba9e8a30e..497724b2b6 100644
--- a/Mage.Sets/src/mage/cards/a/AccursedWitch.java
+++ b/Mage.Sets/src/mage/cards/a/AccursedWitch.java
@@ -5,7 +5,7 @@ import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.Mode;
 import mage.abilities.SpellAbility;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.cost.CostModificationEffectImpl;
@@ -42,7 +42,7 @@ public final class AccursedWitch extends CardImpl {
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new AccursedWitchSpellsCostReductionEffect()));
         // When Accursed Witch dies, return it to the battlefield transformed under your control attached to target opponent.
         this.addAbility(new TransformAbility());
-        Ability ability = new DiesTriggeredAbility(new AccursedWitchReturnTransformedEffect());
+        Ability ability = new DiesSourceTriggeredAbility(new AccursedWitchReturnTransformedEffect());
         ability.addTarget(new TargetOpponent());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/a/AkkiBlizzardHerder.java b/Mage.Sets/src/mage/cards/a/AkkiBlizzardHerder.java
index 41eb809c1d..03a3061252 100644
--- a/Mage.Sets/src/mage/cards/a/AkkiBlizzardHerder.java
+++ b/Mage.Sets/src/mage/cards/a/AkkiBlizzardHerder.java
@@ -3,7 +3,7 @@ package mage.cards.a;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.SacrificeAllEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -29,7 +29,7 @@ public final class AkkiBlizzardHerder extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Akki Blizzard-Herder dies, each player sacrifices a land.
-        this.addAbility(new DiesTriggeredAbility(new SacrificeAllEffect(filter)));
+        this.addAbility(new DiesSourceTriggeredAbility(new SacrificeAllEffect(filter)));
     }
 
     public AkkiBlizzardHerder(final AkkiBlizzardHerder card) {
diff --git a/Mage.Sets/src/mage/cards/a/AlabasterDragon.java b/Mage.Sets/src/mage/cards/a/AlabasterDragon.java
index 49843f304a..62fbda2d27 100644
--- a/Mage.Sets/src/mage/cards/a/AlabasterDragon.java
+++ b/Mage.Sets/src/mage/cards/a/AlabasterDragon.java
@@ -3,7 +3,7 @@ package mage.cards.a;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.ShuffleIntoLibrarySourceEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
@@ -26,7 +26,7 @@ public final class AlabasterDragon extends CardImpl {
         // Flying
         this.addAbility(FlyingAbility.getInstance());
         // When Alabaster Dragon dies, shuffle it into its owner's library.
-        this.addAbility(new DiesTriggeredAbility(new ShuffleIntoLibrarySourceEffect()));                                                                                          }
+        this.addAbility(new DiesSourceTriggeredAbility(new ShuffleIntoLibrarySourceEffect()));                                                                                          }
 
     public AlabasterDragon(final AlabasterDragon card) {
         super(card);
diff --git a/Mage.Sets/src/mage/cards/a/AncientStoneIdol.java b/Mage.Sets/src/mage/cards/a/AncientStoneIdol.java
index 499c85e265..095ca784ba 100644
--- a/Mage.Sets/src/mage/cards/a/AncientStoneIdol.java
+++ b/Mage.Sets/src/mage/cards/a/AncientStoneIdol.java
@@ -1,7 +1,7 @@
 package mage.cards.a;
 
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.dynamicvalue.DynamicValue;
 import mage.abilities.dynamicvalue.common.AttackingCreatureCount;
@@ -44,7 +44,7 @@ public final class AncientStoneIdol extends CardImpl {
         this.addAbility(TrampleAbility.getInstance());
 
         // When Ancient Stone Idol dies, create a 6/12 colorless Construct artifact creature token with trample.
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new StoneTrapIdolToken())));
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new StoneTrapIdolToken())));
     }
 
     public AncientStoneIdol(final AncientStoneIdol card) {
diff --git a/Mage.Sets/src/mage/cards/a/AngelOfFury.java b/Mage.Sets/src/mage/cards/a/AngelOfFury.java
index 9bbdb35252..e6f7115a0c 100644
--- a/Mage.Sets/src/mage/cards/a/AngelOfFury.java
+++ b/Mage.Sets/src/mage/cards/a/AngelOfFury.java
@@ -3,7 +3,7 @@ package mage.cards.a;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.ShuffleIntoLibrarySourceEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
@@ -26,7 +26,7 @@ public final class AngelOfFury extends CardImpl {
         // Flying
         this.addAbility(FlyingAbility.getInstance());
         // When Angel of Fury is put into your graveyard from the battlefield, you may shuffle it into your library.
-        this.addAbility(new DiesTriggeredAbility(new ShuffleIntoLibrarySourceEffect(), true));
+        this.addAbility(new DiesSourceTriggeredAbility(new ShuffleIntoLibrarySourceEffect(), true));
     }
 
     public AngelOfFury(final AngelOfFury card) {
diff --git a/Mage.Sets/src/mage/cards/a/AnodetLurker.java b/Mage.Sets/src/mage/cards/a/AnodetLurker.java
index ecd459aad4..983985f035 100644
--- a/Mage.Sets/src/mage/cards/a/AnodetLurker.java
+++ b/Mage.Sets/src/mage/cards/a/AnodetLurker.java
@@ -3,7 +3,7 @@ package mage.cards.a;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.GainLifeEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -24,7 +24,7 @@ public final class AnodetLurker extends CardImpl {
         this.toughness = new MageInt(3);
 
         // When Anodet Lurker dies, you gain 3 life.
-        this.addAbility(new DiesTriggeredAbility(new GainLifeEffect(3)));
+        this.addAbility(new DiesSourceTriggeredAbility(new GainLifeEffect(3)));
     }
 
     public AnodetLurker(final AnodetLurker card) {
diff --git a/Mage.Sets/src/mage/cards/a/AphettoVulture.java b/Mage.Sets/src/mage/cards/a/AphettoVulture.java
index f62b8b9f22..7096706082 100644
--- a/Mage.Sets/src/mage/cards/a/AphettoVulture.java
+++ b/Mage.Sets/src/mage/cards/a/AphettoVulture.java
@@ -4,7 +4,7 @@ package mage.cards.a;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.PutOnLibraryTargetEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
@@ -38,7 +38,7 @@ public final class AphettoVulture extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
 
         // When Aphetto Vulture dies, you may put target Zombie card from your graveyard on top of your library.
-        Ability ability = new DiesTriggeredAbility(new PutOnLibraryTargetEffect(true), true);
+        Ability ability = new DiesSourceTriggeredAbility(new PutOnLibraryTargetEffect(true), true);
         ability.addTarget(new TargetCardInYourGraveyard(filter));
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/a/ArashinSovereign.java b/Mage.Sets/src/mage/cards/a/ArashinSovereign.java
index 88676ac5f6..0aeac68a2d 100644
--- a/Mage.Sets/src/mage/cards/a/ArashinSovereign.java
+++ b/Mage.Sets/src/mage/cards/a/ArashinSovereign.java
@@ -4,7 +4,7 @@ package mage.cards.a;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.Card;
@@ -33,7 +33,7 @@ public final class ArashinSovereign extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
         
         // When Arashin Sovereign dies, you may put it on the top or bottom of its owner's library.
-        this.addAbility(new DiesTriggeredAbility(new ArashinSovereignEffect(), true));
+        this.addAbility(new DiesSourceTriggeredAbility(new ArashinSovereignEffect(), true));
     }
 
     public ArashinSovereign(final ArashinSovereign card) {
diff --git a/Mage.Sets/src/mage/cards/a/ArchonOfFallingStars.java b/Mage.Sets/src/mage/cards/a/ArchonOfFallingStars.java
index 0ac34127dd..0e32d82850 100644
--- a/Mage.Sets/src/mage/cards/a/ArchonOfFallingStars.java
+++ b/Mage.Sets/src/mage/cards/a/ArchonOfFallingStars.java
@@ -2,7 +2,7 @@ package mage.cards.a;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
@@ -33,7 +33,7 @@ public final class ArchonOfFallingStars extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
 
         // When Archon of Falling Stars dies, you may return target enchantment card from your graveyard to the battlefield.
-        Ability ability = new DiesTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect(false, false), true);
+        Ability ability = new DiesSourceTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect(false, false), true);
         ability.addTarget(new TargetCardInYourGraveyard(filter));
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/a/ArchonOfJustice.java b/Mage.Sets/src/mage/cards/a/ArchonOfJustice.java
index 755522cb19..c08485495b 100644
--- a/Mage.Sets/src/mage/cards/a/ArchonOfJustice.java
+++ b/Mage.Sets/src/mage/cards/a/ArchonOfJustice.java
@@ -4,7 +4,7 @@ package mage.cards.a;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.ExileTargetEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
@@ -27,7 +27,7 @@ public final class ArchonOfJustice extends CardImpl {
         this.toughness = new MageInt(4);
 
         this.addAbility(FlyingAbility.getInstance());
-        Ability ability = new DiesTriggeredAbility(new ExileTargetEffect(), false);
+        Ability ability = new DiesSourceTriggeredAbility(new ExileTargetEffect(), false);
         ability.addTarget(new TargetPermanent());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/a/ArcticNishoba.java b/Mage.Sets/src/mage/cards/a/ArcticNishoba.java
index 5425eed036..11fb4439dd 100644
--- a/Mage.Sets/src/mage/cards/a/ArcticNishoba.java
+++ b/Mage.Sets/src/mage/cards/a/ArcticNishoba.java
@@ -3,7 +3,7 @@ package mage.cards.a;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.costs.OrCost;
 import mage.constants.SubType;
 import mage.abilities.keyword.TrampleAbility;
@@ -45,7 +45,7 @@ public final class ArcticNishoba extends CardImpl {
         // When Arctic Nishoba dies, you gain 2 life for each age counter on it.
         Effect effect = new GainLifeEffect(new MultipliedValue(new CountersSourceCount(CounterType.AGE), 2));
         effect.setText("you gain 2 life for each age counter on it");
-        this.addAbility(new DiesTriggeredAbility(effect));
+        this.addAbility(new DiesSourceTriggeredAbility(effect));
     }
 
     public ArcticNishoba(final ArcticNishoba card) {
diff --git a/Mage.Sets/src/mage/cards/a/ArenaRector.java b/Mage.Sets/src/mage/cards/a/ArenaRector.java
index b1fa212eb2..53510af308 100644
--- a/Mage.Sets/src/mage/cards/a/ArenaRector.java
+++ b/Mage.Sets/src/mage/cards/a/ArenaRector.java
@@ -3,7 +3,7 @@ package mage.cards.a;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.costs.common.ExileSourceFromGraveCost;
 import mage.abilities.effects.common.DoIfCostPaid;
 import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect;
@@ -29,7 +29,7 @@ public final class ArenaRector extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Arena Rector dies, you may exile it. If you do, search your library for a planeswalker card, put it onto the battlefield, then shuffle your library.
-        this.addAbility(new DiesTriggeredAbility(
+        this.addAbility(new DiesSourceTriggeredAbility(
                 new DoIfCostPaid(
                         new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(new FilterPlaneswalkerCard())),
                         new ExileSourceFromGraveCost(),
diff --git a/Mage.Sets/src/mage/cards/a/AshcloudPhoenix.java b/Mage.Sets/src/mage/cards/a/AshcloudPhoenix.java
index c63e5d77ab..d4b74755af 100644
--- a/Mage.Sets/src/mage/cards/a/AshcloudPhoenix.java
+++ b/Mage.Sets/src/mage/cards/a/AshcloudPhoenix.java
@@ -4,7 +4,7 @@ package mage.cards.a;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.TurnedFaceUpSourceTriggeredAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.Effect;
@@ -36,7 +36,7 @@ public final class AshcloudPhoenix extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
 
         // When Ashcloud Phoenix dies, return it to the battlefield face down under your control.
-        this.addAbility(new DiesTriggeredAbility(new AshcloudPhoenixEffect()));
+        this.addAbility(new DiesSourceTriggeredAbility(new AshcloudPhoenixEffect()));
 
         // Morph {4}{R}{R}
         this.addAbility(new MorphAbility(this, new ManaCostsImpl<>("{4}{R}{R}")));
diff --git a/Mage.Sets/src/mage/cards/a/AshenSkinZubera.java b/Mage.Sets/src/mage/cards/a/AshenSkinZubera.java
index 050b2493a3..79fb5cfd07 100644
--- a/Mage.Sets/src/mage/cards/a/AshenSkinZubera.java
+++ b/Mage.Sets/src/mage/cards/a/AshenSkinZubera.java
@@ -5,7 +5,7 @@ package mage.cards.a;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.dynamicvalue.common.ZuberasDiedDynamicValue;
 import mage.abilities.effects.common.discard.DiscardTargetEffect;
 import mage.cards.CardImpl;
@@ -27,7 +27,7 @@ public final class AshenSkinZubera extends CardImpl {
 
         this.power = new MageInt(1);
         this.toughness = new MageInt(2);
-        Ability ability = new DiesTriggeredAbility(new DiscardTargetEffect(ZuberasDiedDynamicValue.instance));
+        Ability ability = new DiesSourceTriggeredAbility(new DiscardTargetEffect(ZuberasDiedDynamicValue.instance));
         ability.addTarget(new TargetOpponent());
         this.addAbility(ability, new ZuberasDiedWatcher());
     }
diff --git a/Mage.Sets/src/mage/cards/a/AttendantOfVraska.java b/Mage.Sets/src/mage/cards/a/AttendantOfVraska.java
index ca986d1948..0222b63049 100644
--- a/Mage.Sets/src/mage/cards/a/AttendantOfVraska.java
+++ b/Mage.Sets/src/mage/cards/a/AttendantOfVraska.java
@@ -2,7 +2,7 @@ package mage.cards.a;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
 import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
 import mage.abilities.dynamicvalue.common.SourcePermanentPowerCount;
@@ -38,7 +38,7 @@ public final class AttendantOfVraska extends CardImpl {
 
         // When Attendant of Vraska dies, if you control a Vraska planeswalker, you gain life equal to Attendant of Vraska's power.
         this.addAbility(new ConditionalInterveningIfTriggeredAbility(
-                new DiesTriggeredAbility(new GainLifeEffect(
+                new DiesSourceTriggeredAbility(new GainLifeEffect(
                         new SourcePermanentPowerCount()
                 ), false), new PermanentsOnTheBattlefieldCondition(filter),
                 "When {this} dies, if you control a Vraska planeswalker, "
diff --git a/Mage.Sets/src/mage/cards/a/AuraThief.java b/Mage.Sets/src/mage/cards/a/AuraThief.java
index 9a148b7e92..d44d77becb 100644
--- a/Mage.Sets/src/mage/cards/a/AuraThief.java
+++ b/Mage.Sets/src/mage/cards/a/AuraThief.java
@@ -7,7 +7,7 @@ package mage.cards.a;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.ContinuousEffect;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.continuous.GainControlTargetEffect;
@@ -43,7 +43,7 @@ public final class AuraThief extends CardImpl {
         
         // When Aura Thief dies, you gain control of all enchantments. You don't get
         // to move Auras.
-        this.addAbility(new DiesTriggeredAbility(new AuraThiefDiesTriggeredEffect()));
+        this.addAbility(new DiesSourceTriggeredAbility(new AuraThiefDiesTriggeredEffect()));
     }
     
     public AuraThief(final AuraThief card) {
diff --git a/Mage.Sets/src/mage/cards/a/AuspiciousAncestor.java b/Mage.Sets/src/mage/cards/a/AuspiciousAncestor.java
index 505daa753c..09612cb5c4 100644
--- a/Mage.Sets/src/mage/cards/a/AuspiciousAncestor.java
+++ b/Mage.Sets/src/mage/cards/a/AuspiciousAncestor.java
@@ -4,7 +4,7 @@ package mage.cards.a;
 import java.util.UUID;
 import mage.MageInt;
 import mage.ObjectColor;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.SpellCastAllTriggeredAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.common.DoIfCostPaid;
@@ -36,7 +36,7 @@ public final class AuspiciousAncestor extends CardImpl {
         this.toughness = new MageInt(3);
 
         // When Auspicious Ancestor dies, you gain 3 life.
-        this.addAbility(new DiesTriggeredAbility(new GainLifeEffect(3), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new GainLifeEffect(3), false));
         // Whenever a player casts a white spell, you may pay {1}. If you do, you gain 1 life.
         this.addAbility(new SpellCastAllTriggeredAbility(new DoIfCostPaid(new GainLifeEffect(1), new ManaCostsImpl("{1}")), filter, true));
     }
diff --git a/Mage.Sets/src/mage/cards/a/AvenFisher.java b/Mage.Sets/src/mage/cards/a/AvenFisher.java
index 8ce46a7e9f..7842c555fe 100644
--- a/Mage.Sets/src/mage/cards/a/AvenFisher.java
+++ b/Mage.Sets/src/mage/cards/a/AvenFisher.java
@@ -3,7 +3,7 @@ package mage.cards.a;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
@@ -25,7 +25,7 @@ public final class AvenFisher extends CardImpl {
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
         this.addAbility(FlyingAbility.getInstance());
-        this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(1), true));
+        this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1), true));
     }
 
     public AvenFisher(final AvenFisher card) {
diff --git a/Mage.Sets/src/mage/cards/a/AvengingAngel.java b/Mage.Sets/src/mage/cards/a/AvengingAngel.java
index 2397ba3109..5769a74414 100644
--- a/Mage.Sets/src/mage/cards/a/AvengingAngel.java
+++ b/Mage.Sets/src/mage/cards/a/AvengingAngel.java
@@ -4,7 +4,7 @@ package mage.cards.a;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.Card;
@@ -33,7 +33,7 @@ public final class AvengingAngel extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
         
         // When Avenging Angel dies, you may put it on top of its owner's library.
-        this.addAbility(new DiesTriggeredAbility(new AvengingAngelEffect(), true));
+        this.addAbility(new DiesSourceTriggeredAbility(new AvengingAngelEffect(), true));
     }
 
     public AvengingAngel(final AvengingAngel card) {
diff --git a/Mage.Sets/src/mage/cards/b/BantSojourners.java b/Mage.Sets/src/mage/cards/b/BantSojourners.java
index 85063e89a3..1aa6ebb6db 100644
--- a/Mage.Sets/src/mage/cards/b/BantSojourners.java
+++ b/Mage.Sets/src/mage/cards/b/BantSojourners.java
@@ -5,7 +5,7 @@ import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.CycleTriggeredAbility;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.abilities.keyword.CyclingAbility;
@@ -30,7 +30,7 @@ public final class BantSojourners extends CardImpl {
 
         // When you cycle Bant Sojourners or it dies, you may create a 1/1 white Soldier creature token.
         Ability ability1 = new CycleTriggeredAbility(new CreateTokenEffect(new SoldierToken()), true);
-        Ability ability2 = new DiesTriggeredAbility(new CreateTokenEffect(new SoldierToken()), true);
+        Ability ability2 = new DiesSourceTriggeredAbility(new CreateTokenEffect(new SoldierToken()), true);
         this.addAbility(ability1);
         this.addAbility(ability2);
         
diff --git a/Mage.Sets/src/mage/cards/b/Barishi.java b/Mage.Sets/src/mage/cards/b/Barishi.java
index 41c85f15c7..fa9196a7e4 100644
--- a/Mage.Sets/src/mage/cards/b/Barishi.java
+++ b/Mage.Sets/src/mage/cards/b/Barishi.java
@@ -3,7 +3,7 @@ package mage.cards.b;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.ExileSourceEffect;
 import mage.constants.SubType;
@@ -31,7 +31,7 @@ public final class Barishi extends CardImpl {
         this.toughness = new MageInt(3);
 
         // When Barishi dies, exile Barishi, then shuffle all creature cards from your graveyard into your library.
-        this.addAbility(new DiesTriggeredAbility(new BarishiEffect(), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new BarishiEffect(), false));
     }
 
     public Barishi(final Barishi card) {
diff --git a/Mage.Sets/src/mage/cards/b/BarteredCow.java b/Mage.Sets/src/mage/cards/b/BarteredCow.java
index 07fb03f4e6..9675188862 100644
--- a/Mage.Sets/src/mage/cards/b/BarteredCow.java
+++ b/Mage.Sets/src/mage/cards/b/BarteredCow.java
@@ -2,7 +2,7 @@ package mage.cards.b;
 
 import mage.MageInt;
 import mage.MageObject;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.abilities.effects.common.DiscardCardControllerTriggeredAbility;
@@ -41,7 +41,7 @@ public final class BarteredCow extends CardImpl {
         // When Bartered Cow dies or when you discard it, create a Food token.
         this.addAbility(new OrTriggeredAbility(
                 Zone.ALL, new CreateTokenEffect(new FoodToken()), false,
-                "When {this} dies or when you discard it, ", new DiesTriggeredAbility((Effect) null),
+                "When {this} dies or when you discard it, ", new DiesSourceTriggeredAbility((Effect) null),
                 new DiscardCardControllerTriggeredAbility(null, false, filter)
         ));
     }
diff --git a/Mage.Sets/src/mage/cards/b/BearerOfTheHeavens.java b/Mage.Sets/src/mage/cards/b/BearerOfTheHeavens.java
index d935f234c4..0cdf4f2efb 100644
--- a/Mage.Sets/src/mage/cards/b/BearerOfTheHeavens.java
+++ b/Mage.Sets/src/mage/cards/b/BearerOfTheHeavens.java
@@ -3,7 +3,7 @@ package mage.cards.b;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.DelayedTriggeredAbility;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
@@ -31,7 +31,7 @@ public final class BearerOfTheHeavens extends CardImpl {
         DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new DestroyAllEffect(StaticFilters.FILTER_PERMANENT));
         Effect effect = new CreateDelayedTriggeredAbilityEffect(delayedAbility);
         effect.setText("destroy all permanents at the beginning of the next end step");
-        this.addAbility(new DiesTriggeredAbility(effect, false));
+        this.addAbility(new DiesSourceTriggeredAbility(effect, false));
     }
 
     public BearerOfTheHeavens(final BearerOfTheHeavens card) {
diff --git a/Mage.Sets/src/mage/cards/b/BibFortuna.java b/Mage.Sets/src/mage/cards/b/BibFortuna.java
index c2a33363c8..8ac710afa3 100644
--- a/Mage.Sets/src/mage/cards/b/BibFortuna.java
+++ b/Mage.Sets/src/mage/cards/b/BibFortuna.java
@@ -4,7 +4,7 @@ package mage.cards.b;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.effects.common.LoseLifeSourceControllerEffect;
 import mage.abilities.effects.common.ShuffleLibrarySourceEffect;
@@ -35,7 +35,7 @@ public final class BibFortuna extends CardImpl {
         this.addAbility(ability);
 
         // When Bib Fortuna dies shuffle your library.
-        this.addAbility(new DiesTriggeredAbility(new ShuffleLibrarySourceEffect()));
+        this.addAbility(new DiesSourceTriggeredAbility(new ShuffleLibrarySourceEffect()));
     }
 
     public BibFortuna(final BibFortuna card) {
diff --git a/Mage.Sets/src/mage/cards/b/BitterheartWitch.java b/Mage.Sets/src/mage/cards/b/BitterheartWitch.java
index 4527fc3c3a..2b4b0623a4 100644
--- a/Mage.Sets/src/mage/cards/b/BitterheartWitch.java
+++ b/Mage.Sets/src/mage/cards/b/BitterheartWitch.java
@@ -4,7 +4,7 @@ package mage.cards.b;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.keyword.DeathtouchAbility;
 import mage.cards.Card;
@@ -36,7 +36,7 @@ public final class BitterheartWitch extends CardImpl {
         this.addAbility(DeathtouchAbility.getInstance());
 
         // When Bitterheart Witch dies, you may search your library for a Curse card, put it onto the battlefield attached to target player, then shuffle your library.
-        Ability ability = new DiesTriggeredAbility(new BitterheartWitchEffect(), true);
+        Ability ability = new DiesSourceTriggeredAbility(new BitterheartWitchEffect(), true);
         ability.addTarget(new TargetPlayer());
         this.addAbility(ability);
 
diff --git a/Mage.Sets/src/mage/cards/b/BlackCat.java b/Mage.Sets/src/mage/cards/b/BlackCat.java
index d6f263c907..f12607a99c 100644
--- a/Mage.Sets/src/mage/cards/b/BlackCat.java
+++ b/Mage.Sets/src/mage/cards/b/BlackCat.java
@@ -32,7 +32,7 @@ package mage.cards.b;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.discard.DiscardTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -54,7 +54,7 @@ public final class BlackCat extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Black Cat dies, target opponent discards a card at random.
-        Ability ability = new DiesTriggeredAbility(new DiscardTargetEffect(1, true),false);
+        Ability ability = new DiesSourceTriggeredAbility(new DiscardTargetEffect(1, true),false);
         ability.addTarget(new TargetOpponent());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/b/BlazingEffigy.java b/Mage.Sets/src/mage/cards/b/BlazingEffigy.java
index ddf098cd72..d429166ebe 100644
--- a/Mage.Sets/src/mage/cards/b/BlazingEffigy.java
+++ b/Mage.Sets/src/mage/cards/b/BlazingEffigy.java
@@ -7,7 +7,7 @@ import java.util.UUID;
 import mage.MageInt;
 import mage.MageObjectReference;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.dynamicvalue.DynamicValue;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.DamageTargetEffect;
@@ -35,7 +35,7 @@ public final class BlazingEffigy extends CardImpl {
         this.toughness = new MageInt(3);
 
         // When Blazing Effigy dies, it deals X damage to target creature, where X is 3 plus the amount of damage dealt to Blazing Effigy this turn by other sources named Blazing Effigy.
-        Ability ability = new DiesTriggeredAbility(new DamageTargetEffect(BlazingEffigyCount.instance), false);
+        Ability ability = new DiesSourceTriggeredAbility(new DamageTargetEffect(BlazingEffigyCount.instance), false);
         ability.addTarget(new TargetCreaturePermanent());
         this.addAbility(ability, new BlazingEffigyWatcher());
     }
diff --git a/Mage.Sets/src/mage/cards/b/Blistergrub.java b/Mage.Sets/src/mage/cards/b/Blistergrub.java
index 7519cb0dd5..198b9e0537 100644
--- a/Mage.Sets/src/mage/cards/b/Blistergrub.java
+++ b/Mage.Sets/src/mage/cards/b/Blistergrub.java
@@ -4,7 +4,7 @@ package mage.cards.b;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.LoseLifeOpponentsEffect;
 import mage.abilities.keyword.SwampwalkAbility;
 import mage.cards.CardImpl;
@@ -28,7 +28,7 @@ public final class Blistergrub extends CardImpl {
         // Swampwalk (This creature can't be blocked as long as defending player controls a Swamp.)
         this.addAbility(new SwampwalkAbility());
         // When Blistergrub dies, each opponent loses 2 life.
-        this.addAbility(new DiesTriggeredAbility(new LoseLifeOpponentsEffect(2), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new LoseLifeOpponentsEffect(2), false));
     }
 
     public Blistergrub (final Blistergrub card) {
diff --git a/Mage.Sets/src/mage/cards/b/Blisterpod.java b/Mage.Sets/src/mage/cards/b/Blisterpod.java
index b3644a8392..217161f196 100644
--- a/Mage.Sets/src/mage/cards/b/Blisterpod.java
+++ b/Mage.Sets/src/mage/cards/b/Blisterpod.java
@@ -3,7 +3,7 @@ package mage.cards.b;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.abilities.keyword.DevoidAbility;
@@ -30,7 +30,7 @@ public final class Blisterpod extends CardImpl {
         // When Blisterpod dies, create a 1/1 colorless Eldrazi Scion creature token. It has "Sacrifice this creature: Add {C}."
         Effect effect = new CreateTokenEffect(new EldraziScionToken());
         effect.setText("Create a 1/1 colorless Eldrazi Scion creature token. It has \"Sacrifice this creature: Add {C}.\"");
-        this.addAbility(new DiesTriggeredAbility(effect, false));
+        this.addAbility(new DiesSourceTriggeredAbility(effect, false));
     }
 
     public Blisterpod(final Blisterpod card) {
diff --git a/Mage.Sets/src/mage/cards/b/BodySnatcher.java b/Mage.Sets/src/mage/cards/b/BodySnatcher.java
index 2f6e6aeb26..0bdc8fa855 100644
--- a/Mage.Sets/src/mage/cards/b/BodySnatcher.java
+++ b/Mage.Sets/src/mage/cards/b/BodySnatcher.java
@@ -4,7 +4,7 @@ package mage.cards.b;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.costs.common.DiscardTargetCost;
 import mage.abilities.effects.Effect;
@@ -38,7 +38,7 @@ public final class BodySnatcher extends CardImpl {
         // When Body Snatcher dies, exile Body Snatcher and return target creature card from your graveyard to the battlefield.
         Effect effect = new ReturnFromGraveyardToBattlefieldTargetEffect();
         effect.setText("and return target creature card from your graveyard to the battlefield");
-        Ability ability = new DiesTriggeredAbility(new ExileSourceEffect(), false);
+        Ability ability = new DiesSourceTriggeredAbility(new ExileSourceEffect(), false);
         ability.addEffect(effect);
         ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD));
         this.addAbility(ability);
diff --git a/Mage.Sets/src/mage/cards/b/BogardanFirefiend.java b/Mage.Sets/src/mage/cards/b/BogardanFirefiend.java
index dce2d9e915..9bcf88f9a7 100644
--- a/Mage.Sets/src/mage/cards/b/BogardanFirefiend.java
+++ b/Mage.Sets/src/mage/cards/b/BogardanFirefiend.java
@@ -4,7 +4,7 @@ package mage.cards.b;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DamageTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -24,7 +24,7 @@ public final class BogardanFirefiend extends CardImpl {
 
         this.power = new MageInt(2);
         this.toughness = new MageInt(1);
-        Ability ability = new DiesTriggeredAbility(new DamageTargetEffect(2), false);
+        Ability ability = new DiesSourceTriggeredAbility(new DamageTargetEffect(2), false);
         ability.addTarget(new TargetCreaturePermanent());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/b/BogardanPhoenix.java b/Mage.Sets/src/mage/cards/b/BogardanPhoenix.java
index e56829c19b..b724e00372 100644
--- a/Mage.Sets/src/mage/cards/b/BogardanPhoenix.java
+++ b/Mage.Sets/src/mage/cards/b/BogardanPhoenix.java
@@ -3,7 +3,7 @@ package mage.cards.b;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.constants.SubType;
 import mage.abilities.keyword.FlyingAbility;
@@ -36,7 +36,7 @@ public final class BogardanPhoenix extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
 
         // When Bogardan Phoenix dies, exile it if it had a death counter on it. Otherwise, return it to the battlefield under your control and put a death counter on it.
-        this.addAbility(new DiesTriggeredAbility(new BogardanPhoenixEffect(), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new BogardanPhoenixEffect(), false));
     }
 
     public BogardanPhoenix(final BogardanPhoenix card) {
diff --git a/Mage.Sets/src/mage/cards/b/BrindleShoat.java b/Mage.Sets/src/mage/cards/b/BrindleShoat.java
index 95f1ad2db2..5b98c191d6 100644
--- a/Mage.Sets/src/mage/cards/b/BrindleShoat.java
+++ b/Mage.Sets/src/mage/cards/b/BrindleShoat.java
@@ -3,7 +3,7 @@ package mage.cards.b;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -25,7 +25,7 @@ public final class BrindleShoat extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Brindle Shoat dies, create a 3/3 green Boar creature token.
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new BoarToken())));
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new BoarToken())));
     }
 
     public BrindleShoat(final BrindleShoat card) {
diff --git a/Mage.Sets/src/mage/cards/b/BrineHag.java b/Mage.Sets/src/mage/cards/b/BrineHag.java
index 5050409b69..608e176fc2 100644
--- a/Mage.Sets/src/mage/cards/b/BrineHag.java
+++ b/Mage.Sets/src/mage/cards/b/BrineHag.java
@@ -7,7 +7,7 @@ import java.util.UUID;
 import mage.MageInt;
 import mage.MageObjectReference;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.continuous.SetPowerToughnessAllEffect;
 import mage.cards.CardImpl;
@@ -36,7 +36,7 @@ public final class BrineHag extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Brine Hag dies, change the base power and toughness of all creatures that dealt damage to it this turn to 0/2.
-        this.addAbility(new DiesTriggeredAbility(new BrineHagEffect()));
+        this.addAbility(new DiesSourceTriggeredAbility(new BrineHagEffect()));
     }
 
     public BrineHag(final BrineHag card) {
diff --git a/Mage.Sets/src/mage/cards/b/BronzehideLion.java b/Mage.Sets/src/mage/cards/b/BronzehideLion.java
index 737dc12661..98dd760b60 100644
--- a/Mage.Sets/src/mage/cards/b/BronzehideLion.java
+++ b/Mage.Sets/src/mage/cards/b/BronzehideLion.java
@@ -5,7 +5,7 @@ import java.util.List;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.ContinuousEffectImpl;
@@ -45,7 +45,7 @@ public final class BronzehideLion extends CardImpl {
         // When 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.
-        this.addAbility(new DiesTriggeredAbility(new BronzehideLionReturnEffect()));
+        this.addAbility(new DiesSourceTriggeredAbility(new BronzehideLionReturnEffect()));
     }
 
     private BronzehideLion(final BronzehideLion card) {
diff --git a/Mage.Sets/src/mage/cards/b/BurningEyeZubera.java b/Mage.Sets/src/mage/cards/b/BurningEyeZubera.java
index e916504a73..d8d34f9dd0 100644
--- a/Mage.Sets/src/mage/cards/b/BurningEyeZubera.java
+++ b/Mage.Sets/src/mage/cards/b/BurningEyeZubera.java
@@ -4,7 +4,7 @@ package mage.cards.b;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.condition.Condition;
 import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
 import mage.abilities.effects.common.DamageTargetEffect;
@@ -31,7 +31,7 @@ public final class BurningEyeZubera extends CardImpl {
         this.toughness = new MageInt(3);
 
         // When Burning-Eye Zubera dies, if 4 or more damage was dealt to it this turn, Burning-Eye Zubera deals 3 damage to any target.
-        Ability ability = new ConditionalInterveningIfTriggeredAbility(new DiesTriggeredAbility(new DamageTargetEffect(3)),new SourceGotFourDamage(),
+        Ability ability = new ConditionalInterveningIfTriggeredAbility(new DiesSourceTriggeredAbility(new DamageTargetEffect(3)),new SourceGotFourDamage(),
                 "When {this} dies, if 4 or more damage was dealt to it this turn, Burning-Eye Zubera deals 3 damage to any target");
         ability.addTarget(new TargetAnyTarget());
         this.addAbility(ability);
diff --git a/Mage.Sets/src/mage/cards/b/BywayCourier.java b/Mage.Sets/src/mage/cards/b/BywayCourier.java
index 6e66b6a06d..dbf1f1d5f0 100644
--- a/Mage.Sets/src/mage/cards/b/BywayCourier.java
+++ b/Mage.Sets/src/mage/cards/b/BywayCourier.java
@@ -3,7 +3,7 @@ package mage.cards.b;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.keyword.InvestigateEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -23,7 +23,7 @@ public final class BywayCourier extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Byway Courier dies, investigate.
-        this.addAbility(new DiesTriggeredAbility(new InvestigateEffect(), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new InvestigateEffect(), false));
     }
 
     public BywayCourier(final BywayCourier card) {
diff --git a/Mage.Sets/src/mage/cards/c/CanopyStalker.java b/Mage.Sets/src/mage/cards/c/CanopyStalker.java
index 3383889e4b..6a8c4fd2d4 100644
--- a/Mage.Sets/src/mage/cards/c/CanopyStalker.java
+++ b/Mage.Sets/src/mage/cards/c/CanopyStalker.java
@@ -1,7 +1,7 @@
 package mage.cards.c;
 
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.dynamicvalue.common.CreaturesDiedThisTurnCount;
 import mage.abilities.effects.common.GainLifeEffect;
@@ -30,7 +30,7 @@ public final class CanopyStalker extends CardImpl {
         this.addAbility(new SimpleStaticAbility(new MustBeBlockedByAtLeastOneSourceEffect()));
 
         // When Canopy Stalker dies, you gain 1 life for each creature that died this turn.
-        this.addAbility(new DiesTriggeredAbility(
+        this.addAbility(new DiesSourceTriggeredAbility(
                 new GainLifeEffect(CreaturesDiedThisTurnCount.instance)
                         .setText("you gain 1 life for each creature that died this turn")
         ), new CreaturesDiedWatcher());
diff --git a/Mage.Sets/src/mage/cards/c/CarelessCelebrant.java b/Mage.Sets/src/mage/cards/c/CarelessCelebrant.java
index 6b97dd3bd2..c2fbb8e0b1 100644
--- a/Mage.Sets/src/mage/cards/c/CarelessCelebrant.java
+++ b/Mage.Sets/src/mage/cards/c/CarelessCelebrant.java
@@ -2,7 +2,7 @@ package mage.cards.c;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DamageTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -40,7 +40,7 @@ public final class CarelessCelebrant extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Careless Celebrant dies, it deals 2 damage to target creature or planeswalker an opponent controls.
-        Ability ability = new DiesTriggeredAbility(new DamageTargetEffect(2, "it"));
+        Ability ability = new DiesSourceTriggeredAbility(new DamageTargetEffect(2, "it"));
         ability.addTarget(new TargetPermanent(filter));
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/c/CarrierThrall.java b/Mage.Sets/src/mage/cards/c/CarrierThrall.java
index d6bc7cea23..94e3bf2813 100644
--- a/Mage.Sets/src/mage/cards/c/CarrierThrall.java
+++ b/Mage.Sets/src/mage/cards/c/CarrierThrall.java
@@ -3,7 +3,7 @@ package mage.cards.c;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.cards.CardImpl;
@@ -27,7 +27,7 @@ public final class CarrierThrall extends CardImpl {
         // When Carrier Thrall dies, create a 1/1 colorless Eldrazi Scion creature token. It has "Sacrifice this creature. Add {C}."
         Effect effect = new CreateTokenEffect(new EldraziScionToken());
         effect.setText("create a 1/1 colorless Eldrazi Scion creature token. It has \"Sacrifice this creature: Add {C}.\"");
-        this.addAbility(new DiesTriggeredAbility(effect, false));
+        this.addAbility(new DiesSourceTriggeredAbility(effect, false));
 
     }
 
diff --git a/Mage.Sets/src/mage/cards/c/CarrionThrash.java b/Mage.Sets/src/mage/cards/c/CarrionThrash.java
index 0bc6afc9f8..62b96da57e 100644
--- a/Mage.Sets/src/mage/cards/c/CarrionThrash.java
+++ b/Mage.Sets/src/mage/cards/c/CarrionThrash.java
@@ -3,7 +3,7 @@ package mage.cards.c;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.costs.mana.GenericManaCost;
 import mage.abilities.effects.common.DoIfCostPaid;
 import mage.abilities.effects.common.ReturnToHandTargetEffect;
@@ -36,7 +36,7 @@ public final class CarrionThrash extends CardImpl {
         this.toughness = new MageInt(4);
 
         // When Carrion Thrash dies, you may pay {2}. If you do, return another target creature card from your graveyard to your hand.
-        DiesTriggeredAbility ability = new DiesTriggeredAbility(new DoIfCostPaid(new ReturnToHandTargetEffect(), new GenericManaCost(2)), false);
+        DiesSourceTriggeredAbility ability = new DiesSourceTriggeredAbility(new DoIfCostPaid(new ReturnToHandTargetEffect(), new GenericManaCost(2)), false);
         ability.addTarget(new TargetCardInYourGraveyard(filter));
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/c/Cathodion.java b/Mage.Sets/src/mage/cards/c/Cathodion.java
index 7de6f730f6..212d3a6872 100644
--- a/Mage.Sets/src/mage/cards/c/Cathodion.java
+++ b/Mage.Sets/src/mage/cards/c/Cathodion.java
@@ -4,7 +4,7 @@ package mage.cards.c;
 import java.util.UUID;
 import mage.MageInt;
 import mage.Mana;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.mana.BasicManaEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -22,7 +22,7 @@ public final class Cathodion extends CardImpl {
         this.subtype.add(SubType.CONSTRUCT);
         this.power = new MageInt(3);
         this.toughness = new MageInt(3);
-        this.addAbility(new DiesTriggeredAbility(new BasicManaEffect(Mana.ColorlessMana(3)), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new BasicManaEffect(Mana.ColorlessMana(3)), false));
     }
 
     public Cathodion(final Cathodion card) {
diff --git a/Mage.Sets/src/mage/cards/c/CausticHound.java b/Mage.Sets/src/mage/cards/c/CausticHound.java
index 266d106cec..f01e20fae4 100644
--- a/Mage.Sets/src/mage/cards/c/CausticHound.java
+++ b/Mage.Sets/src/mage/cards/c/CausticHound.java
@@ -4,7 +4,7 @@ package mage.cards.c;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.LoseLifeAllPlayersEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -23,7 +23,7 @@ public final class CausticHound extends CardImpl {
 
         this.power = new MageInt(4);
         this.toughness = new MageInt(4);
-        this.addAbility(new DiesTriggeredAbility(new LoseLifeAllPlayersEffect(4)));
+        this.addAbility(new DiesSourceTriggeredAbility(new LoseLifeAllPlayersEffect(4)));
     }
 
     public CausticHound (final CausticHound card) {
diff --git a/Mage.Sets/src/mage/cards/c/CavalierOfDawn.java b/Mage.Sets/src/mage/cards/c/CavalierOfDawn.java
index 2eba411769..a77ae3e3fd 100644
--- a/Mage.Sets/src/mage/cards/c/CavalierOfDawn.java
+++ b/Mage.Sets/src/mage/cards/c/CavalierOfDawn.java
@@ -2,7 +2,7 @@ package mage.cards.c;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.OneShotEffect;
@@ -51,7 +51,7 @@ public final class CavalierOfDawn extends CardImpl {
         this.addAbility(ability);
 
         // When Cavalier of Dawn dies, return target artifact or enchantment card from your graveyard to your hand.
-        ability = new DiesTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect());
+        ability = new DiesSourceTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect());
         ability.addTarget(new TargetCardInYourGraveyard(filter));
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/c/CavalierOfFlame.java b/Mage.Sets/src/mage/cards/c/CavalierOfFlame.java
index 4fbf766bc0..4de1d6c087 100644
--- a/Mage.Sets/src/mage/cards/c/CavalierOfFlame.java
+++ b/Mage.Sets/src/mage/cards/c/CavalierOfFlame.java
@@ -2,7 +2,7 @@ package mage.cards.c;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
@@ -61,7 +61,7 @@ public final class CavalierOfFlame extends CardImpl {
         this.addAbility(new EntersBattlefieldTriggeredAbility(new CavalierOfFlameEffect()));
 
         // When Cavalier of Flame dies, it deals X damage to each opponent and each planeswalker they control, where X is the number of land cards in your graveyard.
-        ability = new DiesTriggeredAbility(new DamagePlayersEffect(
+        ability = new DiesSourceTriggeredAbility(new DamagePlayersEffect(
                 Outcome.Damage, xValue, TargetController.OPPONENT
         ).setText("it deals X damage to each opponent"));
         ability.addEffect(new DamageAllEffect(
diff --git a/Mage.Sets/src/mage/cards/c/CavalierOfGales.java b/Mage.Sets/src/mage/cards/c/CavalierOfGales.java
index e972f50c39..76f16388fa 100644
--- a/Mage.Sets/src/mage/cards/c/CavalierOfGales.java
+++ b/Mage.Sets/src/mage/cards/c/CavalierOfGales.java
@@ -2,7 +2,7 @@ package mage.cards.c;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.effects.common.BrainstormEffect;
 import mage.abilities.effects.common.ShuffleIntoLibrarySourceEffect;
@@ -35,7 +35,7 @@ public final class CavalierOfGales extends CardImpl {
         this.addAbility(new EntersBattlefieldTriggeredAbility(new BrainstormEffect()));
 
         // When Cavalier of Gales dies, shuffle it into its owner's library, then scry 2.
-        Ability ability = new DiesTriggeredAbility(new ShuffleIntoLibrarySourceEffect());
+        Ability ability = new DiesSourceTriggeredAbility(new ShuffleIntoLibrarySourceEffect());
         ability.addEffect(new ScryEffect(2).concatBy(", then"));
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/c/CavalierOfNight.java b/Mage.Sets/src/mage/cards/c/CavalierOfNight.java
index e8776a3ea4..2020c78e3d 100644
--- a/Mage.Sets/src/mage/cards/c/CavalierOfNight.java
+++ b/Mage.Sets/src/mage/cards/c/CavalierOfNight.java
@@ -2,7 +2,7 @@ package mage.cards.c;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.common.delayed.ReflexiveTriggeredAbility;
 import mage.abilities.costs.common.SacrificeTargetCost;
@@ -64,7 +64,7 @@ public final class CavalierOfNight extends CardImpl {
         )));
 
         // When Cavalier of Night dies, return target creature card with converted mana cost 3 or less from your graveyard to the battlefield.
-        Ability ability = new DiesTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect());
+        Ability ability = new DiesSourceTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect());
         ability.addTarget(new TargetCardInYourGraveyard(filter2));
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/c/CavalierOfThorns.java b/Mage.Sets/src/mage/cards/c/CavalierOfThorns.java
index eabcf3643d..14b84ac4a1 100644
--- a/Mage.Sets/src/mage/cards/c/CavalierOfThorns.java
+++ b/Mage.Sets/src/mage/cards/c/CavalierOfThorns.java
@@ -2,7 +2,7 @@ package mage.cards.c;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.costs.common.ExileSourceFromGraveCost;
 import mage.abilities.effects.OneShotEffect;
@@ -50,7 +50,7 @@ public final class CavalierOfThorns extends CardImpl {
         this.addAbility(new EntersBattlefieldTriggeredAbility(new CavalierOfThornsEffect()));
 
         // When Cavalier of Thorns dies, you may exile it. If you do, put another target card from your graveyard on top of your library.
-        Ability ability = new DiesTriggeredAbility(new DoIfCostPaid(
+        Ability ability = new DiesSourceTriggeredAbility(new DoIfCostPaid(
                 new PutOnLibraryTargetEffect(true), new ExileSourceFromGraveCost()
         ).setText("you may exile it. If you do, put another target card from your graveyard on top of your library."));
         ability.addTarget(new TargetCardInYourGraveyard(filter));
diff --git a/Mage.Sets/src/mage/cards/c/CelestialGatekeeper.java b/Mage.Sets/src/mage/cards/c/CelestialGatekeeper.java
index 4acadb8fc5..4a66a77b38 100644
--- a/Mage.Sets/src/mage/cards/c/CelestialGatekeeper.java
+++ b/Mage.Sets/src/mage/cards/c/CelestialGatekeeper.java
@@ -3,7 +3,7 @@ package mage.cards.c;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.ExileSourceEffect;
 import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
@@ -47,7 +47,7 @@ public final class CelestialGatekeeper extends CardImpl {
         // When Celestial Gatekeeper dies, exile it, then return up to two target Bird and/or Cleric permanent cards from your graveyard to the battlefield.
         Effect effect = new ExileSourceEffect();
         effect.setText("");
-        DiesTriggeredAbility ability = new DiesTriggeredAbility(effect);
+        DiesSourceTriggeredAbility ability = new DiesSourceTriggeredAbility(effect);
         effect = new ReturnFromGraveyardToBattlefieldTargetEffect();
         effect.setText("exile it, then return up to two target Bird and/or Cleric permanent cards from your graveyard to the battlefield");
         ability.addEffect(effect);
diff --git a/Mage.Sets/src/mage/cards/c/CentaurSafeguard.java b/Mage.Sets/src/mage/cards/c/CentaurSafeguard.java
index 1ebeea7209..475abe297b 100644
--- a/Mage.Sets/src/mage/cards/c/CentaurSafeguard.java
+++ b/Mage.Sets/src/mage/cards/c/CentaurSafeguard.java
@@ -3,7 +3,7 @@ package mage.cards.c;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.GainLifeEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -25,7 +25,7 @@ public final class CentaurSafeguard extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Centaur Safeguard dies, you may gain 3 life.
-        this.addAbility(new DiesTriggeredAbility(new GainLifeEffect(3), true));
+        this.addAbility(new DiesSourceTriggeredAbility(new GainLifeEffect(3), true));
     }
 
     public CentaurSafeguard(final CentaurSafeguard card) {
diff --git a/Mage.Sets/src/mage/cards/c/ChasmSkulker.java b/Mage.Sets/src/mage/cards/c/ChasmSkulker.java
index 880546b17c..8a86c45285 100644
--- a/Mage.Sets/src/mage/cards/c/ChasmSkulker.java
+++ b/Mage.Sets/src/mage/cards/c/ChasmSkulker.java
@@ -4,7 +4,7 @@ package mage.cards.c;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.DrawCardControllerTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.CreateTokenEffect;
@@ -39,7 +39,7 @@ public final class ChasmSkulker extends CardImpl {
         this.addAbility(new DrawCardControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false));
 
         // When Chasm Skulker dies, create X 1/1 blue Squid creature tokens with islandwalk, where X is the number of +1/+1 counters on Chasm Skulker.
-        this.addAbility(new DiesTriggeredAbility(new ChasmSkulkerEffect(), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new ChasmSkulkerEffect(), false));
     }
 
     public ChasmSkulker(final ChasmSkulker card) {
diff --git a/Mage.Sets/src/mage/cards/c/CherishedHatchling.java b/Mage.Sets/src/mage/cards/c/CherishedHatchling.java
index 81c972d53f..f8c3c4835e 100644
--- a/Mage.Sets/src/mage/cards/c/CherishedHatchling.java
+++ b/Mage.Sets/src/mage/cards/c/CherishedHatchling.java
@@ -3,7 +3,7 @@ package mage.cards.c;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.DelayedTriggeredAbility;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
@@ -45,7 +45,7 @@ public final class CherishedHatchling extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Cherished Hatchling dies, you may cast Dinosaur spells this turn as though they had flash, and whenever you cast a Dinosaur spell this turn, it gains "When this creature enters the battlefield, you may have it fight another target creature."
-        Ability ability = new DiesTriggeredAbility(new CastAsThoughItHadFlashAllEffect(Duration.EndOfTurn, filterCard, false));
+        Ability ability = new DiesSourceTriggeredAbility(new CastAsThoughItHadFlashAllEffect(Duration.EndOfTurn, filterCard, false));
         ability.addEffect(new CreateDelayedTriggeredAbilityEffect(new CherishedHatchlingTriggeredAbility()));
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/c/ChildOfAlara.java b/Mage.Sets/src/mage/cards/c/ChildOfAlara.java
index d980f243a1..f739ddb60f 100644
--- a/Mage.Sets/src/mage/cards/c/ChildOfAlara.java
+++ b/Mage.Sets/src/mage/cards/c/ChildOfAlara.java
@@ -3,7 +3,7 @@ package mage.cards.c;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DestroyAllEffect;
 import mage.abilities.keyword.TrampleAbility;
 import mage.cards.CardImpl;
@@ -31,7 +31,7 @@ public final class ChildOfAlara extends CardImpl {
         this.addAbility(TrampleAbility.getInstance());
         
         // When Child of Alara dies, destroy all nonland permanents. They can't be regenerated.
-        this.addAbility(new DiesTriggeredAbility(new DestroyAllEffect(new FilterNonlandPermanent("nonland permanents"), true)));
+        this.addAbility(new DiesSourceTriggeredAbility(new DestroyAllEffect(new FilterNonlandPermanent("nonland permanents"), true)));
         
     }
 
diff --git a/Mage.Sets/src/mage/cards/c/ChimneyImp.java b/Mage.Sets/src/mage/cards/c/ChimneyImp.java
index 6e9489b290..455b303dda 100644
--- a/Mage.Sets/src/mage/cards/c/ChimneyImp.java
+++ b/Mage.Sets/src/mage/cards/c/ChimneyImp.java
@@ -4,7 +4,7 @@ package mage.cards.c;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.Card;
@@ -35,7 +35,7 @@ public final class ChimneyImp extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
         
         // When Chimney Imp dies, target opponent puts a card from their hand on top of their library.
-        Ability ability = new DiesTriggeredAbility(new ChimneyImpEffect(), false);
+        Ability ability = new DiesSourceTriggeredAbility(new ChimneyImpEffect(), false);
         ability.addTarget(new TargetOpponent());
         this.addAbility(ability);
         
diff --git a/Mage.Sets/src/mage/cards/c/Chronozoa.java b/Mage.Sets/src/mage/cards/c/Chronozoa.java
index 81a6153913..2ade15a2fe 100644
--- a/Mage.Sets/src/mage/cards/c/Chronozoa.java
+++ b/Mage.Sets/src/mage/cards/c/Chronozoa.java
@@ -4,7 +4,7 @@ package mage.cards.c;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldAbility;
 import mage.abilities.condition.common.LastTimeCounterRemovedCondition;
 import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
@@ -46,7 +46,7 @@ public final class Chronozoa extends CardImpl {
         // When Chronozoa is put into a graveyard from play, if it had no time counters on it, create two tokens that are copies of it.
         Effect effect = new CreateTokenCopySourceEffect(2);
         effect.setText("create two tokens that are copies of it");
-        this.addAbility(new ConditionalInterveningIfTriggeredAbility(new DiesTriggeredAbility(effect, false),
+        this.addAbility(new ConditionalInterveningIfTriggeredAbility(new DiesSourceTriggeredAbility(effect, false),
                 LastTimeCounterRemovedCondition.instance,
                 "When {this} dies, if it had no time counters on it, create two tokens that are copies of it."));
     }
diff --git a/Mage.Sets/src/mage/cards/c/CitywatchSphinx.java b/Mage.Sets/src/mage/cards/c/CitywatchSphinx.java
index 3bfe96bc8d..0fe4108f9a 100644
--- a/Mage.Sets/src/mage/cards/c/CitywatchSphinx.java
+++ b/Mage.Sets/src/mage/cards/c/CitywatchSphinx.java
@@ -2,7 +2,7 @@ package mage.cards.c;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.keyword.SurveilEffect;
 import mage.constants.SubType;
 import mage.abilities.keyword.FlyingAbility;
@@ -27,7 +27,7 @@ public final class CitywatchSphinx extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
 
         // When Citywatch Sphinx dies, surveil 2.
-        this.addAbility(new DiesTriggeredAbility(new SurveilEffect(2)));
+        this.addAbility(new DiesSourceTriggeredAbility(new SurveilEffect(2)));
     }
 
     public CitywatchSphinx(final CitywatchSphinx card) {
diff --git a/Mage.Sets/src/mage/cards/c/CloneShell.java b/Mage.Sets/src/mage/cards/c/CloneShell.java
index 84171a085b..7ce9a397b9 100644
--- a/Mage.Sets/src/mage/cards/c/CloneShell.java
+++ b/Mage.Sets/src/mage/cards/c/CloneShell.java
@@ -2,7 +2,7 @@ package mage.cards.c;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.cards.*;
@@ -36,7 +36,7 @@ public final class CloneShell extends CardImpl {
         this.addAbility(new EntersBattlefieldTriggeredAbility(new CloneShellEffect(), false));
 
         // When Clone Shell dies, turn the exiled card face up. If it's a creature card, put it onto the battlefield under your control.
-        this.addAbility(new DiesTriggeredAbility(new CloneShellDiesEffect()));
+        this.addAbility(new DiesSourceTriggeredAbility(new CloneShellDiesEffect()));
     }
 
     public CloneShell(final CloneShell card) {
diff --git a/Mage.Sets/src/mage/cards/c/ConclaveCavalier.java b/Mage.Sets/src/mage/cards/c/ConclaveCavalier.java
index f8035d718c..144808dd33 100644
--- a/Mage.Sets/src/mage/cards/c/ConclaveCavalier.java
+++ b/Mage.Sets/src/mage/cards/c/ConclaveCavalier.java
@@ -2,7 +2,7 @@ package mage.cards.c;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.constants.SubType;
 import mage.abilities.keyword.VigilanceAbility;
@@ -29,7 +29,7 @@ public final class ConclaveCavalier extends CardImpl {
         this.addAbility(VigilanceAbility.getInstance());
 
         // When Conclave Cavalier dies, create two green and white 2/2 Elf Knight creature tokens with vigilance.
-        this.addAbility(new DiesTriggeredAbility(
+        this.addAbility(new DiesSourceTriggeredAbility(
                 new CreateTokenEffect(new ElfKnightToken(), 2)
         ));
     }
diff --git a/Mage.Sets/src/mage/cards/c/ConclaveMentor.java b/Mage.Sets/src/mage/cards/c/ConclaveMentor.java
index d7ffc91922..28c1f1217a 100644
--- a/Mage.Sets/src/mage/cards/c/ConclaveMentor.java
+++ b/Mage.Sets/src/mage/cards/c/ConclaveMentor.java
@@ -2,7 +2,7 @@ package mage.cards.c;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.dynamicvalue.DynamicValue;
 import mage.abilities.dynamicvalue.common.SourcePermanentPowerCount;
@@ -40,7 +40,7 @@ public final class ConclaveMentor extends CardImpl {
         this.addAbility(new SimpleStaticAbility(new ConclaveMentorEffect()));
 
         // When Conclave Mentor dies, you gain life equal to its power.
-        this.addAbility(new DiesTriggeredAbility(new GainLifeEffect(xValue, "you gain life equal to its power")));
+        this.addAbility(new DiesSourceTriggeredAbility(new GainLifeEffect(xValue, "you gain life equal to its power")));
     }
 
     private ConclaveMentor(final ConclaveMentor card) {
diff --git a/Mage.Sets/src/mage/cards/c/CoreProwler.java b/Mage.Sets/src/mage/cards/c/CoreProwler.java
index 963b642bbe..708adb93a0 100644
--- a/Mage.Sets/src/mage/cards/c/CoreProwler.java
+++ b/Mage.Sets/src/mage/cards/c/CoreProwler.java
@@ -1,7 +1,7 @@
 package mage.cards.c;
 
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.counter.ProliferateEffect;
 import mage.abilities.keyword.InfectAbility;
 import mage.cards.CardImpl;
@@ -26,7 +26,7 @@ public final class CoreProwler extends CardImpl {
         this.addAbility(InfectAbility.getInstance());
 
         // When Core Prowler dies, proliferate. (You choose any number of permanents and/or players with counters on them, then give each another counter of a kind already there.)
-        this.addAbility(new DiesTriggeredAbility(new ProliferateEffect()));
+        this.addAbility(new DiesSourceTriggeredAbility(new ProliferateEffect()));
     }
 
     public CoreProwler(final CoreProwler card) {
diff --git a/Mage.Sets/src/mage/cards/c/CorpseAugur.java b/Mage.Sets/src/mage/cards/c/CorpseAugur.java
index 01f0dea7d4..4cb6722c91 100644
--- a/Mage.Sets/src/mage/cards/c/CorpseAugur.java
+++ b/Mage.Sets/src/mage/cards/c/CorpseAugur.java
@@ -4,7 +4,7 @@ package mage.cards.c;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.dynamicvalue.common.CardsInTargetPlayersGraveyardCount;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
@@ -33,7 +33,7 @@ public final class CorpseAugur extends CardImpl {
         CardsInTargetPlayersGraveyardCount dynamicValue = new CardsInTargetPlayersGraveyardCount(new FilterCreatureCard("the number of creature cards"));
         Effect effect = new DrawCardSourceControllerEffect(dynamicValue);
         effect.setText("You draw X cards");
-        Ability ability = new DiesTriggeredAbility(effect, false);
+        Ability ability = new DiesSourceTriggeredAbility(effect, false);
         effect = new LoseLifeSourceControllerEffect(dynamicValue);
         effect.setText("and you lose X life, where X is the number of creature cards in target player's graveyard");
         ability.addEffect(effect);
diff --git a/Mage.Sets/src/mage/cards/c/CycloneSire.java b/Mage.Sets/src/mage/cards/c/CycloneSire.java
index ea97032c91..10b665840e 100644
--- a/Mage.Sets/src/mage/cards/c/CycloneSire.java
+++ b/Mage.Sets/src/mage/cards/c/CycloneSire.java
@@ -4,7 +4,7 @@ package mage.cards.c;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.continuous.BecomesCreatureTargetEffect;
 import mage.abilities.effects.common.counter.AddCountersTargetEffect;
@@ -34,7 +34,7 @@ public final class CycloneSire extends CardImpl {
         // Flying
         this.addAbility(FlyingAbility.getInstance());
         // When Cyclone Sire dies, you may put three +1/+1 counters on target land you control. If you do, that land becomes a 0/0 Elemental creature with haste that's still a land.
-        Ability ability = new DiesTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance(3)), true);
+        Ability ability = new DiesSourceTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance(3)), true);
         Effect effect = new BecomesCreatureTargetEffect(new WallOfResurgenceToken(), false, true, Duration.Custom);
         effect.setText("If you do, that land becomes a 0/0 Elemental creature with haste that's still a land");
         ability.addEffect(effect);
diff --git a/Mage.Sets/src/mage/cards/c/CyclopeanGiant.java b/Mage.Sets/src/mage/cards/c/CyclopeanGiant.java
index 1c04cdb2a6..0842dcaa94 100644
--- a/Mage.Sets/src/mage/cards/c/CyclopeanGiant.java
+++ b/Mage.Sets/src/mage/cards/c/CyclopeanGiant.java
@@ -2,7 +2,7 @@ package mage.cards.c;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.ExileSourceEffect;
 import mage.abilities.effects.common.continuous.BecomesBasicLandTargetEffect;
 import mage.constants.Duration;
@@ -27,7 +27,7 @@ public final class CyclopeanGiant extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Cyclopean Giant dies, target land becomes a Swamp. Exile Cyclopean Giant.
-        DiesTriggeredAbility ability = new DiesTriggeredAbility(new BecomesBasicLandTargetEffect(Duration.EndOfGame, SubType.SWAMP));
+        DiesSourceTriggeredAbility ability = new DiesSourceTriggeredAbility(new BecomesBasicLandTargetEffect(Duration.EndOfGame, SubType.SWAMP));
         ability.addEffect(new ExileSourceEffect());
         ability.addTarget(new TargetLandPermanent());
         this.addAbility(ability);
diff --git a/Mage.Sets/src/mage/cards/c/CyclopeanMummy.java b/Mage.Sets/src/mage/cards/c/CyclopeanMummy.java
index aa72a16878..e8f6458b48 100644
--- a/Mage.Sets/src/mage/cards/c/CyclopeanMummy.java
+++ b/Mage.Sets/src/mage/cards/c/CyclopeanMummy.java
@@ -3,7 +3,7 @@ package mage.cards.c;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.ExileSourceEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -23,7 +23,7 @@ public final class CyclopeanMummy extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Cyclopean Mummy dies, exile it.
-        this.addAbility(new DiesTriggeredAbility(new ExileSourceEffect()));
+        this.addAbility(new DiesSourceTriggeredAbility(new ExileSourceEffect()));
     }
 
     public CyclopeanMummy(final CyclopeanMummy card) {
diff --git a/Mage.Sets/src/mage/cards/d/DarkRevenant.java b/Mage.Sets/src/mage/cards/d/DarkRevenant.java
index 8e404392ec..58206923ac 100644
--- a/Mage.Sets/src/mage/cards/d/DarkRevenant.java
+++ b/Mage.Sets/src/mage/cards/d/DarkRevenant.java
@@ -4,7 +4,7 @@ package mage.cards.d;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.Card;
@@ -35,7 +35,7 @@ public final class DarkRevenant extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
 
         // When Dark Revenant dies, put it on top of its owner's library.
-        this.addAbility(new DiesTriggeredAbility(new DarkRevenantEffect()));
+        this.addAbility(new DiesSourceTriggeredAbility(new DarkRevenantEffect()));
     }
 
     public DarkRevenant(final DarkRevenant card) {
diff --git a/Mage.Sets/src/mage/cards/d/DarkslickDrake.java b/Mage.Sets/src/mage/cards/d/DarkslickDrake.java
index 33d29cf7df..a47db43bfc 100644
--- a/Mage.Sets/src/mage/cards/d/DarkslickDrake.java
+++ b/Mage.Sets/src/mage/cards/d/DarkslickDrake.java
@@ -4,7 +4,7 @@ package mage.cards.d;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
@@ -25,7 +25,7 @@ public final class DarkslickDrake extends CardImpl {
         this.power = new MageInt(2);
         this.toughness = new MageInt(4);
         this.addAbility(FlyingAbility.getInstance());
-        this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(1), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1), false));
     }
 
     public DarkslickDrake (final DarkslickDrake card) {
diff --git a/Mage.Sets/src/mage/cards/d/DeadbridgeShaman.java b/Mage.Sets/src/mage/cards/d/DeadbridgeShaman.java
index 8c8f834a9b..473fed7dfa 100644
--- a/Mage.Sets/src/mage/cards/d/DeadbridgeShaman.java
+++ b/Mage.Sets/src/mage/cards/d/DeadbridgeShaman.java
@@ -4,7 +4,7 @@ package mage.cards.d;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.discard.DiscardTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -26,7 +26,7 @@ public final class DeadbridgeShaman extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Deadbridge Shaman dies, target opponent discards a card.
-        Ability ability = new DiesTriggeredAbility(new DiscardTargetEffect(1));
+        Ability ability = new DiesSourceTriggeredAbility(new DiscardTargetEffect(1));
         ability.addTarget(new TargetOpponent());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/d/DeadlyGrub.java b/Mage.Sets/src/mage/cards/d/DeadlyGrub.java
index ddf8afee2d..1e836e6731 100644
--- a/Mage.Sets/src/mage/cards/d/DeadlyGrub.java
+++ b/Mage.Sets/src/mage/cards/d/DeadlyGrub.java
@@ -4,7 +4,7 @@ package mage.cards.d;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldAbility;
 import mage.abilities.condition.common.LastTimeCounterRemovedCondition;
 import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
@@ -38,7 +38,7 @@ public final class DeadlyGrub extends CardImpl {
         this.addAbility(new VanishingUpkeepAbility(3));
         this.addAbility(new VanishingSacrificeAbility());
         // When Deadly Grub dies, if it had no time counters on it, create a 6/1 green Insect creature token with shroud.
-        this.addAbility(new ConditionalInterveningIfTriggeredAbility(new DiesTriggeredAbility(new CreateTokenEffect(new DeadlyGrubToken(), 1)),
+        this.addAbility(new ConditionalInterveningIfTriggeredAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new DeadlyGrubToken(), 1)),
                 LastTimeCounterRemovedCondition.instance, "When {this} dies, if it had no time counters on it, create a 6/1 green Insect creature token with shroud."));
     }
 
diff --git a/Mage.Sets/src/mage/cards/d/DeathbloomThallid.java b/Mage.Sets/src/mage/cards/d/DeathbloomThallid.java
index 31edb39de0..b8b8a048a1 100644
--- a/Mage.Sets/src/mage/cards/d/DeathbloomThallid.java
+++ b/Mage.Sets/src/mage/cards/d/DeathbloomThallid.java
@@ -3,7 +3,7 @@ package mage.cards.d;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.constants.SubType;
 import mage.cards.CardImpl;
@@ -25,7 +25,7 @@ public final class DeathbloomThallid extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Deathbloom Thallid dies, create a 1/1 green Saproling creature token.
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new SaprolingToken()), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new SaprolingToken()), false));
     }
 
     public DeathbloomThallid(final DeathbloomThallid card) {
diff --git a/Mage.Sets/src/mage/cards/d/DeathcurseOgre.java b/Mage.Sets/src/mage/cards/d/DeathcurseOgre.java
index d6fd4f0370..6d5c1ba31f 100644
--- a/Mage.Sets/src/mage/cards/d/DeathcurseOgre.java
+++ b/Mage.Sets/src/mage/cards/d/DeathcurseOgre.java
@@ -4,7 +4,7 @@ package mage.cards.d;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.LoseLifeAllPlayersEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -24,7 +24,7 @@ public final class DeathcurseOgre extends CardImpl {
 
         this.power = new MageInt(3);
         this.toughness = new MageInt(3);
-        this.addAbility(new DiesTriggeredAbility(new LoseLifeAllPlayersEffect(3)));
+        this.addAbility(new DiesSourceTriggeredAbility(new LoseLifeAllPlayersEffect(3)));
     }
 
     public DeathcurseOgre (final DeathcurseOgre card) {
diff --git a/Mage.Sets/src/mage/cards/d/DeathpactAngel.java b/Mage.Sets/src/mage/cards/d/DeathpactAngel.java
index ebcca304ea..8b6378a8f7 100644
--- a/Mage.Sets/src/mage/cards/d/DeathpactAngel.java
+++ b/Mage.Sets/src/mage/cards/d/DeathpactAngel.java
@@ -2,7 +2,7 @@ package mage.cards.d;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
@@ -27,7 +27,7 @@ public final class DeathpactAngel extends CardImpl {
         //Flying
         this.addAbility(FlyingAbility.getInstance());
         //When Deathpact Angel dies, create a 1/1 white and black Cleric creature token. It has "{3}{W}{B}{B}, {T}, Sacrifice this creature: Return a card named Deathpact Angel from your graveyard to the battlefield."
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new DeathpactAngelToken())));
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new DeathpactAngelToken())));
     }
 
     public DeathpactAngel(final DeathpactAngel card) {
diff --git a/Mage.Sets/src/mage/cards/d/DeathsHeadBuzzard.java b/Mage.Sets/src/mage/cards/d/DeathsHeadBuzzard.java
index 91f87019c4..93d6785fec 100644
--- a/Mage.Sets/src/mage/cards/d/DeathsHeadBuzzard.java
+++ b/Mage.Sets/src/mage/cards/d/DeathsHeadBuzzard.java
@@ -3,7 +3,7 @@ package mage.cards.d;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.continuous.BoostAllEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
@@ -28,7 +28,7 @@ public final class DeathsHeadBuzzard extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
         
         // When Death's-Head Buzzard dies, all creatures get -1/-1 until end of turn.
-        this.addAbility(new DiesTriggeredAbility(new BoostAllEffect(-1, -1, Duration.EndOfTurn)));
+        this.addAbility(new DiesSourceTriggeredAbility(new BoostAllEffect(-1, -1, Duration.EndOfTurn)));
     }
 
     public DeathsHeadBuzzard(final DeathsHeadBuzzard card) {
diff --git a/Mage.Sets/src/mage/cards/d/DesperateSentry.java b/Mage.Sets/src/mage/cards/d/DesperateSentry.java
index 28dc4fe5c5..0cb69932d4 100644
--- a/Mage.Sets/src/mage/cards/d/DesperateSentry.java
+++ b/Mage.Sets/src/mage/cards/d/DesperateSentry.java
@@ -4,7 +4,7 @@ import java.util.UUID;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.condition.common.DeliriumCondition;
 import mage.abilities.decorator.ConditionalContinuousEffect;
@@ -32,7 +32,7 @@ public final class DesperateSentry extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Desperate Sentry dies, create a 3/2 colorless Eldrazi Horror creature token.
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new EldraziHorrorToken()), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new EldraziHorrorToken()), false));
 
         // <i>Delirium</i> &mdash; Desperate Sentry gets +3/+0 as long as there are four or more card types among cards in your graveyard.
         ConditionalContinuousEffect effect = new ConditionalContinuousEffect(
diff --git a/Mage.Sets/src/mage/cards/d/DestructorDragon.java b/Mage.Sets/src/mage/cards/d/DestructorDragon.java
index cb37ba3c4b..94a3ca7b08 100644
--- a/Mage.Sets/src/mage/cards/d/DestructorDragon.java
+++ b/Mage.Sets/src/mage/cards/d/DestructorDragon.java
@@ -4,7 +4,7 @@ package mage.cards.d;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DestroyTargetEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
@@ -35,7 +35,7 @@ public final class DestructorDragon extends CardImpl {
         // Flying
         this.addAbility(FlyingAbility.getInstance());
         // When Destructor Dragon dies, destroy target noncreature permanent.
-        Ability ability = new DiesTriggeredAbility(new DestroyTargetEffect(), false);
+        Ability ability = new DiesSourceTriggeredAbility(new DestroyTargetEffect(), false);
         ability.addTarget(new TargetPermanent(filter));
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/d/DireFleetHoarder.java b/Mage.Sets/src/mage/cards/d/DireFleetHoarder.java
index b23b13831b..1c40d69139 100644
--- a/Mage.Sets/src/mage/cards/d/DireFleetHoarder.java
+++ b/Mage.Sets/src/mage/cards/d/DireFleetHoarder.java
@@ -3,7 +3,7 @@ package mage.cards.d;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.constants.SubType;
 import mage.cards.CardImpl;
@@ -26,7 +26,7 @@ public final class DireFleetHoarder extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Dire Fleet Hoarder dies, create a colorless Treasure artifact token with "{t}, Sacrifice this artifact: Add one mana of any color."
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new TreasureToken())));
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new TreasureToken())));
     }
 
     public DireFleetHoarder(final DireFleetHoarder card) {
diff --git a/Mage.Sets/src/mage/cards/d/DiscordantPiper.java b/Mage.Sets/src/mage/cards/d/DiscordantPiper.java
index 2e1a8797da..ce3d17a092 100644
--- a/Mage.Sets/src/mage/cards/d/DiscordantPiper.java
+++ b/Mage.Sets/src/mage/cards/d/DiscordantPiper.java
@@ -1,7 +1,7 @@
 package mage.cards.d;
 
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -25,7 +25,7 @@ public final class DiscordantPiper extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Discordant Piper dies, create a 0/1 white Goat creature token.
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new GoatToken())));
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new GoatToken())));
     }
 
     private DiscordantPiper(final DiscordantPiper card) {
diff --git a/Mage.Sets/src/mage/cards/d/DiseaseCarriers.java b/Mage.Sets/src/mage/cards/d/DiseaseCarriers.java
index 1c94958523..0e926a6ad8 100644
--- a/Mage.Sets/src/mage/cards/d/DiseaseCarriers.java
+++ b/Mage.Sets/src/mage/cards/d/DiseaseCarriers.java
@@ -4,7 +4,7 @@ package mage.cards.d;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.continuous.BoostTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -26,7 +26,7 @@ public final class DiseaseCarriers extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Disease Carriers dies, target creature gets -2/-2 until end of turn.
-        Ability ability = new DiesTriggeredAbility(new BoostTargetEffect(-2, -2, Duration.EndOfTurn), false);
+        Ability ability = new DiesSourceTriggeredAbility(new BoostTargetEffect(-2, -2, Duration.EndOfTurn), false);
         ability.addTarget(new TargetCreaturePermanent());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/d/DoomedDissenter.java b/Mage.Sets/src/mage/cards/d/DoomedDissenter.java
index 04e3dd2cce..08c87cbdc9 100644
--- a/Mage.Sets/src/mage/cards/d/DoomedDissenter.java
+++ b/Mage.Sets/src/mage/cards/d/DoomedDissenter.java
@@ -3,7 +3,7 @@ package mage.cards.d;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -25,7 +25,7 @@ public final class DoomedDissenter extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Doomed Dissenter dies, create a 2/2 black Zombie creature token.        
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new ZombieToken())));
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new ZombieToken())));
     }
 
     public DoomedDissenter(final DoomedDissenter card) {
diff --git a/Mage.Sets/src/mage/cards/d/DoomedTraveler.java b/Mage.Sets/src/mage/cards/d/DoomedTraveler.java
index d1d89ccfa3..cbdbfefeaf 100644
--- a/Mage.Sets/src/mage/cards/d/DoomedTraveler.java
+++ b/Mage.Sets/src/mage/cards/d/DoomedTraveler.java
@@ -3,7 +3,7 @@ package mage.cards.d;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -26,7 +26,7 @@ public final class DoomedTraveler extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Doomed Traveler dies, create a 1/1 white Spirit creature token with flying.
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new SpiritWhiteToken())));
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new SpiritWhiteToken())));
     }
 
     public DoomedTraveler(final DoomedTraveler card) {
diff --git a/Mage.Sets/src/mage/cards/d/DragonEgg.java b/Mage.Sets/src/mage/cards/d/DragonEgg.java
index 7758179a67..defc2e95f8 100644
--- a/Mage.Sets/src/mage/cards/d/DragonEgg.java
+++ b/Mage.Sets/src/mage/cards/d/DragonEgg.java
@@ -2,7 +2,7 @@ package mage.cards.d;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.abilities.keyword.DefenderAbility;
 import mage.cards.CardImpl;
@@ -28,7 +28,7 @@ public final class DragonEgg extends CardImpl {
         this.addAbility(DefenderAbility.getInstance());
 
         // When Dragon Egg dies, create a 2/2 red Dragon creature token with flying. It has "{R}: This creature gets +1/+0 until end of turn".
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new DragonEggDragonToken()), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new DragonEggDragonToken()), false));
 
     }
 
diff --git a/Mage.Sets/src/mage/cards/d/DrainpipeVermin.java b/Mage.Sets/src/mage/cards/d/DrainpipeVermin.java
index 65991fd0e4..23be4152f1 100644
--- a/Mage.Sets/src/mage/cards/d/DrainpipeVermin.java
+++ b/Mage.Sets/src/mage/cards/d/DrainpipeVermin.java
@@ -4,7 +4,7 @@ package mage.cards.d;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.costs.mana.ColoredManaCost;
 import mage.abilities.effects.common.DoIfCostPaid;
 import mage.abilities.effects.common.discard.DiscardTargetEffect;
@@ -29,7 +29,7 @@ public final class DrainpipeVermin extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Drainpipe Vermin dies, you may pay {B}. If you do, target player discards a card.
-        Ability ability = new DiesTriggeredAbility(new DoIfCostPaid(new DiscardTargetEffect(1), new ColoredManaCost(ColoredManaSymbol.B)), false);
+        Ability ability = new DiesSourceTriggeredAbility(new DoIfCostPaid(new DiscardTargetEffect(1), new ColoredManaCost(ColoredManaSymbol.B)), false);
         ability.addTarget(new TargetPlayer());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/d/DreadhordeButcher.java b/Mage.Sets/src/mage/cards/d/DreadhordeButcher.java
index 6e99a3b55f..bd8699396c 100644
--- a/Mage.Sets/src/mage/cards/d/DreadhordeButcher.java
+++ b/Mage.Sets/src/mage/cards/d/DreadhordeButcher.java
@@ -3,7 +3,7 @@ package mage.cards.d;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.dynamicvalue.common.SourcePermanentPowerCount;
 import mage.abilities.effects.common.DamageTargetEffect;
 import mage.abilities.effects.common.counter.AddCountersSourceEffect;
@@ -39,7 +39,7 @@ public final class DreadhordeButcher extends CardImpl {
         ), false).setOrPlaneswalker(true));
 
         // When Dreadhorde Butcher dies, it deals damage equal to its power to any target.
-        Ability ability = new DiesTriggeredAbility(new DamageTargetEffect(
+        Ability ability = new DiesSourceTriggeredAbility(new DamageTargetEffect(
                 new SourcePermanentPowerCount()
         ).setText("it deals damage equal to its power to any target"));
         ability.addTarget(new TargetAnyTarget());
diff --git a/Mage.Sets/src/mage/cards/d/DrippingTongueZubera.java b/Mage.Sets/src/mage/cards/d/DrippingTongueZubera.java
index ed36a32932..ba93772a8b 100644
--- a/Mage.Sets/src/mage/cards/d/DrippingTongueZubera.java
+++ b/Mage.Sets/src/mage/cards/d/DrippingTongueZubera.java
@@ -4,7 +4,7 @@ package mage.cards.d;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.dynamicvalue.common.ZuberasDiedDynamicValue;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.cards.CardImpl;
@@ -27,7 +27,7 @@ public final class DrippingTongueZubera extends CardImpl {
 
         this.power = new MageInt(1);
         this.toughness = new MageInt(2);
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new SpiritToken(), ZuberasDiedDynamicValue.instance), false), new ZuberasDiedWatcher());
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new SpiritToken(), ZuberasDiedDynamicValue.instance), false), new ZuberasDiedWatcher());
     }
 
     public DrippingTongueZubera (final DrippingTongueZubera card) {
diff --git a/Mage.Sets/src/mage/cards/d/DriverOfTheDead.java b/Mage.Sets/src/mage/cards/d/DriverOfTheDead.java
index 06d477e38b..debff34c88 100644
--- a/Mage.Sets/src/mage/cards/d/DriverOfTheDead.java
+++ b/Mage.Sets/src/mage/cards/d/DriverOfTheDead.java
@@ -4,7 +4,7 @@ package mage.cards.d;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -35,7 +35,7 @@ public final class DriverOfTheDead extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Driver of the Dead dies, return target creature card with converted mana cost 2 or less from your graveyard to the battlefield.
-        Ability ability = new DiesTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect(), false);
+        Ability ability = new DiesSourceTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect(), false);
         Target target = new TargetCardInYourGraveyard(filter);
         ability.addTarget(target);
         this.addAbility(ability);
diff --git a/Mage.Sets/src/mage/cards/d/DroidCommando.java b/Mage.Sets/src/mage/cards/d/DroidCommando.java
index 981a61d28a..2c86fd7f72 100644
--- a/Mage.Sets/src/mage/cards/d/DroidCommando.java
+++ b/Mage.Sets/src/mage/cards/d/DroidCommando.java
@@ -4,7 +4,7 @@ package mage.cards.d;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.GainLifeEffect;
 import mage.abilities.effects.common.LoseLifeTargetEffect;
 import mage.abilities.keyword.RepairAbility;
@@ -27,7 +27,7 @@ public final class DroidCommando extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Droid Commando dies, target player loses 2 life and you gain 2 life.
-        Ability ability = new DiesTriggeredAbility(new LoseLifeTargetEffect(2));
+        Ability ability = new DiesSourceTriggeredAbility(new LoseLifeTargetEffect(2));
         ability.addEffect(new GainLifeEffect(2));
         ability.addTarget(new TargetPlayer());
         this.addAbility(ability);
diff --git a/Mage.Sets/src/mage/cards/d/DuskUrchins.java b/Mage.Sets/src/mage/cards/d/DuskUrchins.java
index 81047b88e2..728636cabf 100644
--- a/Mage.Sets/src/mage/cards/d/DuskUrchins.java
+++ b/Mage.Sets/src/mage/cards/d/DuskUrchins.java
@@ -4,7 +4,7 @@ package mage.cards.d;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.common.AttacksOrBlocksTriggeredAbility;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.dynamicvalue.common.CountersSourceCount;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.abilities.effects.common.counter.AddCountersSourceEffect;
@@ -31,7 +31,7 @@ public final class DuskUrchins extends CardImpl {
         this.addAbility(new AttacksOrBlocksTriggeredAbility(new AddCountersSourceEffect(CounterType.M1M1.createInstance()), false));
 
         // When Dusk Urchins dies, draw a card for each -1/-1 counter on it.
-        this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(new CountersSourceCount(CounterType.M1M1))));
+        this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(new CountersSourceCount(CounterType.M1M1))));
 
     }
 
diff --git a/Mage.Sets/src/mage/cards/d/DutifulAttendant.java b/Mage.Sets/src/mage/cards/d/DutifulAttendant.java
index 8262b7050c..46d73e16dc 100644
--- a/Mage.Sets/src/mage/cards/d/DutifulAttendant.java
+++ b/Mage.Sets/src/mage/cards/d/DutifulAttendant.java
@@ -4,7 +4,7 @@ package mage.cards.d;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -34,7 +34,7 @@ public final class DutifulAttendant extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Dutiful Ateendant dies, return another target creature card from your graveyard to your hand.
-        Ability ability = new DiesTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect(), false);
+        Ability ability = new DiesSourceTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect(), false);
         ability.addTarget(new TargetCardInYourGraveyard(filter));
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/e/ElderCathar.java b/Mage.Sets/src/mage/cards/e/ElderCathar.java
index 4a68c92c63..e285ed05aa 100644
--- a/Mage.Sets/src/mage/cards/e/ElderCathar.java
+++ b/Mage.Sets/src/mage/cards/e/ElderCathar.java
@@ -4,7 +4,7 @@ package mage.cards.e;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -31,7 +31,7 @@ public final class ElderCathar extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Elder Cathar dies, put a +1/+1 counter on target creature you control. If that creature is a Human, put two +1/+1 counters on it instead.
-        Ability ability = new DiesTriggeredAbility(new ElderCatharAddCountersTargetEffect(), false);
+        Ability ability = new DiesSourceTriggeredAbility(new ElderCatharAddCountersTargetEffect(), false);
         ability.addTarget(new TargetControlledCreaturePermanent());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/e/ElendaTheDuskRose.java b/Mage.Sets/src/mage/cards/e/ElendaTheDuskRose.java
index f6caea4489..69b841e09c 100644
--- a/Mage.Sets/src/mage/cards/e/ElendaTheDuskRose.java
+++ b/Mage.Sets/src/mage/cards/e/ElendaTheDuskRose.java
@@ -4,7 +4,7 @@ package mage.cards.e;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.common.DiesCreatureTriggeredAbility;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.dynamicvalue.common.SourcePermanentPowerCount;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.abilities.effects.common.counter.AddCountersSourceEffect;
@@ -38,7 +38,7 @@ public final class ElendaTheDuskRose extends CardImpl {
         this.addAbility(new DiesCreatureTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false, true));
 
         // When Elenda dies, create X 1/1 white Vampire creature tokens with lifelink, where X is Elenda's power. 
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new IxalanVampireToken(), new SourcePermanentPowerCount())));
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new IxalanVampireToken(), new SourcePermanentPowerCount())));
     }
 
     public ElendaTheDuskRose(final ElendaTheDuskRose card) {
diff --git a/Mage.Sets/src/mage/cards/e/ElgaudInquisitor.java b/Mage.Sets/src/mage/cards/e/ElgaudInquisitor.java
index 97f1e20c73..dae094242e 100644
--- a/Mage.Sets/src/mage/cards/e/ElgaudInquisitor.java
+++ b/Mage.Sets/src/mage/cards/e/ElgaudInquisitor.java
@@ -3,7 +3,7 @@ package mage.cards.e;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.abilities.keyword.LifelinkAbility;
 import mage.cards.CardImpl;
@@ -28,7 +28,7 @@ public final class ElgaudInquisitor extends CardImpl {
 
         this.addAbility(LifelinkAbility.getInstance());
         // When Elgaud Inquisitor dies, create a 1/1 white Spirit creature token with flying.
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new SpiritWhiteToken())));
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new SpiritWhiteToken())));
     }
 
     public ElgaudInquisitor(final ElgaudInquisitor card) {
diff --git a/Mage.Sets/src/mage/cards/e/ElvishSoultiller.java b/Mage.Sets/src/mage/cards/e/ElvishSoultiller.java
index a7153fbaa5..20cd16aa9c 100644
--- a/Mage.Sets/src/mage/cards/e/ElvishSoultiller.java
+++ b/Mage.Sets/src/mage/cards/e/ElvishSoultiller.java
@@ -6,7 +6,7 @@ import java.util.UUID;
 import mage.MageInt;
 import mage.MageObject;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -34,7 +34,7 @@ public final class ElvishSoultiller extends CardImpl {
         this.toughness = new MageInt(4);
 
         // When Elvish Soultiller dies, choose a creature type. Shuffle all creature cards of that type from your graveyard into your library.
-        addAbility(new DiesTriggeredAbility(new ElvishSoultillerEffect()));
+        addAbility(new DiesSourceTriggeredAbility(new ElvishSoultillerEffect()));
 
     }
 
diff --git a/Mage.Sets/src/mage/cards/e/EmberFistZubera.java b/Mage.Sets/src/mage/cards/e/EmberFistZubera.java
index acfe8a486c..921ad4e910 100644
--- a/Mage.Sets/src/mage/cards/e/EmberFistZubera.java
+++ b/Mage.Sets/src/mage/cards/e/EmberFistZubera.java
@@ -5,7 +5,7 @@ package mage.cards.e;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.dynamicvalue.common.ZuberasDiedDynamicValue;
 import mage.abilities.effects.common.DamageTargetEffect;
 import mage.cards.CardImpl;
@@ -28,7 +28,7 @@ public final class EmberFistZubera extends CardImpl {
 
         this.power = new MageInt(1);
         this.toughness = new MageInt(2);
-        Ability ability = new DiesTriggeredAbility(new DamageTargetEffect(ZuberasDiedDynamicValue.instance));
+        Ability ability = new DiesSourceTriggeredAbility(new DamageTargetEffect(ZuberasDiedDynamicValue.instance));
         ability.addTarget(new TargetAnyTarget());
         this.addAbility(ability, new ZuberasDiedWatcher());
     }
diff --git a/Mage.Sets/src/mage/cards/e/EmielTheBlessed.java b/Mage.Sets/src/mage/cards/e/EmielTheBlessed.java
index 8210d4f6aa..885180cede 100644
--- a/Mage.Sets/src/mage/cards/e/EmielTheBlessed.java
+++ b/Mage.Sets/src/mage/cards/e/EmielTheBlessed.java
@@ -23,6 +23,7 @@ import mage.game.permanent.Permanent;
 import mage.target.TargetPermanent;
 
 import java.util.UUID;
+import mage.abilities.effects.Effect;
 
 /**
  * @author TheElk801
@@ -50,12 +51,13 @@ public final class EmielTheBlessed extends CardImpl {
         ability.addTarget(new TargetPermanent(filter));
         this.addAbility(ability);
 
-        // Whenever another creature enters the battlefield under your control, you may pay {G/W}. If you do, put a +1/+1 counter on it. If it's a Unicorn, put two +1/+1 counters on it instead.
+        // Whenever another creature enters the battlefield under your control, you may pay {G/W}. 
+        // If you do, put a +1/+1 counter on it. If it's a Unicorn, put two +1/+1 counters on it instead.
         this.addAbility(new EntersBattlefieldControlledTriggeredAbility(
                 Zone.BATTLEFIELD, new DoIfCostPaid(new EmielTheBlessedEffect(), new ManaCostsImpl<>("{G/W}")),
                 StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE, false, SetTargetPointer.PERMANENT,
-                "Whenever another creature enters the battlefield under your control, you may pay {G/W}. " +
-                        "If you do, put a +1/+1 counter on it. If it's a Unicorn, put two +1/+1 counters on it instead."
+                "Whenever another creature enters the battlefield under your control, you may pay {G/W}. "
+                + "If you do, put a +1/+1 counter on it. If it's a Unicorn, put two +1/+1 counters on it instead."
         ));
     }
 
diff --git a/Mage.Sets/src/mage/cards/e/EnatuGolem.java b/Mage.Sets/src/mage/cards/e/EnatuGolem.java
index aba9da3ec1..b6f6f92caf 100644
--- a/Mage.Sets/src/mage/cards/e/EnatuGolem.java
+++ b/Mage.Sets/src/mage/cards/e/EnatuGolem.java
@@ -3,7 +3,7 @@ package mage.cards.e;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.GainLifeEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -23,7 +23,7 @@ public final class EnatuGolem extends CardImpl {
         this.power = new MageInt(3);
         this.toughness = new MageInt(5);
 
-        this.addAbility(new DiesTriggeredAbility(new GainLifeEffect(4), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new GainLifeEffect(4), false));
     }
 
     public EnatuGolem(final EnatuGolem card) {
diff --git a/Mage.Sets/src/mage/cards/e/EndlessCockroaches.java b/Mage.Sets/src/mage/cards/e/EndlessCockroaches.java
index ef402fd92d..97b1e014e3 100644
--- a/Mage.Sets/src/mage/cards/e/EndlessCockroaches.java
+++ b/Mage.Sets/src/mage/cards/e/EndlessCockroaches.java
@@ -3,7 +3,7 @@ package mage.cards.e;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.ReturnToHandSourceEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -24,7 +24,7 @@ public final class EndlessCockroaches extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Endless Cockroaches dies, return it to its owner's hand.
-        this.addAbility(new DiesTriggeredAbility(new ReturnToHandSourceEffect()));
+        this.addAbility(new DiesSourceTriggeredAbility(new ReturnToHandSourceEffect()));
     }
 
     public EndlessCockroaches(final EndlessCockroaches card) {
diff --git a/Mage.Sets/src/mage/cards/e/EndlessWhispers.java b/Mage.Sets/src/mage/cards/e/EndlessWhispers.java
index 0eab041fec..4cec197aea 100644
--- a/Mage.Sets/src/mage/cards/e/EndlessWhispers.java
+++ b/Mage.Sets/src/mage/cards/e/EndlessWhispers.java
@@ -4,7 +4,7 @@ package mage.cards.e;
 import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.DelayedTriggeredAbility;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility;
 import mage.abilities.effects.Effect;
@@ -36,7 +36,7 @@ public final class EndlessWhispers extends CardImpl {
         DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ReturnSourceToBattlefieldEffect());
         Effect effect = new CreateDelayedTriggeredAbilityEffect(delayedAbility, true);
         effect.setText("choose target opponent. That player puts this card from its owner's graveyard onto the battlefield under their control at the beginning of the next end step");
-        Ability gainAbility = new DiesTriggeredAbility(effect);
+        Ability gainAbility = new DiesSourceTriggeredAbility(effect);
         gainAbility.addTarget(new TargetOpponent());
 
         effect = new GainAbilityAllEffect(gainAbility, Duration.WhileOnBattlefield, new FilterCreaturePermanent("Each creature"));
diff --git a/Mage.Sets/src/mage/cards/e/Epochrasite.java b/Mage.Sets/src/mage/cards/e/Epochrasite.java
index 96a70cb2b2..f0c0c3cdd0 100644
--- a/Mage.Sets/src/mage/cards/e/Epochrasite.java
+++ b/Mage.Sets/src/mage/cards/e/Epochrasite.java
@@ -5,7 +5,7 @@ import java.util.UUID;
 import mage.MageInt;
 import mage.MageObjectReference;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldAbility;
 import mage.abilities.condition.InvertCondition;
 import mage.abilities.condition.common.CastFromHandSourcePermanentCondition;
@@ -46,7 +46,7 @@ public final class Epochrasite extends CardImpl {
                 new CastFromHandWatcher());
 
         // When Epochrasite dies, exile it with three time counters on it and it gains suspend.
-        this.addAbility(new DiesTriggeredAbility(new EpochrasiteEffect()));
+        this.addAbility(new DiesSourceTriggeredAbility(new EpochrasiteEffect()));
     }
 
     public Epochrasite(final Epochrasite card) {
diff --git a/Mage.Sets/src/mage/cards/e/EsperSojourners.java b/Mage.Sets/src/mage/cards/e/EsperSojourners.java
index ebbaca1dcb..46f72cfd8f 100644
--- a/Mage.Sets/src/mage/cards/e/EsperSojourners.java
+++ b/Mage.Sets/src/mage/cards/e/EsperSojourners.java
@@ -5,7 +5,7 @@ import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.CycleTriggeredAbility;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.common.MayTapOrUntapTargetEffect;
 import mage.abilities.keyword.CyclingAbility;
@@ -34,7 +34,7 @@ public final class EsperSojourners extends CardImpl {
 
         // When you cycle Esper Sojourners or it dies, you may tap or untap target permanent.
         Ability ability1 = new CycleTriggeredAbility(new MayTapOrUntapTargetEffect());
-        Ability ability2 = new DiesTriggeredAbility(new MayTapOrUntapTargetEffect());
+        Ability ability2 = new DiesSourceTriggeredAbility(new MayTapOrUntapTargetEffect());
         ability1.addTarget(new TargetPermanent());
         ability2.addTarget(new TargetPermanent());
         this.addAbility(ability1);
diff --git a/Mage.Sets/src/mage/cards/e/ExiledBoggart.java b/Mage.Sets/src/mage/cards/e/ExiledBoggart.java
index 3d1703ba0d..d78998ea27 100644
--- a/Mage.Sets/src/mage/cards/e/ExiledBoggart.java
+++ b/Mage.Sets/src/mage/cards/e/ExiledBoggart.java
@@ -3,7 +3,7 @@ package mage.cards.e;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.discard.DiscardControllerEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -24,7 +24,7 @@ public final class ExiledBoggart extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Exiled Boggart dies, discard a card.
-        this.addAbility(new DiesTriggeredAbility(new DiscardControllerEffect(1), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new DiscardControllerEffect(1), false));
     }
 
     public ExiledBoggart(final ExiledBoggart card) {
diff --git a/Mage.Sets/src/mage/cards/e/ExultantCultist.java b/Mage.Sets/src/mage/cards/e/ExultantCultist.java
index 5011b0c40e..babb9bf196 100644
--- a/Mage.Sets/src/mage/cards/e/ExultantCultist.java
+++ b/Mage.Sets/src/mage/cards/e/ExultantCultist.java
@@ -3,7 +3,7 @@ package mage.cards.e;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -24,7 +24,7 @@ public final class ExultantCultist extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Exultant Cultist dies, draw a card.
-        this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(1), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1), false));
     }
 
     public ExultantCultist(final ExultantCultist card) {
diff --git a/Mage.Sets/src/mage/cards/f/FalseProphet.java b/Mage.Sets/src/mage/cards/f/FalseProphet.java
index 968b9c3455..6db36bfe8d 100644
--- a/Mage.Sets/src/mage/cards/f/FalseProphet.java
+++ b/Mage.Sets/src/mage/cards/f/FalseProphet.java
@@ -3,7 +3,7 @@ package mage.cards.f;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.ExileAllEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -26,7 +26,7 @@ public final class FalseProphet extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When False Prophet dies, exile all creatures.
-        this.addAbility(new DiesTriggeredAbility(new ExileAllEffect(new FilterCreaturePermanent())));
+        this.addAbility(new DiesSourceTriggeredAbility(new ExileAllEffect(new FilterCreaturePermanent())));
     }
 
     public FalseProphet(final FalseProphet card) {
diff --git a/Mage.Sets/src/mage/cards/f/FeralProwler.java b/Mage.Sets/src/mage/cards/f/FeralProwler.java
index 35935d2fcb..5636aa30e8 100644
--- a/Mage.Sets/src/mage/cards/f/FeralProwler.java
+++ b/Mage.Sets/src/mage/cards/f/FeralProwler.java
@@ -2,7 +2,7 @@ package mage.cards.f;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -18,7 +18,7 @@ public final class FeralProwler extends CardImpl {
         power = new MageInt(1);
         toughness = new MageInt(3);
 
-        addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(1), false));
+        addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1), false));
     }
 
     public FeralProwler(final FeralProwler feralProwler) {
diff --git a/Mage.Sets/src/mage/cards/f/FesteringGoblin.java b/Mage.Sets/src/mage/cards/f/FesteringGoblin.java
index 846165c4b5..a5d737259b 100644
--- a/Mage.Sets/src/mage/cards/f/FesteringGoblin.java
+++ b/Mage.Sets/src/mage/cards/f/FesteringGoblin.java
@@ -4,7 +4,7 @@ package mage.cards.f;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.continuous.BoostTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -26,7 +26,7 @@ public final class FesteringGoblin extends CardImpl {
 
         this.power = new MageInt(1);
         this.toughness = new MageInt(1);
-        Ability ability = new DiesTriggeredAbility(new BoostTargetEffect(-1, -1, Duration.EndOfTurn), false);
+        Ability ability = new DiesSourceTriggeredAbility(new BoostTargetEffect(-1, -1, Duration.EndOfTurn), false);
         ability.addTarget(new TargetCreaturePermanent());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/f/FesteringMummy.java b/Mage.Sets/src/mage/cards/f/FesteringMummy.java
index ce2d4cc4ba..9278a6c6a9 100644
--- a/Mage.Sets/src/mage/cards/f/FesteringMummy.java
+++ b/Mage.Sets/src/mage/cards/f/FesteringMummy.java
@@ -4,7 +4,7 @@ package mage.cards.f;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.counter.AddCountersTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -27,7 +27,7 @@ public final class FesteringMummy extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Festering Mummy dies, you may put a -1/-1 counter on target creature.
-        Ability ability = new DiesTriggeredAbility(new AddCountersTargetEffect(CounterType.M1M1.createInstance()), true);
+        Ability ability = new DiesSourceTriggeredAbility(new AddCountersTargetEffect(CounterType.M1M1.createInstance()), true);
         ability.addTarget(new TargetCreaturePermanent());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/f/FesteringNewt.java b/Mage.Sets/src/mage/cards/f/FesteringNewt.java
index d0219fe32c..21b616b3b6 100644
--- a/Mage.Sets/src/mage/cards/f/FesteringNewt.java
+++ b/Mage.Sets/src/mage/cards/f/FesteringNewt.java
@@ -4,7 +4,7 @@ package mage.cards.f;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.condition.LockedInCondition;
 import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
 import mage.abilities.decorator.ConditionalContinuousEffect;
@@ -45,7 +45,7 @@ public final class FesteringNewt extends CardImpl {
                 new BoostTargetEffect(-1,-1, Duration.EndOfTurn),
                 new LockedInCondition(new PermanentsOnTheBattlefieldCondition(filterBogbrewWitch)),
                 "target creature an opponent controls gets -1/-1 until end of turn. That creature gets -4/-4 instead if you control a creature named Bogbrew Witch");
-        Ability ability = new DiesTriggeredAbility(effect);
+        Ability ability = new DiesSourceTriggeredAbility(effect);
         ability.addTarget(new TargetCreaturePermanent(filterCreature));
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/f/FiligreeCrawler.java b/Mage.Sets/src/mage/cards/f/FiligreeCrawler.java
index 3edcd09dfa..c0ab6710dc 100644
--- a/Mage.Sets/src/mage/cards/f/FiligreeCrawler.java
+++ b/Mage.Sets/src/mage/cards/f/FiligreeCrawler.java
@@ -3,7 +3,7 @@ package mage.cards.f;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -25,7 +25,7 @@ public final class FiligreeCrawler extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Filigree Crawler dies, create a 1/1 colorless Thopter artifact creature token with flying.
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new ThopterColorlessToken())));
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new ThopterColorlessToken())));
     }
 
     public FiligreeCrawler(final FiligreeCrawler card) {
diff --git a/Mage.Sets/src/mage/cards/f/FiligreeFamiliar.java b/Mage.Sets/src/mage/cards/f/FiligreeFamiliar.java
index 854c79187c..50a977f31f 100644
--- a/Mage.Sets/src/mage/cards/f/FiligreeFamiliar.java
+++ b/Mage.Sets/src/mage/cards/f/FiligreeFamiliar.java
@@ -3,7 +3,7 @@ package mage.cards.f;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.abilities.effects.common.GainLifeEffect;
@@ -28,7 +28,7 @@ public final class FiligreeFamiliar extends CardImpl {
         this.addAbility(new EntersBattlefieldTriggeredAbility(new GainLifeEffect(2), false));
 
         // When Filigree Familiar dies, draw a card.
-        this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(1), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1), false));
     }
 
     public FiligreeFamiliar(final FiligreeFamiliar card) {
diff --git a/Mage.Sets/src/mage/cards/f/FireSnake.java b/Mage.Sets/src/mage/cards/f/FireSnake.java
index 2f6dca506d..1a04253391 100644
--- a/Mage.Sets/src/mage/cards/f/FireSnake.java
+++ b/Mage.Sets/src/mage/cards/f/FireSnake.java
@@ -4,7 +4,7 @@ package mage.cards.f;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DestroyTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -25,7 +25,7 @@ public final class FireSnake extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Fire Snake dies, destroy target land.
-        Ability ability = new DiesTriggeredAbility(new DestroyTargetEffect(), false);
+        Ability ability = new DiesSourceTriggeredAbility(new DestroyTargetEffect(), false);
         ability.addTarget(new TargetLandPermanent());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/f/FlameWreathedPhoenix.java b/Mage.Sets/src/mage/cards/f/FlameWreathedPhoenix.java
index e3a7203845..fda65cb18d 100644
--- a/Mage.Sets/src/mage/cards/f/FlameWreathedPhoenix.java
+++ b/Mage.Sets/src/mage/cards/f/FlameWreathedPhoenix.java
@@ -4,7 +4,7 @@ package mage.cards.f;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.TriggeredAbility;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.condition.common.TributeNotPaidCondition;
 import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
@@ -39,7 +39,7 @@ public final class FlameWreathedPhoenix extends CardImpl {
         this.addAbility(new TributeAbility(2));
         // When Flame-Wreathed Phoenix enters the battlefield, if its tribute wasn't paid, it gains haste and "When this creature dies, return it to its owner's hand."
         TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new GainAbilitySourceEffect(HasteAbility.getInstance(), Duration.WhileOnBattlefield));
-        Effect effect = new GainAbilitySourceEffect(new DiesTriggeredAbility(new ReturnToHandSourceEffect()));
+        Effect effect = new GainAbilitySourceEffect(new DiesSourceTriggeredAbility(new ReturnToHandSourceEffect()));
         ability.addEffect(effect);
         this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, TributeNotPaidCondition.instance,
                 "When {this} enters the battlefield, if its tribute wasn't paid, it gains haste and \"When this creature dies, return it to its owner's hand.\""));
diff --git a/Mage.Sets/src/mage/cards/f/FleshCarver.java b/Mage.Sets/src/mage/cards/f/FleshCarver.java
index 96e7e22f3a..1ef0f7f28a 100644
--- a/Mage.Sets/src/mage/cards/f/FleshCarver.java
+++ b/Mage.Sets/src/mage/cards/f/FleshCarver.java
@@ -4,7 +4,7 @@ package mage.cards.f;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.common.SacrificeTargetCost;
 import mage.abilities.costs.mana.ManaCostsImpl;
@@ -63,7 +63,7 @@ public final class FleshCarver extends CardImpl {
     }
 }
 
-class FleshCarverAbility extends DiesTriggeredAbility {
+class FleshCarverAbility extends DiesSourceTriggeredAbility {
 
     public FleshCarverAbility() {
         super(new FleshCarverEffect(), false);
diff --git a/Mage.Sets/src/mage/cards/f/FlightSpellbomb.java b/Mage.Sets/src/mage/cards/f/FlightSpellbomb.java
index 2d5368f786..35162b4f3a 100644
--- a/Mage.Sets/src/mage/cards/f/FlightSpellbomb.java
+++ b/Mage.Sets/src/mage/cards/f/FlightSpellbomb.java
@@ -4,7 +4,7 @@ package mage.cards.f;
 
 import java.util.UUID;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.common.SacrificeSourceCost;
 import mage.abilities.costs.common.TapSourceCost;
@@ -32,7 +32,7 @@ public final class FlightSpellbomb extends CardImpl {
         ability.addCost(new SacrificeSourceCost());
         ability.addTarget(new TargetCreaturePermanent());
         this.addAbility(ability);
-        this.addAbility(new DiesTriggeredAbility(new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{U}")), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{U}")), false));
     }
 
     public FlightSpellbomb (final FlightSpellbomb card) {
diff --git a/Mage.Sets/src/mage/cards/f/FloatingDreamZubera.java b/Mage.Sets/src/mage/cards/f/FloatingDreamZubera.java
index f2e77e0b50..ad4c7fd252 100644
--- a/Mage.Sets/src/mage/cards/f/FloatingDreamZubera.java
+++ b/Mage.Sets/src/mage/cards/f/FloatingDreamZubera.java
@@ -4,7 +4,7 @@ package mage.cards.f;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.dynamicvalue.common.ZuberasDiedDynamicValue;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.cards.CardImpl;
@@ -25,7 +25,7 @@ public final class FloatingDreamZubera extends CardImpl {
 
         this.power = new MageInt(1);
         this.toughness = new MageInt(2);
-        this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(ZuberasDiedDynamicValue.instance)), new ZuberasDiedWatcher());
+        this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(ZuberasDiedDynamicValue.instance)), new ZuberasDiedWatcher());
     }
 
     public FloatingDreamZubera(final FloatingDreamZubera card) {
diff --git a/Mage.Sets/src/mage/cards/f/FootlightFiend.java b/Mage.Sets/src/mage/cards/f/FootlightFiend.java
index 6d7d41f979..08d0071347 100644
--- a/Mage.Sets/src/mage/cards/f/FootlightFiend.java
+++ b/Mage.Sets/src/mage/cards/f/FootlightFiend.java
@@ -2,7 +2,7 @@ package mage.cards.f;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DamageTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -25,7 +25,7 @@ public final class FootlightFiend extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Footlight Fiend dies, it deals 1 damage to any target.
-        Ability ability = new DiesTriggeredAbility(new DamageTargetEffect(1, "it"));
+        Ability ability = new DiesSourceTriggeredAbility(new DamageTargetEffect(1, "it"));
         ability.addTarget(new TargetAnyTarget());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/f/ForsakenDrifters.java b/Mage.Sets/src/mage/cards/f/ForsakenDrifters.java
index cf624e8dce..822954776d 100644
--- a/Mage.Sets/src/mage/cards/f/ForsakenDrifters.java
+++ b/Mage.Sets/src/mage/cards/f/ForsakenDrifters.java
@@ -3,7 +3,7 @@ package mage.cards.f;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.PutTopCardOfLibraryIntoGraveControllerEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -24,7 +24,7 @@ public final class ForsakenDrifters extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Forsaken Drifters dies, put the top four cards of your library into your graveyard.
-        this.addAbility(new DiesTriggeredAbility(new PutTopCardOfLibraryIntoGraveControllerEffect(4)));
+        this.addAbility(new DiesSourceTriggeredAbility(new PutTopCardOfLibraryIntoGraveControllerEffect(4)));
     }
 
     public ForsakenDrifters(final ForsakenDrifters card) {
diff --git a/Mage.Sets/src/mage/cards/f/FyndhornDruid.java b/Mage.Sets/src/mage/cards/f/FyndhornDruid.java
index a1b1fb15af..5c3f7b50cb 100644
--- a/Mage.Sets/src/mage/cards/f/FyndhornDruid.java
+++ b/Mage.Sets/src/mage/cards/f/FyndhornDruid.java
@@ -5,7 +5,7 @@ import java.util.UUID;
 import mage.MageInt;
 import mage.MageObjectReference;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.condition.Condition;
 import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
 import mage.abilities.effects.common.GainLifeEffect;
@@ -31,7 +31,7 @@ public final class FyndhornDruid extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Fyndhorn Druid dies, if it was blocked this turn, you gain 4 life.
-        this.addAbility(new ConditionalInterveningIfTriggeredAbility(new DiesTriggeredAbility(new GainLifeEffect(4)), new SourceWasBlockedThisTurnCondition(),
+        this.addAbility(new ConditionalInterveningIfTriggeredAbility(new DiesSourceTriggeredAbility(new GainLifeEffect(4)), new SourceWasBlockedThisTurnCondition(),
                 "When {this} dies, if it was blocked this turn, you gain 4 life."), new WasBlockedThisTurnWatcher());
     }
 
diff --git a/Mage.Sets/src/mage/cards/g/Gamekeeper.java b/Mage.Sets/src/mage/cards/g/Gamekeeper.java
index d3e3ef3ab0..967860a4e2 100644
--- a/Mage.Sets/src/mage/cards/g/Gamekeeper.java
+++ b/Mage.Sets/src/mage/cards/g/Gamekeeper.java
@@ -3,7 +3,7 @@ package mage.cards.g;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.costs.common.ExileSourceFromGraveCost;
 import mage.abilities.effects.common.DoIfCostPaid;
 import mage.abilities.effects.common.RevealCardsFromLibraryUntilEffect;
@@ -27,7 +27,7 @@ public final class Gamekeeper extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Gamekeeper dies, you may exile it. If you do, reveal cards from the top of your library until you reveal a creature card. Put that card onto the battlefield and put all other cards revealed this way into your graveyard.
-        Ability ability = new DiesTriggeredAbility(new DoIfCostPaid(new RevealCardsFromLibraryUntilEffect(StaticFilters.FILTER_CARD_CREATURE, Zone.BATTLEFIELD, Zone.GRAVEYARD), new ExileSourceFromGraveCost(), "Exile to reveal cards from the top of your library until you reveal a creature card?"), false);
+        Ability ability = new DiesSourceTriggeredAbility(new DoIfCostPaid(new RevealCardsFromLibraryUntilEffect(StaticFilters.FILTER_CARD_CREATURE, Zone.BATTLEFIELD, Zone.GRAVEYARD), new ExileSourceFromGraveCost(), "Exile to reveal cards from the top of your library until you reveal a creature card?"), false);
         this.addAbility(ability);
     }
 
diff --git a/Mage.Sets/src/mage/cards/g/GangOfDevils.java b/Mage.Sets/src/mage/cards/g/GangOfDevils.java
index 551c096e7a..db51d712d7 100644
--- a/Mage.Sets/src/mage/cards/g/GangOfDevils.java
+++ b/Mage.Sets/src/mage/cards/g/GangOfDevils.java
@@ -4,7 +4,7 @@ package mage.cards.g;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DamageMultiEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -27,7 +27,7 @@ public final class GangOfDevils extends CardImpl {
         this.toughness = new MageInt(3);
 
         // When Gang of Devils dies, it deals 3 damage divided as you choose among one, two, or three target creatures and/or players.
-        Ability ability = new DiesTriggeredAbility(new DamageMultiEffect(3, "it"));
+        Ability ability = new DiesSourceTriggeredAbility(new DamageMultiEffect(3, "it"));
         ability.addTarget(new TargetAnyTargetAmount(3));
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/g/GarrisonCat.java b/Mage.Sets/src/mage/cards/g/GarrisonCat.java
index cb8a54b512..5050906846 100644
--- a/Mage.Sets/src/mage/cards/g/GarrisonCat.java
+++ b/Mage.Sets/src/mage/cards/g/GarrisonCat.java
@@ -1,7 +1,7 @@
 package mage.cards.g;
 
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -24,7 +24,7 @@ public final class GarrisonCat extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Garrison Cat dies, create a 1/1 white Human Soldier creature token.
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new HumanSoldierToken())));
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new HumanSoldierToken())));
     }
 
     private GarrisonCat(final GarrisonCat card) {
diff --git a/Mage.Sets/src/mage/cards/g/GerrardWeatherlightHero.java b/Mage.Sets/src/mage/cards/g/GerrardWeatherlightHero.java
index 63374c1973..9e627ba1b4 100644
--- a/Mage.Sets/src/mage/cards/g/GerrardWeatherlightHero.java
+++ b/Mage.Sets/src/mage/cards/g/GerrardWeatherlightHero.java
@@ -3,7 +3,7 @@ package mage.cards.g;
 import mage.MageInt;
 import mage.MageObjectReference;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.ExileSourceEffect;
 import mage.abilities.keyword.FirstStrikeAbility;
@@ -40,7 +40,7 @@ public final class GerrardWeatherlightHero extends CardImpl {
         this.addAbility(FirstStrikeAbility.getInstance());
 
         // When Gerrard, Weatherlight Hero dies, exile it and return to the battlefield all artifact and creature cards in your graveyard that were put there from the battlefield this turn.
-        Ability ability = new DiesTriggeredAbility(new ExileSourceEffect().setText("exile it"));
+        Ability ability = new DiesSourceTriggeredAbility(new ExileSourceEffect().setText("exile it"));
         ability.addEffect(new GerrardWeatherlightHeroEffect());
         this.addAbility(ability, new GerrardWeatherlightHeroWatcher());
     }
diff --git a/Mage.Sets/src/mage/cards/g/GiantAlbatross.java b/Mage.Sets/src/mage/cards/g/GiantAlbatross.java
index e81634de13..ceaa56f5b1 100644
--- a/Mage.Sets/src/mage/cards/g/GiantAlbatross.java
+++ b/Mage.Sets/src/mage/cards/g/GiantAlbatross.java
@@ -3,7 +3,7 @@ package mage.cards.g;
 import mage.MageInt;
 import mage.MageObjectReference;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.costs.Cost;
 import mage.abilities.costs.common.PayLifeCost;
 import mage.abilities.costs.mana.ManaCostsImpl;
@@ -39,7 +39,7 @@ public final class GiantAlbatross extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
 
         // When Giant Albatross dies, you may pay {1}{U}. If you do, for each creature that dealt damage to Giant Albatross this turn, destroy that creature unless its controller pays 2 life. A creature destroyed this way can't be regenerated.
-        Ability ability = new DiesTriggeredAbility(new DoIfCostPaid(new GiantAlbatrossEffect(), new ManaCostsImpl("{1}{U}")));
+        Ability ability = new DiesSourceTriggeredAbility(new DoIfCostPaid(new GiantAlbatrossEffect(), new ManaCostsImpl("{1}{U}")));
         this.addAbility(ability);
     }
 
diff --git a/Mage.Sets/src/mage/cards/g/GleamingBarrier.java b/Mage.Sets/src/mage/cards/g/GleamingBarrier.java
index 8a0c60476a..fc03735d67 100644
--- a/Mage.Sets/src/mage/cards/g/GleamingBarrier.java
+++ b/Mage.Sets/src/mage/cards/g/GleamingBarrier.java
@@ -3,7 +3,7 @@ package mage.cards.g;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.abilities.keyword.DefenderAbility;
 import mage.cards.CardImpl;
@@ -29,7 +29,7 @@ public final class GleamingBarrier extends CardImpl {
         this.addAbility(DefenderAbility.getInstance());
 
         // When Gleaming Barrier dies, create a colorless Treasure artifact token with "{t}, Sacrifice this artifact: Add one mana of any color."
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new TreasureToken())));
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new TreasureToken())));
     }
 
     public GleamingBarrier(final GleamingBarrier card) {
diff --git a/Mage.Sets/src/mage/cards/g/GoblinArsonist.java b/Mage.Sets/src/mage/cards/g/GoblinArsonist.java
index df8e6b48d3..3e6e67a7ff 100644
--- a/Mage.Sets/src/mage/cards/g/GoblinArsonist.java
+++ b/Mage.Sets/src/mage/cards/g/GoblinArsonist.java
@@ -4,7 +4,7 @@ package mage.cards.g;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DamageTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -27,7 +27,7 @@ public final class GoblinArsonist extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Goblin Arsonist dies, you may have it deal 1 damage to any target.
-        Ability ability = new DiesTriggeredAbility(new DamageTargetEffect(1), true);
+        Ability ability = new DiesSourceTriggeredAbility(new DamageTargetEffect(1), true);
         ability.addTarget(new TargetAnyTarget());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/g/GoblinAssaultTeam.java b/Mage.Sets/src/mage/cards/g/GoblinAssaultTeam.java
index 6efdad649a..119ff344a2 100644
--- a/Mage.Sets/src/mage/cards/g/GoblinAssaultTeam.java
+++ b/Mage.Sets/src/mage/cards/g/GoblinAssaultTeam.java
@@ -2,7 +2,7 @@ package mage.cards.g;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.counter.AddCountersTargetEffect;
 import mage.abilities.keyword.HasteAbility;
 import mage.cards.CardImpl;
@@ -31,7 +31,7 @@ public final class GoblinAssaultTeam extends CardImpl {
         this.addAbility(HasteAbility.getInstance());
 
         // When Goblin Assault Team dies, put a +1/+1 counter on target creature you control.
-        Ability ability = new DiesTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance()));
+        Ability ability = new DiesSourceTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance()));
         ability.addTarget(new TargetControlledCreaturePermanent());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/g/GoblinGardener.java b/Mage.Sets/src/mage/cards/g/GoblinGardener.java
index 63f5b18591..ae7e2be66e 100644
--- a/Mage.Sets/src/mage/cards/g/GoblinGardener.java
+++ b/Mage.Sets/src/mage/cards/g/GoblinGardener.java
@@ -4,7 +4,7 @@ package mage.cards.g;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DestroyTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -26,7 +26,7 @@ public final class GoblinGardener extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Goblin Gardener dies, destroy target land.
-        Ability ability = new DiesTriggeredAbility(new DestroyTargetEffect(), false);
+        Ability ability = new DiesSourceTriggeredAbility(new DestroyTargetEffect(), false);
         ability.addTarget(new TargetLandPermanent());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/g/GoblinMasons.java b/Mage.Sets/src/mage/cards/g/GoblinMasons.java
index 7d6fff4100..4afef73b5c 100644
--- a/Mage.Sets/src/mage/cards/g/GoblinMasons.java
+++ b/Mage.Sets/src/mage/cards/g/GoblinMasons.java
@@ -3,7 +3,7 @@ package mage.cards.g;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DestroyTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -33,7 +33,7 @@ public final class GoblinMasons extends CardImpl {
         this.toughness = new MageInt(1);
 
         //When Goblin Masons dies, destroy target Wall
-        DiesTriggeredAbility ability = new DiesTriggeredAbility(new DestroyTargetEffect(), false);
+        DiesSourceTriggeredAbility ability = new DiesSourceTriggeredAbility(new DestroyTargetEffect(), false);
         ability.addTarget(new TargetPermanent(filter));
         this.addAbility(ability);
     
diff --git a/Mage.Sets/src/mage/cards/g/GolgariThug.java b/Mage.Sets/src/mage/cards/g/GolgariThug.java
index 98b5759fed..2054216b9a 100644
--- a/Mage.Sets/src/mage/cards/g/GolgariThug.java
+++ b/Mage.Sets/src/mage/cards/g/GolgariThug.java
@@ -4,7 +4,7 @@ package mage.cards.g;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.PutOnLibraryTargetEffect;
 import mage.abilities.keyword.DredgeAbility;
 import mage.cards.CardImpl;
@@ -35,7 +35,7 @@ public final class GolgariThug extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Golgari Thug dies, put target creature card from your graveyard on top of your library.
-        Ability ability = new DiesTriggeredAbility(new PutOnLibraryTargetEffect(true));
+        Ability ability = new DiesSourceTriggeredAbility(new PutOnLibraryTargetEffect(true));
         ability.addTarget(new TargetCardInYourGraveyard(filter));
         this.addAbility(ability);
         // Dredge 4
diff --git a/Mage.Sets/src/mage/cards/g/Greedo.java b/Mage.Sets/src/mage/cards/g/Greedo.java
index 16bf9f64eb..199ed4564f 100644
--- a/Mage.Sets/src/mage/cards/g/Greedo.java
+++ b/Mage.Sets/src/mage/cards/g/Greedo.java
@@ -3,7 +3,7 @@ package mage.cards.g;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.common.continuous.GainAbilityAllEffect;
 import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect;
@@ -45,7 +45,7 @@ public final class Greedo extends CardImpl {
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(FirstStrikeAbility.getInstance(), Duration.WhileOnBattlefield, filter, "Creatures blocking or blocked by {this} have first strike")));
 
         // When Greedo dies, you may search your library for Hunter or Rogue card, reveal it, and put it into your hand.
-        this.addAbility(new DiesTriggeredAbility(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filterCard), true), true));
+        this.addAbility(new DiesSourceTriggeredAbility(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filterCard), true), true));
     }
 
     public Greedo(final Greedo card) {
diff --git a/Mage.Sets/src/mage/cards/g/GreenwardenOfMurasa.java b/Mage.Sets/src/mage/cards/g/GreenwardenOfMurasa.java
index 3a2bdfcaaa..ab52e22056 100644
--- a/Mage.Sets/src/mage/cards/g/GreenwardenOfMurasa.java
+++ b/Mage.Sets/src/mage/cards/g/GreenwardenOfMurasa.java
@@ -4,7 +4,7 @@ package mage.cards.g;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.costs.common.ExileSourceFromGraveCost;
 import mage.abilities.effects.common.DoIfCostPaid;
@@ -33,7 +33,7 @@ public final class GreenwardenOfMurasa extends CardImpl {
         this.addAbility(ability);
 
         // When Greenwarden of  Murasa dies, you may exile it. If you do, return target card from your graveyard to your hand.
-        ability = new DiesTriggeredAbility(new DoIfCostPaid(new ReturnFromGraveyardToHandTargetEffect(), new ExileSourceFromGraveCost(),
+        ability = new DiesSourceTriggeredAbility(new DoIfCostPaid(new ReturnFromGraveyardToHandTargetEffect(), new ExileSourceFromGraveCost(),
                 "Exile {this} and return target card from your graveyard to your hand?", true), false);
         ability.addTarget(new TargetCardInYourGraveyard());
         this.addAbility(ability);
diff --git a/Mage.Sets/src/mage/cards/g/GriefTyrant.java b/Mage.Sets/src/mage/cards/g/GriefTyrant.java
index 603df5b941..c30b52fd2e 100644
--- a/Mage.Sets/src/mage/cards/g/GriefTyrant.java
+++ b/Mage.Sets/src/mage/cards/g/GriefTyrant.java
@@ -4,7 +4,7 @@ package mage.cards.g;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.counter.AddCountersSourceEffect;
@@ -34,7 +34,7 @@ public final class GriefTyrant extends CardImpl {
         this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.M1M1.createInstance(4))));
 
         // When Grief Tyrant dies, put a -1/-1 counter on target creature for each -1/-1 counter on Grief Tyrant.
-        Ability ability = new DiesTriggeredAbility(new GriefTyrantEffect());
+        Ability ability = new DiesSourceTriggeredAbility(new GriefTyrantEffect());
         ability.addTarget(new TargetCreaturePermanent());
         this.addAbility(ability);
         
diff --git a/Mage.Sets/src/mage/cards/g/GrimInitiate.java b/Mage.Sets/src/mage/cards/g/GrimInitiate.java
index ac59582d80..09e20471ba 100644
--- a/Mage.Sets/src/mage/cards/g/GrimInitiate.java
+++ b/Mage.Sets/src/mage/cards/g/GrimInitiate.java
@@ -1,7 +1,7 @@
 package mage.cards.g;
 
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.keyword.AmassEffect;
 import mage.abilities.keyword.FirstStrikeAbility;
 import mage.cards.CardImpl;
@@ -28,7 +28,7 @@ public final class GrimInitiate extends CardImpl {
         this.addAbility(FirstStrikeAbility.getInstance());
 
         // When Grim Initiate dies, amass 1.
-        this.addAbility(new DiesTriggeredAbility(new AmassEffect(1)));
+        this.addAbility(new DiesSourceTriggeredAbility(new AmassEffect(1)));
     }
 
     private GrimInitiate(final GrimInitiate card) {
diff --git a/Mage.Sets/src/mage/cards/g/GrimPhysician.java b/Mage.Sets/src/mage/cards/g/GrimPhysician.java
index 598305c58e..12daab2dd8 100644
--- a/Mage.Sets/src/mage/cards/g/GrimPhysician.java
+++ b/Mage.Sets/src/mage/cards/g/GrimPhysician.java
@@ -2,7 +2,7 @@ package mage.cards.g;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.continuous.BoostTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -25,7 +25,7 @@ public final class GrimPhysician extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Grim Physician dies, target creature an opponent controls gets -1/-1 until end of turn.
-        Ability ability = new DiesTriggeredAbility(new BoostTargetEffect(-1, -1));
+        Ability ability = new DiesSourceTriggeredAbility(new BoostTargetEffect(-1, -1));
         ability.addTarget(new TargetOpponentsCreaturePermanent());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/g/GrixisSojourners.java b/Mage.Sets/src/mage/cards/g/GrixisSojourners.java
index 9c8af92701..1f15137feb 100644
--- a/Mage.Sets/src/mage/cards/g/GrixisSojourners.java
+++ b/Mage.Sets/src/mage/cards/g/GrixisSojourners.java
@@ -5,7 +5,7 @@ import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.CycleTriggeredAbility;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.common.ExileTargetEffect;
 import mage.abilities.keyword.CyclingAbility;
@@ -35,7 +35,7 @@ public final class GrixisSojourners extends CardImpl {
 
         // When you cycle Grixis Sojourners or it dies, you may exile target card from a graveyard.
         Ability ability1 = new CycleTriggeredAbility(new ExileTargetEffect(), true);
-        Ability ability2 = new DiesTriggeredAbility(new ExileTargetEffect(), true);
+        Ability ability2 = new DiesSourceTriggeredAbility(new ExileTargetEffect(), true);
         ability1.addTarget(new TargetCardInASingleGraveyard(1, 1, new FilterCard()));
         ability2.addTarget(new TargetCardInASingleGraveyard(1, 1, new FilterCard()));
         this.addAbility(ability1);
diff --git a/Mage.Sets/src/mage/cards/g/GuanYuSaintedWarrior.java b/Mage.Sets/src/mage/cards/g/GuanYuSaintedWarrior.java
index e47a6ea0b8..76b4dd93d7 100644
--- a/Mage.Sets/src/mage/cards/g/GuanYuSaintedWarrior.java
+++ b/Mage.Sets/src/mage/cards/g/GuanYuSaintedWarrior.java
@@ -3,7 +3,7 @@ package mage.cards.g;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.ShuffleIntoLibrarySourceEffect;
 import mage.abilities.keyword.HorsemanshipAbility;
 import mage.cards.CardImpl;
@@ -30,7 +30,7 @@ public final class GuanYuSaintedWarrior extends CardImpl {
         // Horsemanship
         this.addAbility(HorsemanshipAbility.getInstance());
         // When Guan Yu, Sainted Warrior is put into your graveyard from the battlefield, you may shuffle Guan Yu into your library.
-        this.addAbility(new DiesTriggeredAbility(new ShuffleIntoLibrarySourceEffect(), true));
+        this.addAbility(new DiesSourceTriggeredAbility(new ShuffleIntoLibrarySourceEffect(), true));
     }
 
     public GuanYuSaintedWarrior(final GuanYuSaintedWarrior card) {
diff --git a/Mage.Sets/src/mage/cards/g/GuardianAutomaton.java b/Mage.Sets/src/mage/cards/g/GuardianAutomaton.java
index 29da03f155..62bb99b5ba 100644
--- a/Mage.Sets/src/mage/cards/g/GuardianAutomaton.java
+++ b/Mage.Sets/src/mage/cards/g/GuardianAutomaton.java
@@ -3,7 +3,7 @@ package mage.cards.g;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.GainLifeEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -24,7 +24,7 @@ public final class GuardianAutomaton extends CardImpl {
         this.toughness = new MageInt(3);
 
         // When Guardian Automaton dies, you gain 3 life.
-        this.addAbility(new DiesTriggeredAbility(new GainLifeEffect(3)));
+        this.addAbility(new DiesSourceTriggeredAbility(new GainLifeEffect(3)));
     }
 
     public GuardianAutomaton(final GuardianAutomaton card) {
diff --git a/Mage.Sets/src/mage/cards/h/HaakonStromgaldScourge.java b/Mage.Sets/src/mage/cards/h/HaakonStromgaldScourge.java
index edba324a79..d900893451 100644
--- a/Mage.Sets/src/mage/cards/h/HaakonStromgaldScourge.java
+++ b/Mage.Sets/src/mage/cards/h/HaakonStromgaldScourge.java
@@ -5,7 +5,7 @@ package mage.cards.h;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.AsThoughEffectImpl;
 import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
@@ -41,7 +41,7 @@ public final class HaakonStromgaldScourge extends CardImpl {
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new HaakonPlayKnightsFromGraveyardEffect()));
 
         // When Haakon dies, you lose 2 life.
-        this.addAbility(new DiesTriggeredAbility(new LoseLifeSourceControllerEffect(2)));
+        this.addAbility(new DiesSourceTriggeredAbility(new LoseLifeSourceControllerEffect(2)));
 
     }
 
diff --git a/Mage.Sets/src/mage/cards/h/HallowedSpiritkeeper.java b/Mage.Sets/src/mage/cards/h/HallowedSpiritkeeper.java
index 99be8546da..857fe640b9 100644
--- a/Mage.Sets/src/mage/cards/h/HallowedSpiritkeeper.java
+++ b/Mage.Sets/src/mage/cards/h/HallowedSpiritkeeper.java
@@ -3,7 +3,7 @@ package mage.cards.h;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.CreateTokenEffect;
@@ -34,7 +34,7 @@ public final class HallowedSpiritkeeper extends CardImpl {
         // When Hallowed Spiritkeeper dies, create X 1/1 white Spirit creature tokens with flying, where X is the number of creature cards in your graveyard.
         Effect effect = new CreateTokenEffect(new SpiritWhiteToken(), new CardsInControllerGraveyardCount(new FilterCreatureCard("creature cards")));
         effect.setText("create X 1/1 white Spirit creature tokens with flying, where X is the number of creature cards in your graveyard");
-        this.addAbility(new DiesTriggeredAbility(effect, false));
+        this.addAbility(new DiesSourceTriggeredAbility(effect, false));
 
     }
 
diff --git a/Mage.Sets/src/mage/cards/h/HangarbackWalker.java b/Mage.Sets/src/mage/cards/h/HangarbackWalker.java
index 4570d90376..ce79708ffd 100644
--- a/Mage.Sets/src/mage/cards/h/HangarbackWalker.java
+++ b/Mage.Sets/src/mage/cards/h/HangarbackWalker.java
@@ -4,7 +4,7 @@ package mage.cards.h;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.common.TapSourceCost;
@@ -37,7 +37,7 @@ public final class HangarbackWalker extends CardImpl {
         this.addAbility(new EntersBattlefieldAbility(new EntersBattlefieldWithXCountersEffect(CounterType.P1P1.createInstance())));
 
         // When Hangarback Walker dies, create a 1/1 colorless Thopter artifact creature token with flying for each +1/+1 counter on Hangarback Walker.
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new ThopterColorlessToken(), new CountersSourceCount(CounterType.P1P1)), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new ThopterColorlessToken(), new CountersSourceCount(CounterType.P1P1)), false));
 
         // {1}, {t}: Put a +1/+1 counter on Hangarback Walker.
         Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance()), new GenericManaCost(1));
diff --git a/Mage.Sets/src/mage/cards/h/HarvestHand.java b/Mage.Sets/src/mage/cards/h/HarvestHand.java
index 7661170773..5a888543a5 100644
--- a/Mage.Sets/src/mage/cards/h/HarvestHand.java
+++ b/Mage.Sets/src/mage/cards/h/HarvestHand.java
@@ -5,7 +5,7 @@ import java.util.UUID;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.keyword.TransformAbility;
 import mage.cards.Card;
@@ -34,7 +34,7 @@ public final class HarvestHand extends CardImpl {
 
         // When Harvest Hand dies, return it to the battlefield transformed under your control.
         this.addAbility(new TransformAbility());
-        this.addAbility(new DiesTriggeredAbility(new HarvestHandReturnTransformedEffect()));
+        this.addAbility(new DiesSourceTriggeredAbility(new HarvestHandReturnTransformedEffect()));
     }
 
     public HarvestHand(final HarvestHand card) {
diff --git a/Mage.Sets/src/mage/cards/h/HauntedAngel.java b/Mage.Sets/src/mage/cards/h/HauntedAngel.java
index 7bdf24ead2..ff9fe07823 100644
--- a/Mage.Sets/src/mage/cards/h/HauntedAngel.java
+++ b/Mage.Sets/src/mage/cards/h/HauntedAngel.java
@@ -4,7 +4,7 @@ package mage.cards.h;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.ExileSourceEffect;
 import mage.abilities.keyword.FlyingAbility;
@@ -32,7 +32,7 @@ public final class HauntedAngel extends CardImpl {
         // Flying
         this.addAbility(FlyingAbility.getInstance());
         // When Haunted Angel dies, exile Haunted Angel and each other player creates a 3/3 black Angel creature token with flying.
-        Ability ability = new DiesTriggeredAbility(new ExileSourceEffect());
+        Ability ability = new DiesSourceTriggeredAbility(new ExileSourceEffect());
         ability.addEffect(new HauntedAngelEffect());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/h/HavocDemon.java b/Mage.Sets/src/mage/cards/h/HavocDemon.java
index aa285492e9..2be010eca2 100644
--- a/Mage.Sets/src/mage/cards/h/HavocDemon.java
+++ b/Mage.Sets/src/mage/cards/h/HavocDemon.java
@@ -3,7 +3,7 @@ package mage.cards.h;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.continuous.BoostAllEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
@@ -27,7 +27,7 @@ public final class HavocDemon extends CardImpl {
         // Flying
         this.addAbility(FlyingAbility.getInstance());
         // When Havoc Demon dies, all creatures get -5/-5 until end of turn.
-        this.addAbility(new DiesTriggeredAbility(new BoostAllEffect(-5, -5, Duration.EndOfTurn), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new BoostAllEffect(-5, -5, Duration.EndOfTurn), false));
     }
 
     public HavocDemon(final HavocDemon card) {
diff --git a/Mage.Sets/src/mage/cards/h/Helvault.java b/Mage.Sets/src/mage/cards/h/Helvault.java
index 3a02b95c3a..325e1aadad 100644
--- a/Mage.Sets/src/mage/cards/h/Helvault.java
+++ b/Mage.Sets/src/mage/cards/h/Helvault.java
@@ -1,6 +1,6 @@
 package mage.cards.h;
 
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.common.TapSourceCost;
 import mage.abilities.costs.mana.GenericManaCost;
@@ -39,7 +39,7 @@ public final class Helvault extends CardImpl {
         this.addAbility(ability);
 
         // When Helvault is put into a graveyard from the battlefield, return all cards exiled with it to the battlefield under their owners' control.
-        this.addAbility(new DiesTriggeredAbility(new ReturnFromExileForSourceEffect(Zone.BATTLEFIELD)));
+        this.addAbility(new DiesSourceTriggeredAbility(new ReturnFromExileForSourceEffect(Zone.BATTLEFIELD)));
     }
 
     private Helvault(final Helvault card) {
diff --git a/Mage.Sets/src/mage/cards/h/HeraldOfTheDreadhorde.java b/Mage.Sets/src/mage/cards/h/HeraldOfTheDreadhorde.java
index ef383f86e4..0538dfcd8b 100644
--- a/Mage.Sets/src/mage/cards/h/HeraldOfTheDreadhorde.java
+++ b/Mage.Sets/src/mage/cards/h/HeraldOfTheDreadhorde.java
@@ -1,7 +1,7 @@
 package mage.cards.h;
 
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.keyword.AmassEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -24,7 +24,7 @@ public final class HeraldOfTheDreadhorde extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Herald of the Dreadhorde dies, amass 2.
-        this.addAbility(new DiesTriggeredAbility(new AmassEffect(2)));
+        this.addAbility(new DiesSourceTriggeredAbility(new AmassEffect(2)));
     }
 
     private HeraldOfTheDreadhorde(final HeraldOfTheDreadhorde card) {
diff --git a/Mage.Sets/src/mage/cards/h/HighlandGame.java b/Mage.Sets/src/mage/cards/h/HighlandGame.java
index 27d939079e..65f04583c0 100644
--- a/Mage.Sets/src/mage/cards/h/HighlandGame.java
+++ b/Mage.Sets/src/mage/cards/h/HighlandGame.java
@@ -3,7 +3,7 @@ package mage.cards.h;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.GainLifeEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -24,7 +24,7 @@ public final class HighlandGame extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Highland Game dies, you gain 2 life.
-        this.addAbility(new DiesTriggeredAbility(new GainLifeEffect(2)));
+        this.addAbility(new DiesSourceTriggeredAbility(new GainLifeEffect(2)));
     }
 
     public HighlandGame(final HighlandGame card) {
diff --git a/Mage.Sets/src/mage/cards/h/HoardingDragon.java b/Mage.Sets/src/mage/cards/h/HoardingDragon.java
index a5a5b394b6..1a42bc8d5f 100644
--- a/Mage.Sets/src/mage/cards/h/HoardingDragon.java
+++ b/Mage.Sets/src/mage/cards/h/HoardingDragon.java
@@ -6,7 +6,7 @@ import java.util.UUID;
 import mage.MageInt;
 import mage.MageObject;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.ReturnFromExileEffect;
@@ -44,7 +44,7 @@ public final class HoardingDragon extends CardImpl {
         this.addAbility(new EntersBattlefieldTriggeredAbility(new HoardingDragonEffect(this.getId()), true));
 
         // When Hoarding Dragon dies, you may put the exiled card into its owner's hand.
-        this.addAbility(new DiesTriggeredAbility(new ReturnFromExileEffect(this.getId(), Zone.HAND), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new ReturnFromExileEffect(this.getId(), Zone.HAND), false));
     }
 
     public HoardingDragon(final HoardingDragon card) {
diff --git a/Mage.Sets/src/mage/cards/h/HomuraHumanAscendant.java b/Mage.Sets/src/mage/cards/h/HomuraHumanAscendant.java
index 6a06beb23d..6b9715e047 100644
--- a/Mage.Sets/src/mage/cards/h/HomuraHumanAscendant.java
+++ b/Mage.Sets/src/mage/cards/h/HomuraHumanAscendant.java
@@ -5,7 +5,7 @@ import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.CantBlockAbility;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.condition.common.FlippedCondition;
@@ -50,7 +50,7 @@ public final class HomuraHumanAscendant extends CardImpl {
         // Homura, Human Ascendant can't block.
         this.addAbility(new CantBlockAbility());
         // When Homura dies, return it to the battlefield flipped.
-        this.addAbility(new DiesTriggeredAbility(new HomuraReturnFlippedSourceEffect(new HomurasEssence2())));
+        this.addAbility(new DiesSourceTriggeredAbility(new HomuraReturnFlippedSourceEffect(new HomurasEssence2())));
     }
 
     public HomuraHumanAscendant(final HomuraHumanAscendant card) {
diff --git a/Mage.Sets/src/mage/cards/h/HoodedHydra.java b/Mage.Sets/src/mage/cards/h/HoodedHydra.java
index 379d8270aa..38c59f42d3 100644
--- a/Mage.Sets/src/mage/cards/h/HoodedHydra.java
+++ b/Mage.Sets/src/mage/cards/h/HoodedHydra.java
@@ -4,7 +4,7 @@ package mage.cards.h;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
@@ -41,7 +41,7 @@ public final class HoodedHydra extends CardImpl {
         this.addAbility(new EntersBattlefieldAbility(new EntersBattlefieldWithXCountersEffect(CounterType.P1P1.createInstance())));
 
         // When Hooded Hydra dies, create a 1/1 green Snake creature token for each +1/+1 counter on it.
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new SnakeToken("KTK"), new CountersSourceCount(CounterType.P1P1)), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new SnakeToken("KTK"), new CountersSourceCount(CounterType.P1P1)), false));
 
         // Morph {3}{G}{G}
         this.addAbility(new MorphAbility(this, new ManaCostsImpl("{3}{G}{G}")));
diff --git a/Mage.Sets/src/mage/cards/h/HorizonSpellbomb.java b/Mage.Sets/src/mage/cards/h/HorizonSpellbomb.java
index 5a2246cb10..d2b560305d 100644
--- a/Mage.Sets/src/mage/cards/h/HorizonSpellbomb.java
+++ b/Mage.Sets/src/mage/cards/h/HorizonSpellbomb.java
@@ -1,7 +1,7 @@
 
 package mage.cards.h;
 
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.common.SacrificeSourceCost;
 import mage.abilities.costs.common.TapSourceCost;
@@ -35,7 +35,7 @@ public final class HorizonSpellbomb extends CardImpl {
         ability.addCost(new SacrificeSourceCost());
         this.addAbility(ability);
         // When Horizon Spellbomb is put into a graveyard from the battlefield, you may pay {G}. If you do, draw a card.
-        this.addAbility(new DiesTriggeredAbility(new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{G}"))));
+        this.addAbility(new DiesSourceTriggeredAbility(new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{G}"))));
     }
 
     public HorizonSpellbomb(final HorizonSpellbomb card) {
diff --git a/Mage.Sets/src/mage/cards/h/HornetHarasser.java b/Mage.Sets/src/mage/cards/h/HornetHarasser.java
index b6aa781c47..7cd503d8af 100644
--- a/Mage.Sets/src/mage/cards/h/HornetHarasser.java
+++ b/Mage.Sets/src/mage/cards/h/HornetHarasser.java
@@ -4,7 +4,7 @@ package mage.cards.h;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.continuous.BoostTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -26,7 +26,7 @@ public final class HornetHarasser extends CardImpl {
 
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
-        Ability ability = new DiesTriggeredAbility(new BoostTargetEffect(-2, -2, Duration.EndOfTurn), false);
+        Ability ability = new DiesSourceTriggeredAbility(new BoostTargetEffect(-2, -2, Duration.EndOfTurn), false);
         ability.addTarget(new TargetCreaturePermanent());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/h/HuntedWitness.java b/Mage.Sets/src/mage/cards/h/HuntedWitness.java
index 33c60a3177..436b98c9b3 100644
--- a/Mage.Sets/src/mage/cards/h/HuntedWitness.java
+++ b/Mage.Sets/src/mage/cards/h/HuntedWitness.java
@@ -2,7 +2,7 @@ package mage.cards.h;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.constants.SubType;
 import mage.cards.CardImpl;
@@ -24,7 +24,7 @@ public final class HuntedWitness extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Hunted Witness dies, create a 1/1 white Soldier creature token with lifelink.
-        this.addAbility(new DiesTriggeredAbility(
+        this.addAbility(new DiesSourceTriggeredAbility(
                 new CreateTokenEffect(new SoldierLifelinkToken())
         ));
     }
diff --git a/Mage.Sets/src/mage/cards/h/HurloonShaman.java b/Mage.Sets/src/mage/cards/h/HurloonShaman.java
index c14aee9699..0347659da2 100644
--- a/Mage.Sets/src/mage/cards/h/HurloonShaman.java
+++ b/Mage.Sets/src/mage/cards/h/HurloonShaman.java
@@ -3,7 +3,7 @@ package mage.cards.h;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.SacrificeAllEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -25,7 +25,7 @@ public final class HurloonShaman extends CardImpl {
         this.toughness = new MageInt(3);
 
         // When Hurloon Shaman dies, each player sacrifices a land.
-        this.addAbility(new DiesTriggeredAbility(new SacrificeAllEffect(new FilterControlledLandPermanent("land"))));
+        this.addAbility(new DiesSourceTriggeredAbility(new SacrificeAllEffect(new FilterControlledLandPermanent("land"))));
     }
 
     public HurloonShaman(final HurloonShaman card) {
diff --git a/Mage.Sets/src/mage/cards/i/ImmortalPhoenix.java b/Mage.Sets/src/mage/cards/i/ImmortalPhoenix.java
index e43b33376c..076ea534f2 100644
--- a/Mage.Sets/src/mage/cards/i/ImmortalPhoenix.java
+++ b/Mage.Sets/src/mage/cards/i/ImmortalPhoenix.java
@@ -1,7 +1,7 @@
 package mage.cards.i;
 
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.ReturnToHandSourceEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
@@ -26,7 +26,7 @@ public final class ImmortalPhoenix extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
 
         // When Immortal Phoenix dies, return it to its owner’s hand.
-        this.addAbility(new DiesTriggeredAbility(new ReturnToHandSourceEffect()));
+        this.addAbility(new DiesSourceTriggeredAbility(new ReturnToHandSourceEffect()));
     }
 
     public ImmortalPhoenix(final ImmortalPhoenix card) {
diff --git a/Mage.Sets/src/mage/cards/i/InameAsOne.java b/Mage.Sets/src/mage/cards/i/InameAsOne.java
index 7009a9b60f..9d81ec065f 100644
--- a/Mage.Sets/src/mage/cards/i/InameAsOne.java
+++ b/Mage.Sets/src/mage/cards/i/InameAsOne.java
@@ -5,7 +5,7 @@ import java.util.UUID;
 import mage.MageInt;
 import mage.MageObject;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.condition.common.CastFromHandSourcePermanentCondition;
 import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
@@ -56,7 +56,7 @@ public final class InameAsOne extends CardImpl {
                 new CastFromHandWatcher());
 
         // When Iname as One dies, you may exile it. If you do, return target Spirit permanent card from your graveyard to the battlefield.
-        Ability ability = new DiesTriggeredAbility(new InameAsOneEffect(), false);
+        Ability ability = new DiesSourceTriggeredAbility(new InameAsOneEffect(), false);
         ability.addTarget(new TargetCardInYourGraveyard(filter));
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/i/InameLifeAspect.java b/Mage.Sets/src/mage/cards/i/InameLifeAspect.java
index 91489ac7b0..4ba6cc586d 100644
--- a/Mage.Sets/src/mage/cards/i/InameLifeAspect.java
+++ b/Mage.Sets/src/mage/cards/i/InameLifeAspect.java
@@ -5,7 +5,7 @@ import java.util.UUID;
 import mage.MageInt;
 import mage.MageObject;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.ExileSourceEffect;
@@ -42,7 +42,7 @@ public final class InameLifeAspect extends CardImpl {
         this.toughness = new MageInt(4);
 
         // When Iname, Life Aspect dies, you may exile it. If you do, return any number of target Spirit cards from your graveyard to your hand.
-        Ability ability = new DiesTriggeredAbility(new InameLifeAspectEffect(), false);
+        Ability ability = new DiesSourceTriggeredAbility(new InameLifeAspectEffect(), false);
         ability.addTarget(new TargetCardInYourGraveyard(0, Integer.MAX_VALUE, filter));
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/i/InfectiousHost.java b/Mage.Sets/src/mage/cards/i/InfectiousHost.java
index 8e352d1287..b8740b2a61 100644
--- a/Mage.Sets/src/mage/cards/i/InfectiousHost.java
+++ b/Mage.Sets/src/mage/cards/i/InfectiousHost.java
@@ -4,7 +4,7 @@ package mage.cards.i;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.LoseLifeTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -26,7 +26,7 @@ public final class InfectiousHost extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Infectious Host dies, target player loses 2 life.
-        Ability ability = new DiesTriggeredAbility(new LoseLifeTargetEffect(2), false);
+        Ability ability = new DiesSourceTriggeredAbility(new LoseLifeTargetEffect(2), false);
         ability.addTarget(new TargetPlayer());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/i/InfernalScarring.java b/Mage.Sets/src/mage/cards/i/InfernalScarring.java
index 10cccb69b0..95d47bd72e 100644
--- a/Mage.Sets/src/mage/cards/i/InfernalScarring.java
+++ b/Mage.Sets/src/mage/cards/i/InfernalScarring.java
@@ -3,7 +3,7 @@ package mage.cards.i;
 
 import java.util.UUID;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.AttachEffect;
@@ -43,7 +43,7 @@ public final class InfernalScarring extends CardImpl {
         Effect effect = new BoostEnchantedEffect(2, 0, Duration.WhileOnBattlefield);
         effect.setText("Enchanted creature gets +2/+0");
         ability = new SimpleStaticAbility(Zone.BATTLEFIELD, effect);
-        effect = new GainAbilityAttachedEffect(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(1)), AttachmentType.AURA, Duration.WhileOnBattlefield);
+        effect = new GainAbilityAttachedEffect(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1)), AttachmentType.AURA, Duration.WhileOnBattlefield);
         effect.setText("and has \"When this creature dies, draw a card.\"");
         ability.addEffect(effect);
         this.addAbility(ability);
diff --git a/Mage.Sets/src/mage/cards/i/InsidiousBookworms.java b/Mage.Sets/src/mage/cards/i/InsidiousBookworms.java
index 4f6e4b83be..09e327fa68 100644
--- a/Mage.Sets/src/mage/cards/i/InsidiousBookworms.java
+++ b/Mage.Sets/src/mage/cards/i/InsidiousBookworms.java
@@ -4,7 +4,7 @@ package mage.cards.i;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.common.DoIfCostPaid;
 import mage.abilities.effects.common.discard.DiscardTargetEffect;
@@ -27,7 +27,7 @@ public final class InsidiousBookworms extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Insidious Bookworms dies, you may pay {1}{B}. If you do, target player discards a card at random.
-        Ability ability = new DiesTriggeredAbility(new DoIfCostPaid(new DiscardTargetEffect(1, true), new ManaCostsImpl("{1}{B}")));
+        Ability ability = new DiesSourceTriggeredAbility(new DoIfCostPaid(new DiscardTargetEffect(1, true), new ManaCostsImpl("{1}{B}")));
         ability.addTarget(new TargetPlayer());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/i/IvoryGargoyle.java b/Mage.Sets/src/mage/cards/i/IvoryGargoyle.java
index 0cd6c5e37d..98dcfe2652 100644
--- a/Mage.Sets/src/mage/cards/i/IvoryGargoyle.java
+++ b/Mage.Sets/src/mage/cards/i/IvoryGargoyle.java
@@ -3,7 +3,7 @@ package mage.cards.i;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
@@ -34,7 +34,7 @@ public final class IvoryGargoyle extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
 
         // When Ivory Gargoyle dies, return it to the battlefield under its owner's control at the beginning of the next end step and you skip your next draw step.
-        Ability ability = new DiesTriggeredAbility(new CreateDelayedTriggeredAbilityEffect(
+        Ability ability = new DiesSourceTriggeredAbility(new CreateDelayedTriggeredAbilityEffect(
                 new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ReturnSourceFromGraveyardToBattlefieldEffect())));
         ability.addEffect(new SkipNextDrawStepControllerEffect());
         this.addAbility(ability);
diff --git a/Mage.Sets/src/mage/cards/j/JeskaiSage.java b/Mage.Sets/src/mage/cards/j/JeskaiSage.java
index f5b2c24b15..645dc0bf29 100644
--- a/Mage.Sets/src/mage/cards/j/JeskaiSage.java
+++ b/Mage.Sets/src/mage/cards/j/JeskaiSage.java
@@ -3,7 +3,7 @@ package mage.cards.j;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.abilities.keyword.ProwessAbility;
 import mage.cards.CardImpl;
@@ -27,7 +27,7 @@ public final class JeskaiSage extends CardImpl {
         // Prowess
         this.addAbility(new ProwessAbility());
         // When Jeskai Sage dies, draw a card.
-        this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(1), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1), false));
 
     }
 
diff --git a/Mage.Sets/src/mage/cards/j/JotunOwlKeeper.java b/Mage.Sets/src/mage/cards/j/JotunOwlKeeper.java
index 3fa5faf6fd..0107825f92 100644
--- a/Mage.Sets/src/mage/cards/j/JotunOwlKeeper.java
+++ b/Mage.Sets/src/mage/cards/j/JotunOwlKeeper.java
@@ -3,7 +3,7 @@ package mage.cards.j;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.costs.OrCost;
 import mage.constants.SubType;
 import mage.abilities.costs.mana.ManaCostsImpl;
@@ -37,7 +37,7 @@ public final class JotunOwlKeeper extends CardImpl {
         )));
 
         // When J&ouml;tun Owl Keeper dies, put a 1/1 white Bird creature token with flying onto the battlefield for each age counter on it.
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new BirdToken(), new CountersSourceCount(CounterType.AGE))));
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new BirdToken(), new CountersSourceCount(CounterType.AGE))));
     }
 
     public JotunOwlKeeper(final JotunOwlKeeper card) {
diff --git a/Mage.Sets/src/mage/cards/j/JuganTheRisingStar.java b/Mage.Sets/src/mage/cards/j/JuganTheRisingStar.java
index e51b970247..5e5531682b 100644
--- a/Mage.Sets/src/mage/cards/j/JuganTheRisingStar.java
+++ b/Mage.Sets/src/mage/cards/j/JuganTheRisingStar.java
@@ -5,7 +5,7 @@ package mage.cards.j;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.counter.DistributeCountersEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
@@ -33,7 +33,7 @@ public final class JuganTheRisingStar extends CardImpl {
         // Flying
         this.addAbility(FlyingAbility.getInstance());
         // When Jugan, the Rising Star dies, you may distribute five +1/+1 counters among any number of target creatures.
-        Ability ability = new DiesTriggeredAbility(new DistributeCountersEffect(CounterType.P1P1, 5, false, "any number of target creatures"), true);
+        Ability ability = new DiesSourceTriggeredAbility(new DistributeCountersEffect(CounterType.P1P1, 5, false, "any number of target creatures"), true);
         ability.addTarget(new TargetCreaturePermanentAmount(5));
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/j/JundSojourners.java b/Mage.Sets/src/mage/cards/j/JundSojourners.java
index 8a57e5939f..6c7964133e 100644
--- a/Mage.Sets/src/mage/cards/j/JundSojourners.java
+++ b/Mage.Sets/src/mage/cards/j/JundSojourners.java
@@ -5,7 +5,7 @@ import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.CycleTriggeredAbility;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.common.DamageTargetEffect;
 import mage.abilities.keyword.CyclingAbility;
@@ -34,7 +34,7 @@ public final class JundSojourners extends CardImpl {
 
         // When you cycle Jund Sojourners or it dies, you may have it deal 1 damage to any target.
         Ability ability1 = new CycleTriggeredAbility(new DamageTargetEffect(1));
-        Ability ability2 = new DiesTriggeredAbility(new DamageTargetEffect(1));
+        Ability ability2 = new DiesSourceTriggeredAbility(new DamageTargetEffect(1));
         ability1.addTarget(new TargetAnyTarget());
         ability2.addTarget(new TargetAnyTarget());
         this.addAbility(ability1);
diff --git a/Mage.Sets/src/mage/cards/j/JunkDiver.java b/Mage.Sets/src/mage/cards/j/JunkDiver.java
index f7b14942e3..9d5d731f6c 100644
--- a/Mage.Sets/src/mage/cards/j/JunkDiver.java
+++ b/Mage.Sets/src/mage/cards/j/JunkDiver.java
@@ -4,7 +4,7 @@ package mage.cards.j;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect;
 import mage.abilities.keyword.FlyingAbility;
@@ -40,7 +40,7 @@ public final class JunkDiver extends CardImpl {
         // When Junk Diver dies, return another target artifact card from your graveyard to your hand.
         Effect effect = new ReturnFromGraveyardToHandTargetEffect();
         effect.setText("return another target artifact card from your graveyard to your hand");
-        Ability ability = new DiesTriggeredAbility(effect);
+        Ability ability = new DiesSourceTriggeredAbility(effect);
         ability.addTarget(new TargetCardInYourGraveyard(filter));
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/k/KeigaTheTideStar.java b/Mage.Sets/src/mage/cards/k/KeigaTheTideStar.java
index 3ba20234cc..6559e0b608 100644
--- a/Mage.Sets/src/mage/cards/k/KeigaTheTideStar.java
+++ b/Mage.Sets/src/mage/cards/k/KeigaTheTideStar.java
@@ -5,7 +5,7 @@ package mage.cards.k;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.continuous.GainControlTargetEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
@@ -34,7 +34,7 @@ public final class KeigaTheTideStar extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
 
         // When Keiga, the Tide Star dies, gain control of target creature.
-        Ability ability = new DiesTriggeredAbility(new GainControlTargetEffect(Duration.Custom));
+        Ability ability = new DiesSourceTriggeredAbility(new GainControlTargetEffect(Duration.Custom));
         ability.addTarget(new TargetCreaturePermanent());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/k/Kingfisher.java b/Mage.Sets/src/mage/cards/k/Kingfisher.java
index 1144f22229..92ec2a993f 100644
--- a/Mage.Sets/src/mage/cards/k/Kingfisher.java
+++ b/Mage.Sets/src/mage/cards/k/Kingfisher.java
@@ -3,7 +3,7 @@ package mage.cards.k;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
@@ -26,7 +26,7 @@ public final class Kingfisher extends CardImpl {
         // Flying
         this.addAbility(FlyingAbility.getInstance());
         // When Kingfisher dies, draw a card.
-        this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(1), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1), false));
     }
 
     public Kingfisher(final Kingfisher card) {
diff --git a/Mage.Sets/src/mage/cards/k/KinsbaileBorderguard.java b/Mage.Sets/src/mage/cards/k/KinsbaileBorderguard.java
index d8134ac438..97feb48727 100644
--- a/Mage.Sets/src/mage/cards/k/KinsbaileBorderguard.java
+++ b/Mage.Sets/src/mage/cards/k/KinsbaileBorderguard.java
@@ -4,7 +4,7 @@ package mage.cards.k;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldAbility;
 import mage.abilities.dynamicvalue.DynamicValue;
 import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
@@ -45,7 +45,7 @@ public final class KinsbaileBorderguard extends CardImpl {
         this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(0),
             new PermanentsOnBattlefieldCount(filter), true), "with a +1/+1 counter on it for each other Kithkin you control"));
         // When Kinsbaile Borderguard dies, create a 1/1 white Kithkin Soldier creature token for each counter on it.
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new KithkinToken(), new AllCountersCount())));
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new KithkinToken(), new AllCountersCount())));
     }
 
     public KinsbaileBorderguard(final KinsbaileBorderguard card) {
diff --git a/Mage.Sets/src/mage/cards/k/KokushoTheEveningStar.java b/Mage.Sets/src/mage/cards/k/KokushoTheEveningStar.java
index 0336c4a729..c1881be2aa 100644
--- a/Mage.Sets/src/mage/cards/k/KokushoTheEveningStar.java
+++ b/Mage.Sets/src/mage/cards/k/KokushoTheEveningStar.java
@@ -5,7 +5,7 @@ package mage.cards.k;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
@@ -30,7 +30,7 @@ public final class KokushoTheEveningStar extends CardImpl {
         this.power = new MageInt(5);
         this.toughness = new MageInt(5);
         this.addAbility(FlyingAbility.getInstance());
-        this.addAbility(new DiesTriggeredAbility(new KokushoTheEveningStarEffect(), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new KokushoTheEveningStarEffect(), false));
     }
 
     public KokushoTheEveningStar(final KokushoTheEveningStar card) {
diff --git a/Mage.Sets/src/mage/cards/l/LawlessBroker.java b/Mage.Sets/src/mage/cards/l/LawlessBroker.java
index 12ebf3c256..d1ddc1b929 100644
--- a/Mage.Sets/src/mage/cards/l/LawlessBroker.java
+++ b/Mage.Sets/src/mage/cards/l/LawlessBroker.java
@@ -4,7 +4,7 @@ package mage.cards.l;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.counter.AddCountersTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -27,7 +27,7 @@ public final class LawlessBroker extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Lawless Broker dies, put a +1/+1 counter on target creature you control.
-        Ability ability = new DiesTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance()), false);
+        Ability ability = new DiesSourceTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance()), false);
         ability.addTarget(new TargetControlledCreaturePermanent());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/l/LeoninOfTheLostPride.java b/Mage.Sets/src/mage/cards/l/LeoninOfTheLostPride.java
index 3d5719900f..1fb3efdf0e 100644
--- a/Mage.Sets/src/mage/cards/l/LeoninOfTheLostPride.java
+++ b/Mage.Sets/src/mage/cards/l/LeoninOfTheLostPride.java
@@ -1,7 +1,7 @@
 package mage.cards.l;
 
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.ExileTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -28,7 +28,7 @@ public final class LeoninOfTheLostPride extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Leonin of the Lost Pride dies, exile target card from an opponent’s graveyard.
-        DiesTriggeredAbility diesTriggeredAbility = new DiesTriggeredAbility(new ExileTargetEffect());
+        DiesSourceTriggeredAbility diesTriggeredAbility = new DiesSourceTriggeredAbility(new ExileTargetEffect());
         diesTriggeredAbility.addTarget(new TargetCardInOpponentsGraveyard(new FilterCard("card from an opponent's graveyard")));
         this.addAbility(diesTriggeredAbility);
     }
diff --git a/Mage.Sets/src/mage/cards/l/LiegeOfTheHollows.java b/Mage.Sets/src/mage/cards/l/LiegeOfTheHollows.java
index 0d95599b09..60e87dbe34 100644
--- a/Mage.Sets/src/mage/cards/l/LiegeOfTheHollows.java
+++ b/Mage.Sets/src/mage/cards/l/LiegeOfTheHollows.java
@@ -2,7 +2,7 @@ package mage.cards.l;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.CreateTokenTargetEffect;
@@ -33,7 +33,7 @@ public final class LiegeOfTheHollows extends CardImpl {
 
         // When Liege of the Hollows dies, each player may pay any amount of mana.
         // Then each player creates a number of 1/1 green Squirrel creature tokens equal to the amount of mana they paid this way.
-        this.addAbility(new DiesTriggeredAbility(new LiegeOfTheHollowsEffect()));
+        this.addAbility(new DiesSourceTriggeredAbility(new LiegeOfTheHollowsEffect()));
     }
 
     public LiegeOfTheHollows(final LiegeOfTheHollows card) {
diff --git a/Mage.Sets/src/mage/cards/l/LifebloodHydra.java b/Mage.Sets/src/mage/cards/l/LifebloodHydra.java
index 64488b6dad..7a77212541 100644
--- a/Mage.Sets/src/mage/cards/l/LifebloodHydra.java
+++ b/Mage.Sets/src/mage/cards/l/LifebloodHydra.java
@@ -4,7 +4,7 @@ package mage.cards.l;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.EntersBattlefieldWithXCountersEffect;
@@ -39,7 +39,7 @@ public final class LifebloodHydra extends CardImpl {
         this.addAbility(new EntersBattlefieldAbility(new EntersBattlefieldWithXCountersEffect(CounterType.P1P1.createInstance())));
 
         // When Lifeblood Hydra dies, you gain life and draw cards equal to its power.
-        this.addAbility(new DiesTriggeredAbility(new LifebloodHydraEffect(), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new LifebloodHydraEffect(), false));
     }
 
     public LifebloodHydra(final LifebloodHydra card) {
diff --git a/Mage.Sets/src/mage/cards/l/LightOfTheLegion.java b/Mage.Sets/src/mage/cards/l/LightOfTheLegion.java
index c9581e3949..ebe0aac8bf 100644
--- a/Mage.Sets/src/mage/cards/l/LightOfTheLegion.java
+++ b/Mage.Sets/src/mage/cards/l/LightOfTheLegion.java
@@ -3,7 +3,7 @@ package mage.cards.l;
 import java.util.UUID;
 import mage.MageInt;
 import mage.ObjectColor;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.counter.AddCountersAllEffect;
 import mage.constants.SubType;
 import mage.abilities.keyword.FlyingAbility;
@@ -42,7 +42,7 @@ public final class LightOfTheLegion extends CardImpl {
         this.addAbility(new MentorAbility());
 
         // When Light of the Legion dies, put a +1/+1 counter on each white creature you control.
-        this.addAbility(new DiesTriggeredAbility(new AddCountersAllEffect(
+        this.addAbility(new DiesSourceTriggeredAbility(new AddCountersAllEffect(
                 CounterType.P1P1.createInstance(), filter
         )));
     }
diff --git a/Mage.Sets/src/mage/cards/l/LivingLightning.java b/Mage.Sets/src/mage/cards/l/LivingLightning.java
index 2b887c82c4..d024eb6c2a 100644
--- a/Mage.Sets/src/mage/cards/l/LivingLightning.java
+++ b/Mage.Sets/src/mage/cards/l/LivingLightning.java
@@ -2,7 +2,7 @@ package mage.cards.l;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -31,7 +31,7 @@ public final class LivingLightning extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Living Lightning dies, return target instant or sorcery card from your graveyard to your hand.
-        Ability ability = new DiesTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect());
+        Ability ability = new DiesSourceTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect());
         ability.addTarget(new TargetCardInYourGraveyard(filter));
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/l/LoathsomeCatoblepas.java b/Mage.Sets/src/mage/cards/l/LoathsomeCatoblepas.java
index ad78c80979..306d696d3a 100644
--- a/Mage.Sets/src/mage/cards/l/LoathsomeCatoblepas.java
+++ b/Mage.Sets/src/mage/cards/l/LoathsomeCatoblepas.java
@@ -4,7 +4,7 @@ package mage.cards.l;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.common.combat.MustBeBlockedByAtLeastOneSourceEffect;
@@ -41,7 +41,7 @@ public final class LoathsomeCatoblepas extends CardImpl {
         // {2}{G}: Loathsome Catoblepas must be blocked this turn if able.
         this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new MustBeBlockedByAtLeastOneSourceEffect(), new ManaCostsImpl("{2}{G}")));
         // When Loathsome Catoblepas dies, target creature an opponent controls gets -3/-3 until end of turn.
-        Ability ability = new DiesTriggeredAbility(new BoostTargetEffect(-3,-3, Duration.EndOfTurn), false);
+        Ability ability = new DiesSourceTriggeredAbility(new BoostTargetEffect(-3,-3, Duration.EndOfTurn), false);
         Target target = new TargetCreaturePermanent(filter);
         ability.addTarget(target);
         this.addAbility(ability);
diff --git a/Mage.Sets/src/mage/cards/l/LockjawSnapper.java b/Mage.Sets/src/mage/cards/l/LockjawSnapper.java
index 7fd25e75c7..9ec5b304c2 100644
--- a/Mage.Sets/src/mage/cards/l/LockjawSnapper.java
+++ b/Mage.Sets/src/mage/cards/l/LockjawSnapper.java
@@ -4,7 +4,7 @@ package mage.cards.l;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.keyword.WitherAbility;
 import mage.cards.CardImpl;
@@ -34,7 +34,7 @@ public final class LockjawSnapper extends CardImpl {
         this.addAbility(WitherAbility.getInstance());
         
         // When Lockjaw Snapper dies, put a -1/-1 counter on each creature with a -1/-1 counter on it.
-        this.addAbility(new DiesTriggeredAbility(new LockjawSnapperEffect()));
+        this.addAbility(new DiesSourceTriggeredAbility(new LockjawSnapperEffect()));
         
     }
 
diff --git a/Mage.Sets/src/mage/cards/l/LoyalCathar.java b/Mage.Sets/src/mage/cards/l/LoyalCathar.java
index 8282b63392..8764ce01df 100644
--- a/Mage.Sets/src/mage/cards/l/LoyalCathar.java
+++ b/Mage.Sets/src/mage/cards/l/LoyalCathar.java
@@ -5,7 +5,7 @@ import java.util.UUID;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.OneShotEffect;
@@ -42,7 +42,7 @@ public final class LoyalCathar extends CardImpl {
 
         // When Loyal Cathar dies, return it to the battlefield transformed under your control at the beginning of the next end step.
         this.addAbility(new TransformAbility());
-        this.addAbility(new DiesTriggeredAbility(new LoyalCatharEffect()));
+        this.addAbility(new DiesSourceTriggeredAbility(new LoyalCatharEffect()));
     }
 
     public LoyalCathar(final LoyalCathar card) {
diff --git a/Mage.Sets/src/mage/cards/m/MaalfeldTwins.java b/Mage.Sets/src/mage/cards/m/MaalfeldTwins.java
index 1d080c5ed4..b13f6cb107 100644
--- a/Mage.Sets/src/mage/cards/m/MaalfeldTwins.java
+++ b/Mage.Sets/src/mage/cards/m/MaalfeldTwins.java
@@ -3,7 +3,7 @@ package mage.cards.m;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -25,7 +25,7 @@ public final class MaalfeldTwins extends CardImpl {
         this.toughness = new MageInt(4);
 
         // When Maalfeld Twins dies, create two 2/2 black Zombie creature tokens.
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new ZombieToken(), 2)));
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new ZombieToken(), 2)));
     }
 
     public MaalfeldTwins(final MaalfeldTwins card) {
diff --git a/Mage.Sets/src/mage/cards/m/MagmaPhoenix.java b/Mage.Sets/src/mage/cards/m/MagmaPhoenix.java
index 06b95eb719..848673d978 100644
--- a/Mage.Sets/src/mage/cards/m/MagmaPhoenix.java
+++ b/Mage.Sets/src/mage/cards/m/MagmaPhoenix.java
@@ -4,7 +4,7 @@ package mage.cards.m;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.common.DamageEverythingEffect;
@@ -30,7 +30,7 @@ public final class MagmaPhoenix extends CardImpl {
         this.toughness = new MageInt(3);
 
         this.addAbility(FlyingAbility.getInstance());
-        this.addAbility(new DiesTriggeredAbility(new DamageEverythingEffect(3), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new DamageEverythingEffect(3), false));
         this.addAbility(new SimpleActivatedAbility(Zone.GRAVEYARD, new ReturnSourceFromGraveyardToHandEffect(), new ManaCostsImpl("{3}{R}{R}")));
     }
 
diff --git a/Mage.Sets/src/mage/cards/m/MarkerBeetles.java b/Mage.Sets/src/mage/cards/m/MarkerBeetles.java
index 4e4f6b2af8..14c4c85748 100644
--- a/Mage.Sets/src/mage/cards/m/MarkerBeetles.java
+++ b/Mage.Sets/src/mage/cards/m/MarkerBeetles.java
@@ -4,7 +4,7 @@ package mage.cards.m;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.common.SacrificeSourceCost;
 import mage.abilities.costs.mana.ManaCostsImpl;
@@ -31,7 +31,7 @@ public final class MarkerBeetles extends CardImpl {
         this.toughness = new MageInt(3);
 
         // When Marker Beetles dies, target creature gets +1/+1 until end of turn.
-        Ability ability = new DiesTriggeredAbility(new BoostTargetEffect(1, 1, Duration.EndOfTurn), false);
+        Ability ability = new DiesSourceTriggeredAbility(new BoostTargetEffect(1, 1, Duration.EndOfTurn), false);
         ability.addTarget(new TargetCreaturePermanent());
         this.addAbility(ability);
         // {2}, Sacrifice Marker Beetles: Draw a card.
diff --git a/Mage.Sets/src/mage/cards/m/MartyrForTheCause.java b/Mage.Sets/src/mage/cards/m/MartyrForTheCause.java
index 19a17d1ec8..0cf33c2cd7 100644
--- a/Mage.Sets/src/mage/cards/m/MartyrForTheCause.java
+++ b/Mage.Sets/src/mage/cards/m/MartyrForTheCause.java
@@ -1,7 +1,7 @@
 package mage.cards.m;
 
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.counter.ProliferateEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -24,7 +24,7 @@ public final class MartyrForTheCause extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Martyr for the Cause dies, proliferate. (Choose any number of permanents and/or players, then give each another counter of each kind already there.)
-        this.addAbility(new DiesTriggeredAbility(new ProliferateEffect()));
+        this.addAbility(new DiesSourceTriggeredAbility(new ProliferateEffect()));
     }
 
     private MartyrForTheCause(final MartyrForTheCause card) {
diff --git a/Mage.Sets/src/mage/cards/m/MartyrOfDusk.java b/Mage.Sets/src/mage/cards/m/MartyrOfDusk.java
index 5e3591429d..fcbdf48f5d 100644
--- a/Mage.Sets/src/mage/cards/m/MartyrOfDusk.java
+++ b/Mage.Sets/src/mage/cards/m/MartyrOfDusk.java
@@ -3,7 +3,7 @@ package mage.cards.m;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -25,7 +25,7 @@ public final class MartyrOfDusk extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Martyr of Dusk dies, create a 1/1 white Vampire creature token with lifelink.
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new IxalanVampireToken())));
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new IxalanVampireToken())));
     }
 
     public MartyrOfDusk(final MartyrOfDusk card) {
diff --git a/Mage.Sets/src/mage/cards/m/MathasFiendSeeker.java b/Mage.Sets/src/mage/cards/m/MathasFiendSeeker.java
index 97ef3aca9a..e8c020953c 100644
--- a/Mage.Sets/src/mage/cards/m/MathasFiendSeeker.java
+++ b/Mage.Sets/src/mage/cards/m/MathasFiendSeeker.java
@@ -5,7 +5,7 @@ import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.BeginningOfYourEndStepTriggeredAbility;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.DrawCardAllEffect;
@@ -54,7 +54,7 @@ public final class MathasFiendSeeker extends CardImpl {
         // At the beginning of your end step, put a bounty counter on target creature an opponent controls. For as long as that creature has a bounty counter on it, it has "When this creature dies, each opponent draws a card and gains 2 life."
         Ability ability = new BeginningOfYourEndStepTriggeredAbility(new AddCountersTargetEffect(CounterType.BOUNTY.createInstance()), false);
         ability.addTarget(new TargetCreaturePermanent(filter));
-        Ability ability2 = new DiesTriggeredAbility(new DrawCardAllEffect(1, TargetController.OPPONENT));
+        Ability ability2 = new DiesSourceTriggeredAbility(new DrawCardAllEffect(1, TargetController.OPPONENT));
         ability2.addEffect(new OpponentsGainLifeEffect());
         Effect effect = new MathasFiendSeekerGainAbilityEffect(ability2, Duration.Custom, rule);
         ability.addEffect(effect);
diff --git a/Mage.Sets/src/mage/cards/m/MatterReshaper.java b/Mage.Sets/src/mage/cards/m/MatterReshaper.java
index 164315f45e..5d4455be7c 100644
--- a/Mage.Sets/src/mage/cards/m/MatterReshaper.java
+++ b/Mage.Sets/src/mage/cards/m/MatterReshaper.java
@@ -4,7 +4,7 @@ package mage.cards.m;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.cards.Card;
 import mage.cards.CardImpl;
@@ -34,7 +34,7 @@ public final class MatterReshaper extends CardImpl {
 
         // When Matter Reshaper dies, reveal the top card of your library. You may put that card onto the battlefield
         // if it's a permanent card with converted mana cost 3 or less. Otherwise, put that card into your hand.
-        this.addAbility(new DiesTriggeredAbility(new MatterReshaperEffect(), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new MatterReshaperEffect(), false));
     }
 
     public MatterReshaper(final MatterReshaper card) {
diff --git a/Mage.Sets/src/mage/cards/m/MausoleumGuard.java b/Mage.Sets/src/mage/cards/m/MausoleumGuard.java
index 37cc2384c7..0d64b008ce 100644
--- a/Mage.Sets/src/mage/cards/m/MausoleumGuard.java
+++ b/Mage.Sets/src/mage/cards/m/MausoleumGuard.java
@@ -1,7 +1,7 @@
 package mage.cards.m;
 
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -25,7 +25,7 @@ public final class MausoleumGuard extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Mausoleum Guard dies, create two 1/1 white Spirit creature tokens with flying.
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new SpiritWhiteToken(), 2)));
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new SpiritWhiteToken(), 2)));
     }
 
     public MausoleumGuard(final MausoleumGuard card) {
diff --git a/Mage.Sets/src/mage/cards/m/MerfolkSeer.java b/Mage.Sets/src/mage/cards/m/MerfolkSeer.java
index f7eef20f97..66de865d95 100644
--- a/Mage.Sets/src/mage/cards/m/MerfolkSeer.java
+++ b/Mage.Sets/src/mage/cards/m/MerfolkSeer.java
@@ -3,7 +3,7 @@ package mage.cards.m;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.common.DoIfCostPaid;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
@@ -26,7 +26,7 @@ public final class MerfolkSeer extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Merfolk Seer dies, you may pay {1}{U}. If you do, draw a card.
-        this.addAbility(new DiesTriggeredAbility(new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{1}{U}"))));
+        this.addAbility(new DiesSourceTriggeredAbility(new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{1}{U}"))));
     }
 
     public MerfolkSeer(final MerfolkSeer card) {
diff --git a/Mage.Sets/src/mage/cards/m/MessengerDrake.java b/Mage.Sets/src/mage/cards/m/MessengerDrake.java
index 1c15bcf7b2..d2b1c1e81a 100644
--- a/Mage.Sets/src/mage/cards/m/MessengerDrake.java
+++ b/Mage.Sets/src/mage/cards/m/MessengerDrake.java
@@ -3,7 +3,7 @@ package mage.cards.m;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
@@ -27,7 +27,7 @@ public final class MessengerDrake extends CardImpl {
         // Flying
         this.addAbility(FlyingAbility.getInstance());
         // When Messenger Drake dies, draw a card.
-        this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(1)));
+        this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1)));
     }
 
     public MessengerDrake(final MessengerDrake card) {
diff --git a/Mage.Sets/src/mage/cards/m/MindeyeDrake.java b/Mage.Sets/src/mage/cards/m/MindeyeDrake.java
index a2410f56b8..d693c35d37 100644
--- a/Mage.Sets/src/mage/cards/m/MindeyeDrake.java
+++ b/Mage.Sets/src/mage/cards/m/MindeyeDrake.java
@@ -4,7 +4,7 @@ package mage.cards.m;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.PutLibraryIntoGraveTargetEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
@@ -29,7 +29,7 @@ public final class MindeyeDrake extends CardImpl {
         // Flying
         this.addAbility(FlyingAbility.getInstance());
         // When Mindeye Drake dies, target player puts the top five cards of their library into their graveyard.
-        Ability ability = new DiesTriggeredAbility(new PutLibraryIntoGraveTargetEffect(5));
+        Ability ability = new DiesSourceTriggeredAbility(new PutLibraryIntoGraveTargetEffect(5));
         ability.addTarget(new TargetPlayer());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/m/Mindslicer.java b/Mage.Sets/src/mage/cards/m/Mindslicer.java
index 3d3bab1a09..160481b70c 100644
--- a/Mage.Sets/src/mage/cards/m/Mindslicer.java
+++ b/Mage.Sets/src/mage/cards/m/Mindslicer.java
@@ -3,7 +3,7 @@ package mage.cards.m;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.discard.DiscardHandAllEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -24,7 +24,7 @@ public final class Mindslicer extends CardImpl {
         this.toughness = new MageInt(3);
 
         // When Mindslicer dies, each player discards their hand.
-        this.addAbility(new DiesTriggeredAbility(new DiscardHandAllEffect(),false));
+        this.addAbility(new DiesSourceTriggeredAbility(new DiscardHandAllEffect(),false));
     }
 
     public Mindslicer(final Mindslicer card) {
diff --git a/Mage.Sets/src/mage/cards/m/MistmoonGriffin.java b/Mage.Sets/src/mage/cards/m/MistmoonGriffin.java
index 471aaa6753..98c2e47201 100644
--- a/Mage.Sets/src/mage/cards/m/MistmoonGriffin.java
+++ b/Mage.Sets/src/mage/cards/m/MistmoonGriffin.java
@@ -4,7 +4,7 @@ package mage.cards.m;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.ExileSourceEffect;
 import mage.abilities.keyword.FlyingAbility;
@@ -34,7 +34,7 @@ public final class MistmoonGriffin extends CardImpl {
         // Flying
         this.addAbility(FlyingAbility.getInstance());
         // When Mistmoon Griffin dies, exile Mistmoon Griffin, then return the top creature card of your graveyard to the battlefield.
-        Ability ability = new DiesTriggeredAbility(new ExileSourceEffect());
+        Ability ability = new DiesSourceTriggeredAbility(new ExileSourceEffect());
         ability.addEffect(new MistmoonGriffinEffect());
         this.addAbility(ability);
 
diff --git a/Mage.Sets/src/mage/cards/m/MitoticSlime.java b/Mage.Sets/src/mage/cards/m/MitoticSlime.java
index 73b29d2cf0..1118a07e55 100644
--- a/Mage.Sets/src/mage/cards/m/MitoticSlime.java
+++ b/Mage.Sets/src/mage/cards/m/MitoticSlime.java
@@ -3,7 +3,7 @@ package mage.cards.m;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -24,7 +24,7 @@ public final class MitoticSlime extends CardImpl {
         this.power = new MageInt(4);
         this.toughness = new MageInt(4);
 
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new Ooze2Token(), 2), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new Ooze2Token(), 2), false));
     }
 
     public MitoticSlime(final MitoticSlime card) {
diff --git a/Mage.Sets/src/mage/cards/m/MoldgrafMonstrosity.java b/Mage.Sets/src/mage/cards/m/MoldgrafMonstrosity.java
index 8cbb8e4271..90ee145c1c 100644
--- a/Mage.Sets/src/mage/cards/m/MoldgrafMonstrosity.java
+++ b/Mage.Sets/src/mage/cards/m/MoldgrafMonstrosity.java
@@ -4,7 +4,7 @@ package mage.cards.m;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.ExileSourceEffect;
@@ -39,7 +39,7 @@ public final class MoldgrafMonstrosity extends CardImpl {
         // When Moldgraf Monstrosity dies, exile it, then return two creature cards at random from your graveyard to the battlefield.
         Effect effect = new ExileSourceEffect();
         effect.setText("");
-        DiesTriggeredAbility ability = new DiesTriggeredAbility(effect);
+        DiesSourceTriggeredAbility ability = new DiesSourceTriggeredAbility(effect);
         ability.addEffect(new MoldgrafMonstrosityEffect());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/m/MoltenFirebird.java b/Mage.Sets/src/mage/cards/m/MoltenFirebird.java
index 81001ce6ab..d89a6837bc 100644
--- a/Mage.Sets/src/mage/cards/m/MoltenFirebird.java
+++ b/Mage.Sets/src/mage/cards/m/MoltenFirebird.java
@@ -3,7 +3,7 @@ package mage.cards.m;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility;
@@ -32,7 +32,7 @@ public final class MoltenFirebird extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
 
         // When Molten Firebird dies, return it to the battlefield under its owner’s control at the beginning of the next end step and you skip your next draw step.
-        Ability ability = new DiesTriggeredAbility(new CreateDelayedTriggeredAbilityEffect(
+        Ability ability = new DiesSourceTriggeredAbility(new CreateDelayedTriggeredAbilityEffect(
                 new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ReturnSourceFromGraveyardToBattlefieldEffect())));
         ability.addEffect(new SkipNextDrawStepControllerEffect());
         this.addAbility(ability);
diff --git a/Mage.Sets/src/mage/cards/m/MortisDogs.java b/Mage.Sets/src/mage/cards/m/MortisDogs.java
index 2f138d7819..452a6319c5 100644
--- a/Mage.Sets/src/mage/cards/m/MortisDogs.java
+++ b/Mage.Sets/src/mage/cards/m/MortisDogs.java
@@ -5,7 +5,7 @@ import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.AttacksTriggeredAbility;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.dynamicvalue.common.SourcePermanentPowerCount;
 import mage.abilities.effects.common.LoseLifeTargetEffect;
 import mage.abilities.effects.common.continuous.BoostSourceEffect;
@@ -32,7 +32,7 @@ public final class MortisDogs extends CardImpl {
         // Whenever Mortis Dogs attacks, it gets +2/+0 until end of turn.
         this.addAbility(new AttacksTriggeredAbility(new BoostSourceEffect(2, 0, Duration.EndOfTurn), false));
         // When Mortis Dogs dies, target player loses life equal to its power.
-        Ability ability = new DiesTriggeredAbility(new LoseLifeTargetEffect(new SourcePermanentPowerCount(false)));
+        Ability ability = new DiesSourceTriggeredAbility(new LoseLifeTargetEffect(new SourcePermanentPowerCount(false)));
         ability.addTarget(new TargetPlayer());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/m/MortusStrider.java b/Mage.Sets/src/mage/cards/m/MortusStrider.java
index 664bd3ea85..fb7cd29490 100644
--- a/Mage.Sets/src/mage/cards/m/MortusStrider.java
+++ b/Mage.Sets/src/mage/cards/m/MortusStrider.java
@@ -3,7 +3,7 @@ package mage.cards.m;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.ReturnToHandSourceEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -24,7 +24,7 @@ public final class MortusStrider extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Mortus Strider dies, return it to its owner's hand.
-        this.addAbility(new DiesTriggeredAbility(new ReturnToHandSourceEffect(false)));
+        this.addAbility(new DiesSourceTriggeredAbility(new ReturnToHandSourceEffect(false)));
     }
 
     public MortusStrider(final MortusStrider card) {
diff --git a/Mage.Sets/src/mage/cards/m/MouseDroid.java b/Mage.Sets/src/mage/cards/m/MouseDroid.java
index 1d911de668..f3ee4dbd2f 100644
--- a/Mage.Sets/src/mage/cards/m/MouseDroid.java
+++ b/Mage.Sets/src/mage/cards/m/MouseDroid.java
@@ -2,7 +2,7 @@ package mage.cards.m;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.abilities.keyword.RepairAbility;
 import mage.constants.SubType;
@@ -24,7 +24,7 @@ public final class MouseDroid extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Mouse Droid dies, draw a card.
-        this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(1)));
+        this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1)));
 
         // Repair 3
         this.addAbility(new RepairAbility(3));
diff --git a/Mage.Sets/src/mage/cards/m/MudbuttonTorchrunner.java b/Mage.Sets/src/mage/cards/m/MudbuttonTorchrunner.java
index 577e64980d..07f76c18f0 100644
--- a/Mage.Sets/src/mage/cards/m/MudbuttonTorchrunner.java
+++ b/Mage.Sets/src/mage/cards/m/MudbuttonTorchrunner.java
@@ -4,7 +4,7 @@ package mage.cards.m;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DamageTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -26,7 +26,7 @@ public final class MudbuttonTorchrunner extends CardImpl {
         this.power = new MageInt(1);
         this.toughness = new MageInt(1);
         // When Mudbutton Torchrunner dies, it deals 3 damage to any target.
-        Ability ability = new DiesTriggeredAbility(new DamageTargetEffect(3, "it"), false);
+        Ability ability = new DiesSourceTriggeredAbility(new DamageTargetEffect(3, "it"), false);
         ability.addTarget(new TargetAnyTarget());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/m/MurderousRider.java b/Mage.Sets/src/mage/cards/m/MurderousRider.java
index 7e7864389f..cc42e69eaa 100644
--- a/Mage.Sets/src/mage/cards/m/MurderousRider.java
+++ b/Mage.Sets/src/mage/cards/m/MurderousRider.java
@@ -1,7 +1,7 @@
 package mage.cards.m;
 
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DestroyTargetEffect;
 import mage.abilities.effects.common.LoseLifeSourceControllerEffect;
 import mage.abilities.keyword.LifelinkAbility;
@@ -30,7 +30,7 @@ public final class MurderousRider extends AdventureCard {
         this.addAbility(LifelinkAbility.getInstance());
 
         // When Murderous Rider dies, put it on the bottom of its owner's library.
-        this.addAbility(new DiesTriggeredAbility(new PutOnLibrarySourceEffect(
+        this.addAbility(new DiesSourceTriggeredAbility(new PutOnLibrarySourceEffect(
                 false, "put it on the bottom of its owner's library"
         ), false));
 
diff --git a/Mage.Sets/src/mage/cards/m/MycosynthWellspring.java b/Mage.Sets/src/mage/cards/m/MycosynthWellspring.java
index a79e7bf812..bcf4918447 100644
--- a/Mage.Sets/src/mage/cards/m/MycosynthWellspring.java
+++ b/Mage.Sets/src/mage/cards/m/MycosynthWellspring.java
@@ -1,7 +1,7 @@
 
 package mage.cards.m;
 
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect;
 import mage.cards.CardImpl;
@@ -24,7 +24,7 @@ public final class MycosynthWellspring extends CardImpl {
         // When Mycosynth Wellspring enters the battlefield or is put into a graveyard from the battlefield,
         // you may search your library for a basic land card, reveal it, put it into your hand, then shuffle your library.
         this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true, true), true));
-        this.addAbility(new DiesTriggeredAbility(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true, true), true));
+        this.addAbility(new DiesSourceTriggeredAbility(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true, true), true));
     }
 
     public MycosynthWellspring(final MycosynthWellspring card) {
diff --git a/Mage.Sets/src/mage/cards/m/MyrMoonvessel.java b/Mage.Sets/src/mage/cards/m/MyrMoonvessel.java
index b25556c35a..b31ea7095f 100644
--- a/Mage.Sets/src/mage/cards/m/MyrMoonvessel.java
+++ b/Mage.Sets/src/mage/cards/m/MyrMoonvessel.java
@@ -4,7 +4,7 @@ package mage.cards.m;
 import java.util.UUID;
 import mage.MageInt;
 import mage.Mana;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.mana.BasicManaEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -21,7 +21,7 @@ public final class MyrMoonvessel extends CardImpl {
         this.subtype.add(SubType.MYR);
         this.power = new MageInt(1);
         this.toughness = new MageInt(1);
-        this.addAbility(new DiesTriggeredAbility(new BasicManaEffect(Mana.ColorlessMana(1))));
+        this.addAbility(new DiesSourceTriggeredAbility(new BasicManaEffect(Mana.ColorlessMana(1))));
     }
 
     public MyrMoonvessel(final MyrMoonvessel card) {
diff --git a/Mage.Sets/src/mage/cards/m/MyrRetriever.java b/Mage.Sets/src/mage/cards/m/MyrRetriever.java
index 5d0560a459..2bb166aa7c 100644
--- a/Mage.Sets/src/mage/cards/m/MyrRetriever.java
+++ b/Mage.Sets/src/mage/cards/m/MyrRetriever.java
@@ -4,7 +4,7 @@ package mage.cards.m;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect;
 import mage.cards.CardImpl;
@@ -36,7 +36,7 @@ public final class MyrRetriever extends CardImpl {
         // When Myr Retriever dies, return another target artifact card from your graveyard to your hand.
         Effect effect = new ReturnFromGraveyardToHandTargetEffect();
         effect.setText("return another target artifact card from your graveyard to your hand");
-        Ability ability = new DiesTriggeredAbility(effect);
+        Ability ability = new DiesSourceTriggeredAbility(effect);
         ability.addTarget(new TargetCardInYourGraveyard(filter));
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/m/MyrSire.java b/Mage.Sets/src/mage/cards/m/MyrSire.java
index 2d13b14161..80b28b593c 100644
--- a/Mage.Sets/src/mage/cards/m/MyrSire.java
+++ b/Mage.Sets/src/mage/cards/m/MyrSire.java
@@ -4,7 +4,7 @@ package mage.cards.m;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -23,7 +23,7 @@ public final class MyrSire extends CardImpl {
         this.subtype.add(SubType.MYR);
         this.power = new MageInt(1);
         this.toughness = new MageInt(1);
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new MyrToken())));
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new MyrToken())));
     }
 
     public MyrSire (final MyrSire card) {
diff --git a/Mage.Sets/src/mage/cards/n/NayaSojourners.java b/Mage.Sets/src/mage/cards/n/NayaSojourners.java
index 11b95f9caf..a3b52564ea 100644
--- a/Mage.Sets/src/mage/cards/n/NayaSojourners.java
+++ b/Mage.Sets/src/mage/cards/n/NayaSojourners.java
@@ -5,7 +5,7 @@ import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.CycleTriggeredAbility;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.common.counter.AddCountersTargetEffect;
 import mage.abilities.keyword.CyclingAbility;
@@ -35,7 +35,7 @@ public final class NayaSojourners extends CardImpl {
 
         // When you cycle Naya Sojourners or it dies, you may put a +1/+1 counter on target creature.
         Ability ability1 = new CycleTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance()));
-        Ability ability2 = new DiesTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance()));
+        Ability ability2 = new DiesSourceTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance()));
         ability1.addTarget(new TargetCreaturePermanent());
         ability2.addTarget(new TargetCreaturePermanent());
         this.addAbility(ability1);
diff --git a/Mage.Sets/src/mage/cards/n/Necropede.java b/Mage.Sets/src/mage/cards/n/Necropede.java
index 8819ff3e5e..5ec4b67ac1 100644
--- a/Mage.Sets/src/mage/cards/n/Necropede.java
+++ b/Mage.Sets/src/mage/cards/n/Necropede.java
@@ -5,7 +5,7 @@ package mage.cards.n;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.counter.AddCountersTargetEffect;
 import mage.abilities.keyword.InfectAbility;
 import mage.cards.CardImpl;
@@ -27,7 +27,7 @@ public final class Necropede extends CardImpl {
         this.power = new MageInt(1);
         this.toughness = new MageInt(1);
         this.addAbility(InfectAbility.getInstance());
-        Ability ability = new DiesTriggeredAbility(new AddCountersTargetEffect(CounterType.M1M1.createInstance()), true);
+        Ability ability = new DiesSourceTriggeredAbility(new AddCountersTargetEffect(CounterType.M1M1.createInstance()), true);
         ability.addTarget(new TargetCreaturePermanent());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/n/NihilSpellbomb.java b/Mage.Sets/src/mage/cards/n/NihilSpellbomb.java
index ecc51d332a..e26f856ce8 100644
--- a/Mage.Sets/src/mage/cards/n/NihilSpellbomb.java
+++ b/Mage.Sets/src/mage/cards/n/NihilSpellbomb.java
@@ -2,7 +2,7 @@
 package mage.cards.n;
 
 import java.util.UUID;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.common.SacrificeSourceCost;
 import mage.abilities.costs.common.TapSourceCost;
@@ -31,7 +31,7 @@ public final class NihilSpellbomb extends CardImpl {
         ability.addTarget(new TargetPlayer());
         this.addAbility(ability);
         // When Nihil Spellbomb is put into a graveyard from the battlefield, you may pay {B}. If you do, draw a card.
-        this.addAbility(new DiesTriggeredAbility(new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{B}")), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{B}")), false));
     }
 
     public NihilSpellbomb(final NihilSpellbomb card) {
diff --git a/Mage.Sets/src/mage/cards/n/NobleBenefactor.java b/Mage.Sets/src/mage/cards/n/NobleBenefactor.java
index d223291a3c..7abb0ccf4b 100644
--- a/Mage.Sets/src/mage/cards/n/NobleBenefactor.java
+++ b/Mage.Sets/src/mage/cards/n/NobleBenefactor.java
@@ -3,7 +3,7 @@ package mage.cards.n;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.cards.Card;
 import mage.cards.CardImpl;
@@ -33,7 +33,7 @@ public final class NobleBenefactor extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Noble Benefactor dies, each player may search their library for a card and put that card into their hand. Then each player who searched their library this way shuffles it.
-        this.addAbility(new DiesTriggeredAbility(new NobleBenefactorEffect()));
+        this.addAbility(new DiesSourceTriggeredAbility(new NobleBenefactorEffect()));
     }
 
     public NobleBenefactor(final NobleBenefactor card) {
diff --git a/Mage.Sets/src/mage/cards/n/NocturnalFeeder.java b/Mage.Sets/src/mage/cards/n/NocturnalFeeder.java
index 2415335428..6f1dd06950 100644
--- a/Mage.Sets/src/mage/cards/n/NocturnalFeeder.java
+++ b/Mage.Sets/src/mage/cards/n/NocturnalFeeder.java
@@ -2,7 +2,7 @@ package mage.cards.n;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.GainLifeEffect;
 import mage.abilities.effects.common.LoseLifeOpponentsEffect;
 import mage.abilities.keyword.FlyingAbility;
@@ -30,7 +30,7 @@ public final class NocturnalFeeder extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
 
         // When Nocturnal Feeder dies, each opponent loses 2 life and you gain 2 life.
-        Ability ability = new DiesTriggeredAbility(new LoseLifeOpponentsEffect(2));
+        Ability ability = new DiesSourceTriggeredAbility(new LoseLifeOpponentsEffect(2));
         ability.addEffect(new GainLifeEffect(2).concatBy("and"));
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/n/NoxiousDragon.java b/Mage.Sets/src/mage/cards/n/NoxiousDragon.java
index 0d029e3ad1..eafd0ece77 100644
--- a/Mage.Sets/src/mage/cards/n/NoxiousDragon.java
+++ b/Mage.Sets/src/mage/cards/n/NoxiousDragon.java
@@ -4,7 +4,7 @@ package mage.cards.n;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DestroyTargetEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
@@ -38,7 +38,7 @@ public final class NoxiousDragon extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
 
         // When Noxious Dragon dies, you may destroy target creature with converted mana cost 3 or less.
-        Ability ability = new DiesTriggeredAbility(new DestroyTargetEffect(), true);
+        Ability ability = new DiesSourceTriggeredAbility(new DestroyTargetEffect(), true);
         ability.addTarget(new TargetCreaturePermanent(filter));
         this.addAbility(ability);
         
diff --git a/Mage.Sets/src/mage/cards/n/NoxiousToad.java b/Mage.Sets/src/mage/cards/n/NoxiousToad.java
index c05adfe612..cb9c93f579 100644
--- a/Mage.Sets/src/mage/cards/n/NoxiousToad.java
+++ b/Mage.Sets/src/mage/cards/n/NoxiousToad.java
@@ -3,7 +3,7 @@ package mage.cards.n;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.discard.DiscardEachPlayerEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -24,7 +24,7 @@ public final class NoxiousToad extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Noxious Toad dies, each opponent discards a card.
-        this.addAbility(new DiesTriggeredAbility(new DiscardEachPlayerEffect(TargetController.OPPONENT), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new DiscardEachPlayerEffect(TargetController.OPPONENT), false));
     }
 
     public NoxiousToad(final NoxiousToad card) {
diff --git a/Mage.Sets/src/mage/cards/o/Oculus.java b/Mage.Sets/src/mage/cards/o/Oculus.java
index 77e73712e1..ee4129d7b9 100644
--- a/Mage.Sets/src/mage/cards/o/Oculus.java
+++ b/Mage.Sets/src/mage/cards/o/Oculus.java
@@ -4,7 +4,7 @@ package mage.cards.o;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -23,7 +23,7 @@ public final class Oculus extends CardImpl {
 
         this.power = new MageInt(1);
         this.toughness = new MageInt(1);
-        this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(1), true));
+        this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1), true));
     }
 
     public Oculus (final Oculus card) {
diff --git a/Mage.Sets/src/mage/cards/o/Onulet.java b/Mage.Sets/src/mage/cards/o/Onulet.java
index 3a58d56823..ebf562bda7 100644
--- a/Mage.Sets/src/mage/cards/o/Onulet.java
+++ b/Mage.Sets/src/mage/cards/o/Onulet.java
@@ -3,7 +3,7 @@ package mage.cards.o;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.GainLifeEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -23,7 +23,7 @@ public final class Onulet extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Onulet dies, you gain 2 life.
-        this.addAbility(new DiesTriggeredAbility(new GainLifeEffect(2)));
+        this.addAbility(new DiesSourceTriggeredAbility(new GainLifeEffect(2)));
     }
 
     public Onulet(final Onulet card) {
diff --git a/Mage.Sets/src/mage/cards/o/OrcishHellraiser.java b/Mage.Sets/src/mage/cards/o/OrcishHellraiser.java
index 701bb8d750..a227b3ab60 100644
--- a/Mage.Sets/src/mage/cards/o/OrcishHellraiser.java
+++ b/Mage.Sets/src/mage/cards/o/OrcishHellraiser.java
@@ -2,7 +2,7 @@ package mage.cards.o;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DamageTargetEffect;
 import mage.abilities.keyword.EchoAbility;
 import mage.cards.CardImpl;
@@ -30,7 +30,7 @@ public final class OrcishHellraiser extends CardImpl {
         this.addAbility(new EchoAbility("{R}"));
 
         // When Orcish Hellraiser dies, it deals 2 damage to target player or planeswalker.
-        Ability ability = new DiesTriggeredAbility(new DamageTargetEffect(2, "it"));
+        Ability ability = new DiesSourceTriggeredAbility(new DamageTargetEffect(2, "it"));
         ability.addTarget(new TargetPlayerOrPlaneswalker());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/o/OriginSpellbomb.java b/Mage.Sets/src/mage/cards/o/OriginSpellbomb.java
index 6b4dc0f0f8..eec623e987 100644
--- a/Mage.Sets/src/mage/cards/o/OriginSpellbomb.java
+++ b/Mage.Sets/src/mage/cards/o/OriginSpellbomb.java
@@ -4,7 +4,7 @@ package mage.cards.o;
 
 import java.util.UUID;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.common.SacrificeSourceCost;
 import mage.abilities.costs.common.TapSourceCost;
@@ -31,7 +31,7 @@ public final class OriginSpellbomb extends CardImpl {
         ability.addCost(new TapSourceCost());
         ability.addCost(new SacrificeSourceCost());
         this.addAbility(ability);
-        this.addAbility(new DiesTriggeredAbility(new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{W}")), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{W}")), false));
     }
 
     public OriginSpellbomb (final OriginSpellbomb card) {
diff --git a/Mage.Sets/src/mage/cards/p/PalaceFamiliar.java b/Mage.Sets/src/mage/cards/p/PalaceFamiliar.java
index 50afbd795d..7bf7245f86 100644
--- a/Mage.Sets/src/mage/cards/p/PalaceFamiliar.java
+++ b/Mage.Sets/src/mage/cards/p/PalaceFamiliar.java
@@ -3,7 +3,7 @@ package mage.cards.p;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
@@ -27,7 +27,7 @@ public final class PalaceFamiliar extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
         
         // When Palace Familiar dies, draw a card.
-        this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(1), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1), false));
     }
 
     public PalaceFamiliar(final PalaceFamiliar card) {
diff --git a/Mage.Sets/src/mage/cards/p/PaladinOfAtonement.java b/Mage.Sets/src/mage/cards/p/PaladinOfAtonement.java
index 3b21dd98e7..e0c8ca2b25 100644
--- a/Mage.Sets/src/mage/cards/p/PaladinOfAtonement.java
+++ b/Mage.Sets/src/mage/cards/p/PaladinOfAtonement.java
@@ -4,7 +4,7 @@ package mage.cards.p;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.condition.common.LiveLostLastTurnCondition;
 import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
 import mage.abilities.dynamicvalue.common.SourcePermanentToughnessValue;
@@ -38,7 +38,7 @@ public final class PaladinOfAtonement extends CardImpl {
                 "At the beginning of each upkeep, if you lost life last turn, put a +1/+1 counter on {this}"));
 
         // When Paladin of Atonement dies, you gain life equal to it's toughness.
-        this.addAbility(new DiesTriggeredAbility(new GainLifeEffect(SourcePermanentToughnessValue.getInstance(),
+        this.addAbility(new DiesSourceTriggeredAbility(new GainLifeEffect(SourcePermanentToughnessValue.getInstance(),
                 "you gain life equal to it's toughness")));
     }
 
diff --git a/Mage.Sets/src/mage/cards/p/PelakkaWurm.java b/Mage.Sets/src/mage/cards/p/PelakkaWurm.java
index 0a690d34e8..3acc2b2a80 100644
--- a/Mage.Sets/src/mage/cards/p/PelakkaWurm.java
+++ b/Mage.Sets/src/mage/cards/p/PelakkaWurm.java
@@ -3,7 +3,7 @@ package mage.cards.p;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.abilities.effects.common.GainLifeEffect;
@@ -28,7 +28,7 @@ public final class PelakkaWurm extends CardImpl {
 
         this.addAbility(TrampleAbility.getInstance());
         this.addAbility(new EntersBattlefieldTriggeredAbility(new GainLifeEffect(7), false));
-        this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(1), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1), false));
     }
 
     public PelakkaWurm(final PelakkaWurm card) {
diff --git a/Mage.Sets/src/mage/cards/p/PenumbraBobcat.java b/Mage.Sets/src/mage/cards/p/PenumbraBobcat.java
index cb35264113..0bf0813923 100644
--- a/Mage.Sets/src/mage/cards/p/PenumbraBobcat.java
+++ b/Mage.Sets/src/mage/cards/p/PenumbraBobcat.java
@@ -3,7 +3,7 @@ package mage.cards.p;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -23,7 +23,7 @@ public final class PenumbraBobcat extends CardImpl {
 
         this.power = new MageInt(2);
         this.toughness = new MageInt(1);
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new PenumbraBobcatToken(), 1), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new PenumbraBobcatToken(), 1), false));
     }
 
     public PenumbraBobcat(final PenumbraBobcat card) {
diff --git a/Mage.Sets/src/mage/cards/p/PenumbraKavu.java b/Mage.Sets/src/mage/cards/p/PenumbraKavu.java
index 62f28bb944..39c884d8e7 100644
--- a/Mage.Sets/src/mage/cards/p/PenumbraKavu.java
+++ b/Mage.Sets/src/mage/cards/p/PenumbraKavu.java
@@ -3,7 +3,7 @@ package mage.cards.p;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -23,7 +23,7 @@ public final class PenumbraKavu extends CardImpl {
 
         this.power = new MageInt(3);
         this.toughness = new MageInt(3);
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new PenumbraKavuToken(), 1), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new PenumbraKavuToken(), 1), false));
     }
 
     public PenumbraKavu(final PenumbraKavu card) {
diff --git a/Mage.Sets/src/mage/cards/p/PenumbraSpider.java b/Mage.Sets/src/mage/cards/p/PenumbraSpider.java
index 9761b8f2b4..34d1aa47ad 100644
--- a/Mage.Sets/src/mage/cards/p/PenumbraSpider.java
+++ b/Mage.Sets/src/mage/cards/p/PenumbraSpider.java
@@ -3,7 +3,7 @@ package mage.cards.p;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.abilities.keyword.ReachAbility;
 import mage.cards.CardImpl;
@@ -28,7 +28,7 @@ public final class PenumbraSpider extends CardImpl {
         // Reach
         this.addAbility(ReachAbility.getInstance());
         // When Penumbra Spider dies, create a 2/4 black Spider creature token with reach.
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new PenumbraSpiderToken()), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new PenumbraSpiderToken()), false));
     }
 
     public PenumbraSpider(final PenumbraSpider card) {
diff --git a/Mage.Sets/src/mage/cards/p/PenumbraWurm.java b/Mage.Sets/src/mage/cards/p/PenumbraWurm.java
index ca9654f73d..ac7b100b62 100644
--- a/Mage.Sets/src/mage/cards/p/PenumbraWurm.java
+++ b/Mage.Sets/src/mage/cards/p/PenumbraWurm.java
@@ -3,7 +3,7 @@ package mage.cards.p;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.abilities.keyword.TrampleAbility;
 import mage.cards.CardImpl;
@@ -29,7 +29,7 @@ public final class PenumbraWurm extends CardImpl {
         this.addAbility(TrampleAbility.getInstance());
 
         // When Penumbra Wurm dies, create a 6/6 black Wurm creature token with trample.
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new PenumbraWurmToken(), 1), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new PenumbraWurmToken(), 1), false));
     }
 
     public PenumbraWurm(final PenumbraWurm card) {
diff --git a/Mage.Sets/src/mage/cards/p/PerilousMyr.java b/Mage.Sets/src/mage/cards/p/PerilousMyr.java
index 1ccbe47fad..1843606d1f 100644
--- a/Mage.Sets/src/mage/cards/p/PerilousMyr.java
+++ b/Mage.Sets/src/mage/cards/p/PerilousMyr.java
@@ -4,7 +4,7 @@ package mage.cards.p;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DamageTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -25,7 +25,7 @@ public final class PerilousMyr extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Perilous Myr dies, it deals 2 damage to any target.
-        Ability ability = new DiesTriggeredAbility(new DamageTargetEffect(2, "it"), false);
+        Ability ability = new DiesSourceTriggeredAbility(new DamageTargetEffect(2, "it"), false);
         ability.addTarget(new TargetAnyTarget());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/p/PersonalIncarnation.java b/Mage.Sets/src/mage/cards/p/PersonalIncarnation.java
index d0c82220e6..c39a4cc0fc 100644
--- a/Mage.Sets/src/mage/cards/p/PersonalIncarnation.java
+++ b/Mage.Sets/src/mage/cards/p/PersonalIncarnation.java
@@ -4,7 +4,7 @@ package mage.cards.p;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.mana.GenericManaCost;
 import mage.abilities.effects.OneShotEffect;
@@ -41,7 +41,7 @@ public final class PersonalIncarnation extends CardImpl {
         ability.setMayActivate(TargetController.OWNER);
         this.addAbility(ability);
         // When Personal Incarnation dies, its owner loses half their life, rounded up.
-        this.addAbility(new DiesTriggeredAbility(new PersonalIncarnationLoseHalfLifeEffect()));
+        this.addAbility(new DiesSourceTriggeredAbility(new PersonalIncarnationLoseHalfLifeEffect()));
     }
 
     public PersonalIncarnation(final PersonalIncarnation card) {
diff --git a/Mage.Sets/src/mage/cards/p/Phytotitan.java b/Mage.Sets/src/mage/cards/p/Phytotitan.java
index 30ca720f79..e8c6591370 100644
--- a/Mage.Sets/src/mage/cards/p/Phytotitan.java
+++ b/Mage.Sets/src/mage/cards/p/Phytotitan.java
@@ -5,7 +5,7 @@ import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.DelayedTriggeredAbility;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.delayed.AtTheBeginOfYourNextUpkeepDelayedTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.OneShotEffect;
@@ -32,7 +32,7 @@ public final class Phytotitan extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Phytotitan dies, return it to the battlefield tapped under its owner's control at the beginning of their next upkeep.
-        this.addAbility(new DiesTriggeredAbility(new PhytotitanEffect()));
+        this.addAbility(new DiesSourceTriggeredAbility(new PhytotitanEffect()));
     }
 
     public Phytotitan(final Phytotitan card) {
diff --git a/Mage.Sets/src/mage/cards/p/PitchburnDevils.java b/Mage.Sets/src/mage/cards/p/PitchburnDevils.java
index d715c2eb37..3f18bbe211 100644
--- a/Mage.Sets/src/mage/cards/p/PitchburnDevils.java
+++ b/Mage.Sets/src/mage/cards/p/PitchburnDevils.java
@@ -3,7 +3,7 @@ package mage.cards.p;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DamageTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -25,7 +25,7 @@ public final class PitchburnDevils extends CardImpl {
         this.toughness = new MageInt(3);
 
         // When Pitchburn Devils dies, it deals 3 damage to any target.
-        DiesTriggeredAbility ability = new DiesTriggeredAbility(new DamageTargetEffect(3, "it"));
+        DiesSourceTriggeredAbility ability = new DiesSourceTriggeredAbility(new DamageTargetEffect(3, "it"));
         ability.addTarget(new TargetAnyTarget());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/p/PlagueDogs.java b/Mage.Sets/src/mage/cards/p/PlagueDogs.java
index 4982b29055..f12376dd91 100644
--- a/Mage.Sets/src/mage/cards/p/PlagueDogs.java
+++ b/Mage.Sets/src/mage/cards/p/PlagueDogs.java
@@ -4,7 +4,7 @@ package mage.cards.p;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.common.SacrificeSourceCost;
 import mage.abilities.costs.mana.ManaCostsImpl;
@@ -31,7 +31,7 @@ public final class PlagueDogs extends CardImpl {
         this.toughness = new MageInt(3);
 
         // When Plague Dogs dies, all creatures get -1/-1 until end of turn.
-        this.addAbility(new DiesTriggeredAbility(new BoostAllEffect(-1, -1, Duration.EndOfTurn), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new BoostAllEffect(-1, -1, Duration.EndOfTurn), false));
         // {2}, Sacrifice Plague Dogs: Draw a card.
         Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{2}"));
         ability.addCost(new SacrificeSourceCost());
diff --git a/Mage.Sets/src/mage/cards/p/PlagueSpitter.java b/Mage.Sets/src/mage/cards/p/PlagueSpitter.java
index e312b05ab3..c80ac79d91 100644
--- a/Mage.Sets/src/mage/cards/p/PlagueSpitter.java
+++ b/Mage.Sets/src/mage/cards/p/PlagueSpitter.java
@@ -4,7 +4,7 @@ package mage.cards.p;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DamageEverythingEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -28,7 +28,7 @@ public final class PlagueSpitter extends CardImpl {
         // At the beginning of your upkeep, Plague Spitter deals 1 damage to each creature and each player.
         this.addAbility(new BeginningOfUpkeepTriggeredAbility(new DamageEverythingEffect(1), TargetController.YOU, false));
         // When Plague Spitter dies, Plague Spitter deals 1 damage to each creature and each player.
-        this.addAbility(new DiesTriggeredAbility(new DamageEverythingEffect(1), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new DamageEverythingEffect(1), false));
     }
 
     public PlagueSpitter(final PlagueSpitter card) {
diff --git a/Mage.Sets/src/mage/cards/p/PollutedDead.java b/Mage.Sets/src/mage/cards/p/PollutedDead.java
index fc70b9d4e1..9bc48539cf 100644
--- a/Mage.Sets/src/mage/cards/p/PollutedDead.java
+++ b/Mage.Sets/src/mage/cards/p/PollutedDead.java
@@ -4,7 +4,7 @@ package mage.cards.p;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DestroyTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -26,7 +26,7 @@ public final class PollutedDead extends CardImpl {
         this.toughness = new MageInt(3);
 
         // When Polluted Dead dies, destroy target land.
-        Ability ability = new DiesTriggeredAbility(new DestroyTargetEffect());
+        Ability ability = new DiesSourceTriggeredAbility(new DestroyTargetEffect());
         Target target = new TargetLandPermanent();
         ability.addTarget(target);
         this.addAbility(ability);
diff --git a/Mage.Sets/src/mage/cards/p/PrimalDruid.java b/Mage.Sets/src/mage/cards/p/PrimalDruid.java
index 2a8de813ed..fcafcea74c 100644
--- a/Mage.Sets/src/mage/cards/p/PrimalDruid.java
+++ b/Mage.Sets/src/mage/cards/p/PrimalDruid.java
@@ -2,7 +2,7 @@
 package mage.cards.p;
 
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect;
 import mage.cards.CardImpl;
@@ -30,7 +30,7 @@ public final class PrimalDruid extends CardImpl {
         // When Primal Druid dies, you may search your library for a basic land card, put it onto the battlefield tapped, then shuffle your library.
         Effect effect = new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true);
         effect.setText("you may search your library for a basic land card, put it onto the battlefield tapped, then shuffle your library");
-        this.addAbility(new DiesTriggeredAbility(effect, true));
+        this.addAbility(new DiesSourceTriggeredAbility(effect, true));
 
     }
 
diff --git a/Mage.Sets/src/mage/cards/p/ProteanHulk.java b/Mage.Sets/src/mage/cards/p/ProteanHulk.java
index 50a43b2da6..30a2df4205 100644
--- a/Mage.Sets/src/mage/cards/p/ProteanHulk.java
+++ b/Mage.Sets/src/mage/cards/p/ProteanHulk.java
@@ -4,7 +4,7 @@ package mage.cards.p;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.cards.*;
 import mage.constants.CardType;
@@ -34,7 +34,7 @@ public final class ProteanHulk extends CardImpl {
         this.toughness = new MageInt(6);
 
         // When Protean Hulk dies, search your library for any number of creature cards with total converted mana cost 6 or less and put them onto the battlefield. Then shuffle your library.
-        this.addAbility(new DiesTriggeredAbility(new ProteanHulkEffect()));
+        this.addAbility(new DiesSourceTriggeredAbility(new ProteanHulkEffect()));
     }
 
     public ProteanHulk(final ProteanHulk card) {
diff --git a/Mage.Sets/src/mage/cards/p/PurpleCrystalCrab.java b/Mage.Sets/src/mage/cards/p/PurpleCrystalCrab.java
index 43dbbc267b..6a896a5d4c 100644
--- a/Mage.Sets/src/mage/cards/p/PurpleCrystalCrab.java
+++ b/Mage.Sets/src/mage/cards/p/PurpleCrystalCrab.java
@@ -2,7 +2,7 @@ package mage.cards.p;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.constants.SubType;
 import mage.cards.CardImpl;
@@ -23,7 +23,7 @@ public final class PurpleCrystalCrab extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Purple-Crystal Crab dies, draw card.
-        this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(1), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1), false));
     }
 
     public PurpleCrystalCrab(final PurpleCrystalCrab card) {
diff --git a/Mage.Sets/src/mage/cards/r/RebornHero.java b/Mage.Sets/src/mage/cards/r/RebornHero.java
index eafb801ffa..c0f824c082 100644
--- a/Mage.Sets/src/mage/cards/r/RebornHero.java
+++ b/Mage.Sets/src/mage/cards/r/RebornHero.java
@@ -4,7 +4,7 @@ package mage.cards.r;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.condition.common.CardsInControllerGraveCondition;
 import mage.abilities.costs.mana.ManaCostsImpl;
@@ -41,7 +41,7 @@ public final class RebornHero extends CardImpl {
         Ability ability = new SimpleStaticAbility(
                 Zone.BATTLEFIELD,
                 new ConditionalContinuousEffect(
-                        new GainAbilitySourceEffect(new DiesTriggeredAbility(new DoIfCostPaid(
+                        new GainAbilitySourceEffect(new DiesSourceTriggeredAbility(new DoIfCostPaid(
                                 new ReturnSourceFromGraveyardToBattlefieldEffect(), new ManaCostsImpl("{W}{W}")
                         ))),
                         new CardsInControllerGraveCondition(7),
diff --git a/Mage.Sets/src/mage/cards/r/ReefWorm.java b/Mage.Sets/src/mage/cards/r/ReefWorm.java
index 07707cac53..b54fed6a46 100644
--- a/Mage.Sets/src/mage/cards/r/ReefWorm.java
+++ b/Mage.Sets/src/mage/cards/r/ReefWorm.java
@@ -3,7 +3,7 @@ package mage.cards.r;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -25,7 +25,7 @@ public final class ReefWorm extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Reef Worm dies, create a 3/3 blue Fish creature token with "When this creature dies, create a 6/6 blue Whale creature token with "When this creature dies, create a 9/9 blue Kraken creature token.""
-        addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new ReefWormFishToken())));
+        addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new ReefWormFishToken())));
     }
 
     public ReefWorm(final ReefWorm card) {
diff --git a/Mage.Sets/src/mage/cards/r/RekindlingPhoenix.java b/Mage.Sets/src/mage/cards/r/RekindlingPhoenix.java
index 3eac6457e9..6528f7ae01 100644
--- a/Mage.Sets/src/mage/cards/r/RekindlingPhoenix.java
+++ b/Mage.Sets/src/mage/cards/r/RekindlingPhoenix.java
@@ -3,7 +3,7 @@ package mage.cards.r;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
@@ -29,7 +29,7 @@ public final class RekindlingPhoenix extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
 
         // When Rekindling Phoenix dies, create a 0/1 red Elemental creature token with "At the beginning of your upkeep, sacrifice this creature and return target card named Rekindling Phoenix from your graveyard to the battlefield. It gains haste until end of turn."
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new RekindlingPhoenixToken()), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new RekindlingPhoenixToken()), false));
     }
 
     public RekindlingPhoenix(final RekindlingPhoenix card) {
diff --git a/Mage.Sets/src/mage/cards/r/RelentlessDead.java b/Mage.Sets/src/mage/cards/r/RelentlessDead.java
index 063fd464b5..d9567943ba 100644
--- a/Mage.Sets/src/mage/cards/r/RelentlessDead.java
+++ b/Mage.Sets/src/mage/cards/r/RelentlessDead.java
@@ -2,7 +2,7 @@ package mage.cards.r;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.DoIfCostPaid;
@@ -37,10 +37,10 @@ public final class RelentlessDead extends CardImpl {
         this.addAbility(new MenaceAbility());
 
         // When Relentless Dead dies, you may pay {B}. If you do, return it to its owner's hand.
-        this.addAbility(new DiesTriggeredAbility(new DoIfCostPaid(new ReturnToHandSourceEffect(), new ManaCostsImpl("{B}"))));
+        this.addAbility(new DiesSourceTriggeredAbility(new DoIfCostPaid(new ReturnToHandSourceEffect(), new ManaCostsImpl("{B}"))));
 
         // When Relentless Dead dies, you may pay {X}. If you do, return another target Zombie creature card with converted mana cost X from your graveyard to the battlefield.
-        this.addAbility(new DiesTriggeredAbility(new RelentlessDeadEffect()));
+        this.addAbility(new DiesSourceTriggeredAbility(new RelentlessDeadEffect()));
     }
 
     public RelentlessDead(final RelentlessDead card) {
diff --git a/Mage.Sets/src/mage/cards/r/ReliquaryMonk.java b/Mage.Sets/src/mage/cards/r/ReliquaryMonk.java
index 2e3e3ff044..5b254a277b 100644
--- a/Mage.Sets/src/mage/cards/r/ReliquaryMonk.java
+++ b/Mage.Sets/src/mage/cards/r/ReliquaryMonk.java
@@ -3,7 +3,7 @@ package mage.cards.r;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DestroyTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -29,7 +29,7 @@ public final class ReliquaryMonk extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Reliquary Monk dies, destroy target artifact or enchantment.
-        Ability ability = new DiesTriggeredAbility(new DestroyTargetEffect(), false);
+        Ability ability = new DiesSourceTriggeredAbility(new DestroyTargetEffect(), false);
         ability.addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT));
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/r/ReturnedReveler.java b/Mage.Sets/src/mage/cards/r/ReturnedReveler.java
index e0a10efc98..34a34083ae 100644
--- a/Mage.Sets/src/mage/cards/r/ReturnedReveler.java
+++ b/Mage.Sets/src/mage/cards/r/ReturnedReveler.java
@@ -3,7 +3,7 @@ package mage.cards.r;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.PutTopCardOfLibraryIntoGraveEachPlayerEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -26,7 +26,7 @@ public final class ReturnedReveler extends CardImpl {
         this.toughness = new MageInt(3);
 
         // When Returned Reveler dies, each player puts the top three cards of their library into their graveyard.
-        this.addAbility(new DiesTriggeredAbility(new PutTopCardOfLibraryIntoGraveEachPlayerEffect(3, TargetController.ANY)));
+        this.addAbility(new DiesSourceTriggeredAbility(new PutTopCardOfLibraryIntoGraveEachPlayerEffect(3, TargetController.ANY)));
     }
 
     public ReturnedReveler(final ReturnedReveler card) {
diff --git a/Mage.Sets/src/mage/cards/r/RiptideCrab.java b/Mage.Sets/src/mage/cards/r/RiptideCrab.java
index c0ce13fec5..2bfcc369a5 100644
--- a/Mage.Sets/src/mage/cards/r/RiptideCrab.java
+++ b/Mage.Sets/src/mage/cards/r/RiptideCrab.java
@@ -3,7 +3,7 @@ package mage.cards.r;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.abilities.keyword.VigilanceAbility;
 import mage.cards.CardImpl;
@@ -27,7 +27,7 @@ public final class RiptideCrab extends CardImpl {
         // Vigilance
         this.addAbility(VigilanceAbility.getInstance());
         // When Riptide Crab dies, draw a card.
-        this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(1), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1), false));
     }
 
     public RiptideCrab(final RiptideCrab card) {
diff --git a/Mage.Sets/src/mage/cards/r/RoaleskApexHybrid.java b/Mage.Sets/src/mage/cards/r/RoaleskApexHybrid.java
index 6c77689b4a..29cd3f704a 100644
--- a/Mage.Sets/src/mage/cards/r/RoaleskApexHybrid.java
+++ b/Mage.Sets/src/mage/cards/r/RoaleskApexHybrid.java
@@ -2,7 +2,7 @@ package mage.cards.r;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.effects.common.counter.AddCountersTargetEffect;
 import mage.abilities.effects.common.counter.ProliferateEffect;
@@ -56,7 +56,7 @@ public final class RoaleskApexHybrid extends CardImpl {
         this.addAbility(ability);
 
         // When Roalsk dies, proliferate, then proliferate again. (Choose any number of permanents and/or players, then give each another counter of each kind already there. Then do it again.)
-        ability = new DiesTriggeredAbility(new ProliferateEffect(false));
+        ability = new DiesSourceTriggeredAbility(new ProliferateEffect(false));
         ability.addEffect(new ProliferateEffect(" again", true).concatBy(", then"));
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/r/RocEgg.java b/Mage.Sets/src/mage/cards/r/RocEgg.java
index 126df90a82..e5e5797ee9 100644
--- a/Mage.Sets/src/mage/cards/r/RocEgg.java
+++ b/Mage.Sets/src/mage/cards/r/RocEgg.java
@@ -3,7 +3,7 @@ package mage.cards.r;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.abilities.keyword.DefenderAbility;
 import mage.cards.CardImpl;
@@ -28,7 +28,7 @@ public final class RocEgg extends CardImpl {
         this.toughness = new MageInt(3);
 
         this.addAbility(DefenderAbility.getInstance());
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(rocEggToken, 1), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(rocEggToken, 1), false));
     }
 
     public RocEgg(final RocEgg card) {
diff --git a/Mage.Sets/src/mage/cards/r/RootingKavu.java b/Mage.Sets/src/mage/cards/r/RootingKavu.java
index 7e0fc263db..8e34a33494 100644
--- a/Mage.Sets/src/mage/cards/r/RootingKavu.java
+++ b/Mage.Sets/src/mage/cards/r/RootingKavu.java
@@ -3,7 +3,7 @@ package mage.cards.r;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.costs.common.ExileSourceFromGraveCost;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.DoIfCostPaid;
@@ -31,7 +31,7 @@ public final class RootingKavu extends CardImpl {
         this.toughness = new MageInt(3);
 
         // When Rooting Kavu dies, you may exile it. If you do, shuffle all creature cards from your graveyard into your library.
-        this.addAbility(new DiesTriggeredAbility(new DoIfCostPaid(new RootingKavuEffect(), new ExileSourceFromGraveCost())));
+        this.addAbility(new DiesSourceTriggeredAbility(new DoIfCostPaid(new RootingKavuEffect(), new ExileSourceFromGraveCost())));
     }
 
     public RootingKavu(final RootingKavu card) {
diff --git a/Mage.Sets/src/mage/cards/r/RotcrownGhoul.java b/Mage.Sets/src/mage/cards/r/RotcrownGhoul.java
index bb979190f9..5591fd73a1 100644
--- a/Mage.Sets/src/mage/cards/r/RotcrownGhoul.java
+++ b/Mage.Sets/src/mage/cards/r/RotcrownGhoul.java
@@ -4,7 +4,7 @@ package mage.cards.r;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.PutLibraryIntoGraveTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -25,7 +25,7 @@ public final class RotcrownGhoul extends CardImpl {
         this.toughness = new MageInt(3);
 
         // When Rotcrown Ghoul dies, target player puts the top five cards of their library into their graveyard.
-        Ability ability = new DiesTriggeredAbility(new PutLibraryIntoGraveTargetEffect(5));
+        Ability ability = new DiesSourceTriggeredAbility(new PutLibraryIntoGraveTargetEffect(5));
         ability.addTarget(new TargetPlayer());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/r/RottenheartGhoul.java b/Mage.Sets/src/mage/cards/r/RottenheartGhoul.java
index a3ae6504fa..8a71bd7a30 100644
--- a/Mage.Sets/src/mage/cards/r/RottenheartGhoul.java
+++ b/Mage.Sets/src/mage/cards/r/RottenheartGhoul.java
@@ -4,7 +4,7 @@ package mage.cards.r;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.discard.DiscardTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -25,7 +25,7 @@ public final class RottenheartGhoul extends CardImpl {
         this.toughness = new MageInt(4);
 
         // When Rottenheart Ghoul dies, target player discards a card.
-        Ability ability = new DiesTriggeredAbility(new DiscardTargetEffect(1));
+        Ability ability = new DiesSourceTriggeredAbility(new DiscardTargetEffect(1));
         ability.addTarget(new TargetPlayer());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/r/RuinRat.java b/Mage.Sets/src/mage/cards/r/RuinRat.java
index 513b137219..86ce487b07 100644
--- a/Mage.Sets/src/mage/cards/r/RuinRat.java
+++ b/Mage.Sets/src/mage/cards/r/RuinRat.java
@@ -3,7 +3,7 @@ package mage.cards.r;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.ExileTargetEffect;
 import mage.abilities.keyword.DeathtouchAbility;
 import mage.cards.CardImpl;
@@ -30,7 +30,7 @@ public final class RuinRat extends CardImpl {
         this.addAbility(DeathtouchAbility.getInstance());
 
         // When Ruin Rat dies, exile target card from an opponent's graveyard.
-        DiesTriggeredAbility ability = new DiesTriggeredAbility(new ExileTargetEffect());
+        DiesSourceTriggeredAbility ability = new DiesSourceTriggeredAbility(new ExileTargetEffect());
         ability.addTarget(new TargetCardInOpponentsGraveyard(new FilterCard("card from an opponent's graveyard")));
         this.addAbility(ability);
 
diff --git a/Mage.Sets/src/mage/cards/r/RuinationRioter.java b/Mage.Sets/src/mage/cards/r/RuinationRioter.java
index dace98732c..00af5f37cf 100644
--- a/Mage.Sets/src/mage/cards/r/RuinationRioter.java
+++ b/Mage.Sets/src/mage/cards/r/RuinationRioter.java
@@ -2,7 +2,7 @@ package mage.cards.r;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.dynamicvalue.DynamicValue;
 import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount;
 import mage.abilities.effects.common.DamageTargetEffect;
@@ -31,7 +31,7 @@ public final class RuinationRioter extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Ruination Rioter dies, you may have it deal damage to any target equal to the number of land cards in your graveyard.
-        Ability ability = new DiesTriggeredAbility(
+        Ability ability = new DiesSourceTriggeredAbility(
                 new DamageTargetEffect(xValue).setText("you may have it deal damage to any target " +
                         "equal to the number of land cards in your graveyard."), true
         );
diff --git a/Mage.Sets/src/mage/cards/r/RukhEgg.java b/Mage.Sets/src/mage/cards/r/RukhEgg.java
index 6cb6036d6e..7889f9206f 100644
--- a/Mage.Sets/src/mage/cards/r/RukhEgg.java
+++ b/Mage.Sets/src/mage/cards/r/RukhEgg.java
@@ -4,7 +4,7 @@ package mage.cards.r;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
@@ -31,7 +31,7 @@ public final class RukhEgg extends CardImpl {
         // When Rukh Egg dies, create a 4/4 red Bird creature token with flying at the beginning of the next end step.
         Effect effect = new CreateDelayedTriggeredAbilityEffect(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new CreateTokenEffect(new RukhEggBirdToken())));
         effect.setText("create a 4/4 red Bird creature token with flying at the beginning of the next end step");
-        Ability ability = new DiesTriggeredAbility(effect);
+        Ability ability = new DiesSourceTriggeredAbility(effect);
         this.addAbility(ability);
     }
 
diff --git a/Mage.Sets/src/mage/cards/r/RunedServitor.java b/Mage.Sets/src/mage/cards/r/RunedServitor.java
index 80651aab25..0f8e979d4a 100644
--- a/Mage.Sets/src/mage/cards/r/RunedServitor.java
+++ b/Mage.Sets/src/mage/cards/r/RunedServitor.java
@@ -3,7 +3,7 @@ package mage.cards.r;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DrawCardAllEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -21,7 +21,7 @@ public final class RunedServitor extends CardImpl {
         this.subtype.add(SubType.CONSTRUCT);
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
-        this.addAbility(new DiesTriggeredAbility(new DrawCardAllEffect(1), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new DrawCardAllEffect(1), false));
     }
 
     public RunedServitor(final RunedServitor card) {
diff --git a/Mage.Sets/src/mage/cards/r/Runewing.java b/Mage.Sets/src/mage/cards/r/Runewing.java
index 4532e36b6d..f5a329d2bc 100644
--- a/Mage.Sets/src/mage/cards/r/Runewing.java
+++ b/Mage.Sets/src/mage/cards/r/Runewing.java
@@ -3,7 +3,7 @@ package mage.cards.r;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.abilities.keyword.DefenderAbility;
 import mage.abilities.keyword.FlyingAbility;
@@ -37,7 +37,7 @@ public final class Runewing extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
 
         // When Runewing dies, draw a card.
-        this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(1), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1), false));
     }
 
     public Runewing(final Runewing card) {
diff --git a/Mage.Sets/src/mage/cards/r/RushingTideZubera.java b/Mage.Sets/src/mage/cards/r/RushingTideZubera.java
index f80c8015a4..fb8920811b 100644
--- a/Mage.Sets/src/mage/cards/r/RushingTideZubera.java
+++ b/Mage.Sets/src/mage/cards/r/RushingTideZubera.java
@@ -4,7 +4,7 @@ package mage.cards.r;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.condition.Condition;
 import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
@@ -31,7 +31,7 @@ public final class RushingTideZubera extends CardImpl {
         this.toughness = new MageInt(3);
 
         // When Rushing-Tide Zubera dies, if 4 or more damage was dealt to it this turn, draw three cards.
-        Ability ability = new ConditionalInterveningIfTriggeredAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(3)), new RushingTideZuberaCondition(),
+        Ability ability = new ConditionalInterveningIfTriggeredAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(3)), new RushingTideZuberaCondition(),
                 "When {this} dies, if 4 or more damage was dealt to it this turn, draw three cards.");
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/r/RyuseiTheFallingStar.java b/Mage.Sets/src/mage/cards/r/RyuseiTheFallingStar.java
index 08a62c7fd6..58b9630ef6 100644
--- a/Mage.Sets/src/mage/cards/r/RyuseiTheFallingStar.java
+++ b/Mage.Sets/src/mage/cards/r/RyuseiTheFallingStar.java
@@ -4,7 +4,7 @@ package mage.cards.r;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DamageAllEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
@@ -36,7 +36,7 @@ public final class RyuseiTheFallingStar extends CardImpl {
         this.power = new MageInt(5);
         this.toughness = new MageInt(5);
         this.addAbility(FlyingAbility.getInstance());
-        this.addAbility(new DiesTriggeredAbility(new DamageAllEffect(5, filter)));
+        this.addAbility(new DiesSourceTriggeredAbility(new DamageAllEffect(5, filter)));
     }
 
     public RyuseiTheFallingStar(final RyuseiTheFallingStar card) {
diff --git a/Mage.Sets/src/mage/cards/s/SadisticAugermage.java b/Mage.Sets/src/mage/cards/s/SadisticAugermage.java
index f6b2d9d83f..dfbd3d36f5 100644
--- a/Mage.Sets/src/mage/cards/s/SadisticAugermage.java
+++ b/Mage.Sets/src/mage/cards/s/SadisticAugermage.java
@@ -5,7 +5,7 @@ import java.util.UUID;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.cards.Card;
 import mage.cards.CardImpl;
@@ -32,7 +32,7 @@ public final class SadisticAugermage extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Sadistic Augermage dies, each player puts a card from their hand on top of their library.
-        this.addAbility(new DiesTriggeredAbility(new WidespreadPanicEffect()));
+        this.addAbility(new DiesSourceTriggeredAbility(new WidespreadPanicEffect()));
     }
 
     public SadisticAugermage(final SadisticAugermage card) {
diff --git a/Mage.Sets/src/mage/cards/s/SalvageDrone.java b/Mage.Sets/src/mage/cards/s/SalvageDrone.java
index 66e0942638..e8944e482f 100644
--- a/Mage.Sets/src/mage/cards/s/SalvageDrone.java
+++ b/Mage.Sets/src/mage/cards/s/SalvageDrone.java
@@ -3,7 +3,7 @@ package mage.cards.s;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DrawDiscardControllerEffect;
 import mage.abilities.keyword.DevoidAbility;
 import mage.abilities.keyword.IngestAbility;
@@ -30,7 +30,7 @@ public final class SalvageDrone extends CardImpl {
         // Ingest (Whenever this creature deals combat damage to a player, that player exiles the top card of their library.)
         this.addAbility(new IngestAbility());
         // When Salvage Drone dies, you may draw a card. If you do, discard a card.
-        this.addAbility(new DiesTriggeredAbility(new DrawDiscardControllerEffect(1, 1, true), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new DrawDiscardControllerEffect(1, 1, true), false));
 
     }
 
diff --git a/Mage.Sets/src/mage/cards/s/ScreechingBuzzard.java b/Mage.Sets/src/mage/cards/s/ScreechingBuzzard.java
index 0c4e229616..a3424b3e16 100644
--- a/Mage.Sets/src/mage/cards/s/ScreechingBuzzard.java
+++ b/Mage.Sets/src/mage/cards/s/ScreechingBuzzard.java
@@ -3,7 +3,7 @@ package mage.cards.s;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.discard.DiscardEachPlayerEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
@@ -27,7 +27,7 @@ public final class ScreechingBuzzard extends CardImpl {
         // Flying
         this.addAbility(FlyingAbility.getInstance());
         // When Screeching Buzzard dies, each opponent discards a card.
-        this.addAbility(new DiesTriggeredAbility(new DiscardEachPlayerEffect(TargetController.OPPONENT), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new DiscardEachPlayerEffect(TargetController.OPPONENT), false));
     }
 
     public ScreechingBuzzard(final ScreechingBuzzard card) {
diff --git a/Mage.Sets/src/mage/cards/s/ScuttlingDoomEngine.java b/Mage.Sets/src/mage/cards/s/ScuttlingDoomEngine.java
index acb9f784d4..52469546ce 100644
--- a/Mage.Sets/src/mage/cards/s/ScuttlingDoomEngine.java
+++ b/Mage.Sets/src/mage/cards/s/ScuttlingDoomEngine.java
@@ -4,7 +4,7 @@ package mage.cards.s;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.SimpleEvasionAbility;
 import mage.abilities.effects.common.DamageTargetEffect;
 import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesSourceEffect;
@@ -41,7 +41,7 @@ public final class ScuttlingDoomEngine extends CardImpl {
         // Scuttling Doom Engine can't be blocked by creatures with power 2 or less.
         this.addAbility(new SimpleEvasionAbility(new CantBeBlockedByCreaturesSourceEffect(filter, Duration.WhileOnBattlefield)));
         // When Scuttling Doom Engine dies, it deals 6 damage to target opponnent
-        Ability ability = new DiesTriggeredAbility(new DamageTargetEffect(6, "it"), false);
+        Ability ability = new DiesSourceTriggeredAbility(new DamageTargetEffect(6, "it"), false);
         ability.addTarget(new TargetOpponentOrPlaneswalker());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/s/SeedGuardian.java b/Mage.Sets/src/mage/cards/s/SeedGuardian.java
index ade4624f24..c268d4e176 100644
--- a/Mage.Sets/src/mage/cards/s/SeedGuardian.java
+++ b/Mage.Sets/src/mage/cards/s/SeedGuardian.java
@@ -3,7 +3,7 @@ package mage.cards.s;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.abilities.keyword.ReachAbility;
@@ -32,7 +32,7 @@ public final class SeedGuardian extends CardImpl {
         // Reach
         this.addAbility(ReachAbility.getInstance());
         // When Seed Guardian dies, create an X/X green Elemental creature token, where X is the number of creature cards in your graveyard.
-        this.addAbility(new DiesTriggeredAbility(new SeedGuardianEffect(), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new SeedGuardianEffect(), false));
     }
 
     public SeedGuardian(final SeedGuardian card) {
diff --git a/Mage.Sets/src/mage/cards/s/SeedguideAsh.java b/Mage.Sets/src/mage/cards/s/SeedguideAsh.java
index 660b6695dc..cdb0582109 100644
--- a/Mage.Sets/src/mage/cards/s/SeedguideAsh.java
+++ b/Mage.Sets/src/mage/cards/s/SeedguideAsh.java
@@ -3,7 +3,7 @@ package mage.cards.s;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -33,7 +33,7 @@ public final class SeedguideAsh extends CardImpl {
         this.power = new MageInt(4);
         this.toughness = new MageInt(4);
         // When Seedguide Ash dies, you may search your library for up to three Forest cards and put them onto the battlefield tapped. If you do, shuffle your library.
-        this.addAbility(new DiesTriggeredAbility(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0, 3, filter), true, false, Outcome.PutLandInPlay), true));
+        this.addAbility(new DiesSourceTriggeredAbility(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0, 3, filter), true, false, Outcome.PutLandInPlay), true));
     }
 
     public SeedguideAsh(final SeedguideAsh card) {
diff --git a/Mage.Sets/src/mage/cards/s/SellSwordBrute.java b/Mage.Sets/src/mage/cards/s/SellSwordBrute.java
index 3048cc10ae..bdf0bae886 100644
--- a/Mage.Sets/src/mage/cards/s/SellSwordBrute.java
+++ b/Mage.Sets/src/mage/cards/s/SellSwordBrute.java
@@ -3,7 +3,7 @@ package mage.cards.s;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DamageControllerEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -25,7 +25,7 @@ public final class SellSwordBrute extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Sell-Sword Brute dies, it deals 2 damage to you.
-        this.addAbility(new DiesTriggeredAbility(new DamageControllerEffect(2, "it"), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new DamageControllerEffect(2, "it"), false));
     }
 
     public SellSwordBrute(final SellSwordBrute card) {
diff --git a/Mage.Sets/src/mage/cards/s/SerratedScorpion.java b/Mage.Sets/src/mage/cards/s/SerratedScorpion.java
index 425cc67965..f95e3077ef 100644
--- a/Mage.Sets/src/mage/cards/s/SerratedScorpion.java
+++ b/Mage.Sets/src/mage/cards/s/SerratedScorpion.java
@@ -2,7 +2,7 @@ package mage.cards.s;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DamagePlayersEffect;
 import mage.abilities.effects.common.GainLifeEffect;
 import mage.cards.CardImpl;
@@ -26,7 +26,7 @@ public final class SerratedScorpion extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Serrated Scorpion dies, it deals 2 damage to each opponent and you gain 2 life.
-        Ability ability = new DiesTriggeredAbility(new DamagePlayersEffect(
+        Ability ability = new DiesSourceTriggeredAbility(new DamagePlayersEffect(
                 2, TargetController.OPPONENT, "it"
         ));
         ability.addEffect(new GainLifeEffect(2).concatBy("and"));
diff --git a/Mage.Sets/src/mage/cards/s/SerumRaker.java b/Mage.Sets/src/mage/cards/s/SerumRaker.java
index 2ccbb51593..9055a92de9 100644
--- a/Mage.Sets/src/mage/cards/s/SerumRaker.java
+++ b/Mage.Sets/src/mage/cards/s/SerumRaker.java
@@ -4,7 +4,7 @@ package mage.cards.s;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.discard.DiscardEachPlayerEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
@@ -25,7 +25,7 @@ public final class SerumRaker extends CardImpl {
         this.power = new MageInt(3);
         this.toughness = new MageInt(2);
         this.addAbility(FlyingAbility.getInstance());
-        this.addAbility(new DiesTriggeredAbility(new DiscardEachPlayerEffect()));
+        this.addAbility(new DiesSourceTriggeredAbility(new DiscardEachPlayerEffect()));
     }
 
     public SerumRaker (final SerumRaker card) {
diff --git a/Mage.Sets/src/mage/cards/s/ServantOfTheScale.java b/Mage.Sets/src/mage/cards/s/ServantOfTheScale.java
index 32aaf50a85..f6454d2975 100644
--- a/Mage.Sets/src/mage/cards/s/ServantOfTheScale.java
+++ b/Mage.Sets/src/mage/cards/s/ServantOfTheScale.java
@@ -4,7 +4,7 @@ package mage.cards.s;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.OneShotEffect;
@@ -40,7 +40,7 @@ public final class ServantOfTheScale extends CardImpl {
                 "with a +1/+1 counter on it"));
 
         // When Servant of the Scale dies, put X +1/+1 counters on target creature you control, where X is the number of +1/+1 counter on Servant of the Scale.
-        Ability ability = new DiesTriggeredAbility(new ServantOfTheScaleEffect(), false);
+        Ability ability = new DiesSourceTriggeredAbility(new ServantOfTheScaleEffect(), false);
         ability.addTarget(new TargetControlledCreaturePermanent());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/s/ShaakHerd.java b/Mage.Sets/src/mage/cards/s/ShaakHerd.java
index 96f3933057..16852ebec0 100644
--- a/Mage.Sets/src/mage/cards/s/ShaakHerd.java
+++ b/Mage.Sets/src/mage/cards/s/ShaakHerd.java
@@ -4,7 +4,7 @@ package mage.cards.s;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -33,7 +33,7 @@ public final class ShaakHerd extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Shaak Herd dies, you may return another target creature card from your graveyard to your hand.
-        Ability ability = new DiesTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect(), true);
+        Ability ability = new DiesSourceTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect(), true);
         ability.addTarget(new TargetCardInYourGraveyard(filter));
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/s/ShamblingGoblin.java b/Mage.Sets/src/mage/cards/s/ShamblingGoblin.java
index ed512bf60f..83fdf692a4 100644
--- a/Mage.Sets/src/mage/cards/s/ShamblingGoblin.java
+++ b/Mage.Sets/src/mage/cards/s/ShamblingGoblin.java
@@ -4,7 +4,7 @@ package mage.cards.s;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.continuous.BoostTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -35,7 +35,7 @@ public final class ShamblingGoblin extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Shambling Goblin dies, target creature an opponent controls gets -1/-1 until end of turn.
-        Ability ability = new DiesTriggeredAbility(new BoostTargetEffect(-1,-1, Duration.EndOfTurn));
+        Ability ability = new DiesSourceTriggeredAbility(new BoostTargetEffect(-1,-1, Duration.EndOfTurn));
         ability.addTarget(new TargetCreaturePermanent(filterOpponentCreature));
         this.addAbility(ability);
 
diff --git a/Mage.Sets/src/mage/cards/s/ShamblingSwarm.java b/Mage.Sets/src/mage/cards/s/ShamblingSwarm.java
index 84d5681765..c6f445e9ec 100644
--- a/Mage.Sets/src/mage/cards/s/ShamblingSwarm.java
+++ b/Mage.Sets/src/mage/cards/s/ShamblingSwarm.java
@@ -4,7 +4,7 @@ package mage.cards.s;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.counter.DistributeCountersEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -26,7 +26,7 @@ public final class ShamblingSwarm extends CardImpl {
         this.toughness = new MageInt(3);
 
         // When Shambling Swarm dies, distribute three -1/-1 counters among one, two, or three target creatures. For each -1/-1 counter you put on a creature this way, remove a -1/-1 counter from that creature at the beginning of the next end step.
-        Ability ability = new DiesTriggeredAbility(new DistributeCountersEffect(CounterType.M1M1, 3, true, "one, two, or three target creatures"), false);
+        Ability ability = new DiesSourceTriggeredAbility(new DistributeCountersEffect(CounterType.M1M1, 3, true, "one, two, or three target creatures"), false);
         ability.addTarget(new TargetCreaturePermanentAmount(3));
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/s/ShivanPhoenix.java b/Mage.Sets/src/mage/cards/s/ShivanPhoenix.java
index 33d1dca830..80425ea158 100644
--- a/Mage.Sets/src/mage/cards/s/ShivanPhoenix.java
+++ b/Mage.Sets/src/mage/cards/s/ShivanPhoenix.java
@@ -3,7 +3,7 @@ package mage.cards.s;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.ReturnToHandSourceEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
@@ -27,7 +27,7 @@ public final class ShivanPhoenix extends CardImpl {
         // Flying
         this.addAbility(FlyingAbility.getInstance());
         // When Shivan Phoenix dies, return Shivan Phoenix to its owner's hand.
-        this.addAbility(new DiesTriggeredAbility(new ReturnToHandSourceEffect()));
+        this.addAbility(new DiesSourceTriggeredAbility(new ReturnToHandSourceEffect()));
     }
 
     public ShivanPhoenix(final ShivanPhoenix card) {
diff --git a/Mage.Sets/src/mage/cards/s/Showstopper.java b/Mage.Sets/src/mage/cards/s/Showstopper.java
index c8b70747a2..8fbc3a3f4b 100644
--- a/Mage.Sets/src/mage/cards/s/Showstopper.java
+++ b/Mage.Sets/src/mage/cards/s/Showstopper.java
@@ -2,7 +2,7 @@ package mage.cards.s;
 
 import java.util.UUID;
 import mage.abilities.TriggeredAbility;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.DamageTargetEffect;
 import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
@@ -34,7 +34,7 @@ public final class Showstopper extends CardImpl {
         super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{B}{R}");
 
         // Until end of turn, creatures you control gain "When this creature dies, it deals 2 damage to target creature an opponent controls."
-        TriggeredAbility ability = new DiesTriggeredAbility(new DamageTargetEffect(2, "it"), false);
+        TriggeredAbility ability = new DiesSourceTriggeredAbility(new DamageTargetEffect(2, "it"), false);
         Target target = new TargetCreaturePermanent(filter2);
         ability.addTarget(target);
         Effect effect = new GainAbilityControlledEffect(ability, Duration.EndOfTurn, filter);
diff --git a/Mage.Sets/src/mage/cards/s/SilentChantZubera.java b/Mage.Sets/src/mage/cards/s/SilentChantZubera.java
index b2d0ec1e0b..b3d36bc842 100644
--- a/Mage.Sets/src/mage/cards/s/SilentChantZubera.java
+++ b/Mage.Sets/src/mage/cards/s/SilentChantZubera.java
@@ -5,7 +5,7 @@ package mage.cards.s;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.dynamicvalue.DynamicValue;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.GainLifeEffect;
@@ -29,7 +29,7 @@ public final class SilentChantZubera extends CardImpl {
 
         this.power = new MageInt(1);
         this.toughness = new MageInt(2);
-        Ability ability = new DiesTriggeredAbility(new GainLifeEffect(new SilentChantZuberaDynamicValue()));
+        Ability ability = new DiesSourceTriggeredAbility(new GainLifeEffect(new SilentChantZuberaDynamicValue()));
         this.addAbility(ability, new ZuberasDiedWatcher());
     }
 
diff --git a/Mage.Sets/src/mage/cards/s/SilverbackShaman.java b/Mage.Sets/src/mage/cards/s/SilverbackShaman.java
index d33b3e2867..794d0a67e7 100644
--- a/Mage.Sets/src/mage/cards/s/SilverbackShaman.java
+++ b/Mage.Sets/src/mage/cards/s/SilverbackShaman.java
@@ -1,7 +1,7 @@
 package mage.cards.s;
 
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.abilities.keyword.TrampleAbility;
 import mage.cards.CardImpl;
@@ -28,7 +28,7 @@ public final class SilverbackShaman extends CardImpl {
         this.addAbility(TrampleAbility.getInstance());
 
         // When Silverback Shaman dies, draw a card.
-        this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(1)));
+        this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1)));
     }
 
     private SilverbackShaman(final SilverbackShaman card) {
diff --git a/Mage.Sets/src/mage/cards/s/SlaughterhouseBouncer.java b/Mage.Sets/src/mage/cards/s/SlaughterhouseBouncer.java
index b68d8d3a68..5a687fed34 100644
--- a/Mage.Sets/src/mage/cards/s/SlaughterhouseBouncer.java
+++ b/Mage.Sets/src/mage/cards/s/SlaughterhouseBouncer.java
@@ -4,7 +4,7 @@ package mage.cards.s;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.condition.common.HellbentCondition;
 import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
 import mage.abilities.effects.common.continuous.BoostTargetEffect;
@@ -32,7 +32,7 @@ public final class SlaughterhouseBouncer extends CardImpl {
 
         // Hellbent - When Slaughterhouse Bouncer dies, if you have no cards in hand, target creature gets -3/-3 until end of turn.
         Ability ability = new ConditionalInterveningIfTriggeredAbility(
-                new DiesTriggeredAbility(new BoostTargetEffect(-3, -3, Duration.EndOfTurn)),
+                new DiesSourceTriggeredAbility(new BoostTargetEffect(-3, -3, Duration.EndOfTurn)),
                 HellbentCondition.instance,
                 "When {this} dies, if you have no cards in hand, target creature gets -3/-3 until end of turn."
         );
diff --git a/Mage.Sets/src/mage/cards/s/SmolderingEfreet.java b/Mage.Sets/src/mage/cards/s/SmolderingEfreet.java
index 56fed80827..642e602031 100644
--- a/Mage.Sets/src/mage/cards/s/SmolderingEfreet.java
+++ b/Mage.Sets/src/mage/cards/s/SmolderingEfreet.java
@@ -3,7 +3,7 @@ package mage.cards.s;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DamageControllerEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -24,7 +24,7 @@ public final class SmolderingEfreet extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Smoldering Efreet dies, it deals 2 damage to you.
-        this.addAbility(new DiesTriggeredAbility(new DamageControllerEffect(2, "it"), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new DamageControllerEffect(2, "it"), false));
     }
 
     public SmolderingEfreet(final SmolderingEfreet card) {
diff --git a/Mage.Sets/src/mage/cards/s/SolemnSimulacrum.java b/Mage.Sets/src/mage/cards/s/SolemnSimulacrum.java
index e80c292346..2783e4d2e3 100644
--- a/Mage.Sets/src/mage/cards/s/SolemnSimulacrum.java
+++ b/Mage.Sets/src/mage/cards/s/SolemnSimulacrum.java
@@ -2,7 +2,7 @@
 package mage.cards.s;
 
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect;
@@ -26,7 +26,7 @@ public final class SolemnSimulacrum extends CardImpl {
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
         this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true), true));
-        this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(1), true));
+        this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1), true));
     }
 
     public SolemnSimulacrum(final SolemnSimulacrum card) {
diff --git a/Mage.Sets/src/mage/cards/s/SoulcageFiend.java b/Mage.Sets/src/mage/cards/s/SoulcageFiend.java
index 669c4cacf3..71c179f86d 100644
--- a/Mage.Sets/src/mage/cards/s/SoulcageFiend.java
+++ b/Mage.Sets/src/mage/cards/s/SoulcageFiend.java
@@ -3,7 +3,7 @@ package mage.cards.s;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.LoseLifeAllPlayersEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -23,7 +23,7 @@ public final class SoulcageFiend extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Soulcage Fiend dies, each player loses 3 life.
-        this.addAbility(new DiesTriggeredAbility(new LoseLifeAllPlayersEffect(3)));
+        this.addAbility(new DiesSourceTriggeredAbility(new LoseLifeAllPlayersEffect(3)));
     }
 
     public SoulcageFiend(final SoulcageFiend card) {
diff --git a/Mage.Sets/src/mage/cards/s/Soulstinger.java b/Mage.Sets/src/mage/cards/s/Soulstinger.java
index 955da0d7cd..56204981e3 100644
--- a/Mage.Sets/src/mage/cards/s/Soulstinger.java
+++ b/Mage.Sets/src/mage/cards/s/Soulstinger.java
@@ -4,7 +4,7 @@ package mage.cards.s;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.dynamicvalue.common.CountersSourceCount;
 import mage.abilities.effects.common.counter.AddCountersTargetEffect;
@@ -43,7 +43,7 @@ public final class Soulstinger extends CardImpl {
                         new CountersSourceCount(CounterType.M1M1),
                         Outcome.Detriment);
         effect.setText("you may put a -1/-1 counter on target creature for each -1/-1 counter on {this}");
-        ability = new DiesTriggeredAbility(effect, true);
+        ability = new DiesSourceTriggeredAbility(effect, true);
         ability.addTarget(new TargetCreaturePermanent());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/s/SparringConstruct.java b/Mage.Sets/src/mage/cards/s/SparringConstruct.java
index fd9626d107..18022974cd 100644
--- a/Mage.Sets/src/mage/cards/s/SparringConstruct.java
+++ b/Mage.Sets/src/mage/cards/s/SparringConstruct.java
@@ -4,7 +4,7 @@ package mage.cards.s;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.counter.AddCountersTargetEffect;
 import mage.constants.SubType;
 import mage.cards.CardImpl;
@@ -27,7 +27,7 @@ public final class SparringConstruct extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Sparring Construct dies, put a +1/+1 counter on target creature you control.
-        Ability ability = new DiesTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance()), false);
+        Ability ability = new DiesSourceTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance()), false);
         ability.addTarget(new TargetControlledCreaturePermanent());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/s/SpinalCentipede.java b/Mage.Sets/src/mage/cards/s/SpinalCentipede.java
index 9d5073b48b..7e8c8347a9 100644
--- a/Mage.Sets/src/mage/cards/s/SpinalCentipede.java
+++ b/Mage.Sets/src/mage/cards/s/SpinalCentipede.java
@@ -3,7 +3,7 @@ package mage.cards.s;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.counter.AddCountersTargetEffect;
 import mage.constants.SubType;
 import mage.cards.CardImpl;
@@ -26,7 +26,7 @@ public final class SpinalCentipede extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Spinal Centipede dies, put a +1/+1 counter on target creature you control.
-        Ability ability = new DiesTriggeredAbility(new AddCountersTargetEffect(
+        Ability ability = new DiesSourceTriggeredAbility(new AddCountersTargetEffect(
                 CounterType.P1P1.createInstance()
         ), false);
         ability.addTarget(new TargetControlledCreaturePermanent());
diff --git a/Mage.Sets/src/mage/cards/s/SpiritOfMalevolence.java b/Mage.Sets/src/mage/cards/s/SpiritOfMalevolence.java
index 946050840c..bd49a41871 100644
--- a/Mage.Sets/src/mage/cards/s/SpiritOfMalevolence.java
+++ b/Mage.Sets/src/mage/cards/s/SpiritOfMalevolence.java
@@ -2,7 +2,7 @@ package mage.cards.s;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.GainLifeEffect;
 import mage.abilities.effects.common.LoseLifeOpponentsEffect;
 import mage.cards.CardImpl;
@@ -25,7 +25,7 @@ public final class SpiritOfMalevolence extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Spirit of Malevolence dies, each opponent loses 1 life and you gain 1 life.
-        Ability ability = new DiesTriggeredAbility(new LoseLifeOpponentsEffect(1));
+        Ability ability = new DiesSourceTriggeredAbility(new LoseLifeOpponentsEffect(1));
         ability.addEffect(new GainLifeEffect(1).concatBy("and"));
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/s/SproutingThrinax.java b/Mage.Sets/src/mage/cards/s/SproutingThrinax.java
index fd5fe9af5d..a5b5f5d231 100644
--- a/Mage.Sets/src/mage/cards/s/SproutingThrinax.java
+++ b/Mage.Sets/src/mage/cards/s/SproutingThrinax.java
@@ -4,7 +4,7 @@ package mage.cards.s;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -28,7 +28,7 @@ public final class SproutingThrinax extends CardImpl {
         this.power = new MageInt(3);
         this.toughness = new MageInt(3);
 
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(saprolingToken, 3), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(saprolingToken, 3), false));
     }
 
     public SproutingThrinax(final SproutingThrinax card) {
diff --git a/Mage.Sets/src/mage/cards/s/SteadfastSentry.java b/Mage.Sets/src/mage/cards/s/SteadfastSentry.java
index e118f863b5..8d5ecc1093 100644
--- a/Mage.Sets/src/mage/cards/s/SteadfastSentry.java
+++ b/Mage.Sets/src/mage/cards/s/SteadfastSentry.java
@@ -2,7 +2,7 @@ package mage.cards.s;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.counter.AddCountersTargetEffect;
 import mage.abilities.keyword.VigilanceAbility;
 import mage.cards.CardImpl;
@@ -31,7 +31,7 @@ public final class SteadfastSentry extends CardImpl {
         this.addAbility(VigilanceAbility.getInstance());
 
         // When Steadfast Sentry dies, put a +1/+1 counter on target creature you control.
-        Ability ability = new DiesTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance()));
+        Ability ability = new DiesSourceTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance()));
         ability.addTarget(new TargetControlledCreaturePermanent());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/s/SuChi.java b/Mage.Sets/src/mage/cards/s/SuChi.java
index b8c58ef91c..d59bc7e049 100644
--- a/Mage.Sets/src/mage/cards/s/SuChi.java
+++ b/Mage.Sets/src/mage/cards/s/SuChi.java
@@ -4,7 +4,7 @@ package mage.cards.s;
 import java.util.UUID;
 import mage.MageInt;
 import mage.Mana;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.mana.BasicManaEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -25,7 +25,7 @@ public final class SuChi extends CardImpl {
         this.toughness = new MageInt(4);
 
         // When Su-Chi dies, add {C}{C}{C}{C}.
-        this.addAbility(new DiesTriggeredAbility(new BasicManaEffect(Mana.ColorlessMana(4)), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new BasicManaEffect(Mana.ColorlessMana(4)), false));
     }
 
     public SuChi(final SuChi card) {
diff --git a/Mage.Sets/src/mage/cards/s/SultaiEmissary.java b/Mage.Sets/src/mage/cards/s/SultaiEmissary.java
index cb615a363b..29c91953fd 100644
--- a/Mage.Sets/src/mage/cards/s/SultaiEmissary.java
+++ b/Mage.Sets/src/mage/cards/s/SultaiEmissary.java
@@ -3,7 +3,7 @@ package mage.cards.s;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.keyword.ManifestEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -24,7 +24,7 @@ public final class SultaiEmissary extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Sultai Emissary dies, manifest the top card of your library.
-        this.addAbility(new DiesTriggeredAbility(new ManifestEffect(1)));
+        this.addAbility(new DiesSourceTriggeredAbility(new ManifestEffect(1)));
     }
 
     public SultaiEmissary(final SultaiEmissary card) {
diff --git a/Mage.Sets/src/mage/cards/s/SummonersEgg.java b/Mage.Sets/src/mage/cards/s/SummonersEgg.java
index 055eb81237..d4d249ec2c 100644
--- a/Mage.Sets/src/mage/cards/s/SummonersEgg.java
+++ b/Mage.Sets/src/mage/cards/s/SummonersEgg.java
@@ -4,7 +4,7 @@ package mage.cards.s;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.cards.Card;
@@ -36,7 +36,7 @@ public final class SummonersEgg extends CardImpl {
         // Imprint - When Summoner's Egg enters the battlefield, you may exile a card from your hand face down.
         this.addAbility(new EntersBattlefieldTriggeredAbility(new SummonersEggImprintEffect(), true, "<i>Imprint</i> &mdash; "));
         // When Summoner's Egg dies, turn the exiled card face up. If it's a creature card, put it onto the battlefield under your control.
-        this.addAbility(new DiesTriggeredAbility(new SummonersEggPutOntoBattlefieldEffect()));
+        this.addAbility(new DiesSourceTriggeredAbility(new SummonersEggPutOntoBattlefieldEffect()));
     }
 
     public SummonersEgg(final SummonersEgg card) {
diff --git a/Mage.Sets/src/mage/cards/s/SupernaturalStamina.java b/Mage.Sets/src/mage/cards/s/SupernaturalStamina.java
index 3f67103eff..b4667f2ab7 100644
--- a/Mage.Sets/src/mage/cards/s/SupernaturalStamina.java
+++ b/Mage.Sets/src/mage/cards/s/SupernaturalStamina.java
@@ -1,7 +1,7 @@
 package mage.cards.s;
 
 import java.util.UUID;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect;
 import mage.abilities.effects.common.continuous.BoostTargetEffect;
 import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
@@ -26,7 +26,7 @@ public final class SupernaturalStamina extends CardImpl {
                 .setText("Until end of turn, target creature gets +2/+0")
         );
         getSpellAbility().addEffect(new GainAbilityTargetEffect(
-                new DiesTriggeredAbility(
+                new DiesSourceTriggeredAbility(
                         new ReturnSourceFromGraveyardToBattlefieldEffect(true, true),
                         false),
                 Duration.EndOfTurn,
diff --git a/Mage.Sets/src/mage/cards/s/SurveillingSprite.java b/Mage.Sets/src/mage/cards/s/SurveillingSprite.java
index 988c83be6f..b27d88f50e 100644
--- a/Mage.Sets/src/mage/cards/s/SurveillingSprite.java
+++ b/Mage.Sets/src/mage/cards/s/SurveillingSprite.java
@@ -3,7 +3,7 @@ package mage.cards.s;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
@@ -27,7 +27,7 @@ public final class SurveillingSprite extends CardImpl {
 
         this.addAbility(FlyingAbility.getInstance());
         // When Surveilling Sprite dies, you may draw a card.
-        this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(1), true));
+        this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1), true));
     }
 
     public SurveillingSprite(final SurveillingSprite card) {
diff --git a/Mage.Sets/src/mage/cards/s/SylvanHierophant.java b/Mage.Sets/src/mage/cards/s/SylvanHierophant.java
index 80555a1adc..ea72e04e61 100644
--- a/Mage.Sets/src/mage/cards/s/SylvanHierophant.java
+++ b/Mage.Sets/src/mage/cards/s/SylvanHierophant.java
@@ -2,7 +2,7 @@ package mage.cards.s;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.ExileSourceEffect;
 import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect;
@@ -40,7 +40,7 @@ public final class SylvanHierophant extends CardImpl {
 
         // When Sylvan Hierophant dies, exile Sylvan Hierophant, then return another target creature card from your graveyard to your hand.
         Effect effect = new ReturnFromGraveyardToHandTargetEffect();
-        Ability ability = new DiesTriggeredAbility(new ExileSourceEffect(), false);
+        Ability ability = new DiesSourceTriggeredAbility(new ExileSourceEffect(), false);
         ability.addEffect(effect);
         ability.addTarget(new TargetCardInYourGraveyard(filter));
         this.addAbility(ability);
diff --git a/Mage.Sets/src/mage/cards/s/SymbioticBeast.java b/Mage.Sets/src/mage/cards/s/SymbioticBeast.java
index df9de72dce..4f2d1b0cc0 100644
--- a/Mage.Sets/src/mage/cards/s/SymbioticBeast.java
+++ b/Mage.Sets/src/mage/cards/s/SymbioticBeast.java
@@ -3,7 +3,7 @@ package mage.cards.s;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -25,7 +25,7 @@ public final class SymbioticBeast extends CardImpl {
         this.toughness = new MageInt(4);
       
         // When Symbiotic Beast dies, create four 1/1 green Insect creature tokens.
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new InsectToken(), 4)));
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new InsectToken(), 4)));
     }
 
     public SymbioticBeast(final SymbioticBeast card) {
diff --git a/Mage.Sets/src/mage/cards/s/SymbioticElf.java b/Mage.Sets/src/mage/cards/s/SymbioticElf.java
index b2f3b8d408..a40b193767 100644
--- a/Mage.Sets/src/mage/cards/s/SymbioticElf.java
+++ b/Mage.Sets/src/mage/cards/s/SymbioticElf.java
@@ -3,7 +3,7 @@ package mage.cards.s;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -24,7 +24,7 @@ public final class SymbioticElf extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Symbiotic Elf dies, create two 1/1 green Insect creature tokens.
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new InsectToken(), 2)));
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new InsectToken(), 2)));
     }
 
     public SymbioticElf(final SymbioticElf card) {
diff --git a/Mage.Sets/src/mage/cards/s/SymbioticWurm.java b/Mage.Sets/src/mage/cards/s/SymbioticWurm.java
index 0cca8ae6d8..45df2721da 100644
--- a/Mage.Sets/src/mage/cards/s/SymbioticWurm.java
+++ b/Mage.Sets/src/mage/cards/s/SymbioticWurm.java
@@ -3,7 +3,7 @@ package mage.cards.s;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -25,7 +25,7 @@ public final class SymbioticWurm extends CardImpl {
         this.toughness = new MageInt(7);
 
         // When Symbiotic Wurm dies, create seven 1/1 green Insect creature tokens.
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new InsectToken(), 7)));
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new InsectToken(), 7)));
 
     }
 
diff --git a/Mage.Sets/src/mage/cards/t/Tarpan.java b/Mage.Sets/src/mage/cards/t/Tarpan.java
index 5529e42b62..c3a83c1d78 100644
--- a/Mage.Sets/src/mage/cards/t/Tarpan.java
+++ b/Mage.Sets/src/mage/cards/t/Tarpan.java
@@ -3,7 +3,7 @@ package mage.cards.t;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.GainLifeEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -24,7 +24,7 @@ public final class Tarpan extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Tarpan dies, you gain 1 life.
-        this.addAbility(new DiesTriggeredAbility(new GainLifeEffect(1), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new GainLifeEffect(1), false));
     }
 
     public Tarpan(final Tarpan card) {
diff --git a/Mage.Sets/src/mage/cards/t/TatteredMummy.java b/Mage.Sets/src/mage/cards/t/TatteredMummy.java
index 92d4daecc1..7dd0951593 100644
--- a/Mage.Sets/src/mage/cards/t/TatteredMummy.java
+++ b/Mage.Sets/src/mage/cards/t/TatteredMummy.java
@@ -3,7 +3,7 @@ package mage.cards.t;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.LoseLifeOpponentsEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -25,7 +25,7 @@ public final class TatteredMummy extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Tattered Mummy dies, each opponent loses 2 life.
-        this.addAbility(new DiesTriggeredAbility(new LoseLifeOpponentsEffect(2)));
+        this.addAbility(new DiesSourceTriggeredAbility(new LoseLifeOpponentsEffect(2)));
     }
 
     public TatteredMummy(final TatteredMummy card) {
diff --git a/Mage.Sets/src/mage/cards/t/TenaciousDead.java b/Mage.Sets/src/mage/cards/t/TenaciousDead.java
index a9f9a1e0e2..54c980379d 100644
--- a/Mage.Sets/src/mage/cards/t/TenaciousDead.java
+++ b/Mage.Sets/src/mage/cards/t/TenaciousDead.java
@@ -3,7 +3,7 @@ package mage.cards.t;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.DoIfCostPaid;
@@ -29,7 +29,7 @@ public final class TenaciousDead extends CardImpl {
 
         // When Tenacious Dead dies, you may pay {1}{B}. If you do, return it to the battlefield tapped under its owner's control.
         Effect effect = new DoIfCostPaid(new ReturnToBattlefieldUnderOwnerControlSourceEffect(true), new ManaCostsImpl("{1}{B}"));
-        this.addAbility(new DiesTriggeredAbility(effect, false));
+        this.addAbility(new DiesSourceTriggeredAbility(effect, false));
 
     }
 
diff --git a/Mage.Sets/src/mage/cards/t/TheLocustGod.java b/Mage.Sets/src/mage/cards/t/TheLocustGod.java
index 752b438e49..0e3c4852e2 100644
--- a/Mage.Sets/src/mage/cards/t/TheLocustGod.java
+++ b/Mage.Sets/src/mage/cards/t/TheLocustGod.java
@@ -5,7 +5,7 @@ import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.DelayedTriggeredAbility;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.DrawCardControllerTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility;
@@ -52,7 +52,7 @@ public final class TheLocustGod extends CardImpl {
         this.addAbility(ability);
 
         // When The Locust God dies, return it to its owner's hand at the beginning of the next end step.
-        this.addAbility(new DiesTriggeredAbility(new TheLocustGodEffect()));
+        this.addAbility(new DiesSourceTriggeredAbility(new TheLocustGodEffect()));
     }
 
     public TheLocustGod(final TheLocustGod card) {
diff --git a/Mage.Sets/src/mage/cards/t/TheScarabGod.java b/Mage.Sets/src/mage/cards/t/TheScarabGod.java
index 7a7f4fbf4c..e8d7e44ae0 100644
--- a/Mage.Sets/src/mage/cards/t/TheScarabGod.java
+++ b/Mage.Sets/src/mage/cards/t/TheScarabGod.java
@@ -7,7 +7,7 @@ import mage.ObjectColor;
 import mage.abilities.Ability;
 import mage.abilities.DelayedTriggeredAbility;
 import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
@@ -54,7 +54,7 @@ public final class TheScarabGod extends CardImpl {
         this.addAbility(ability);
 
         // When The Scarab God dies, return it to its owner's hand at the beginning of the next end step.
-        this.addAbility(new DiesTriggeredAbility(new TheScarabGodEffect3()));
+        this.addAbility(new DiesSourceTriggeredAbility(new TheScarabGodEffect3()));
     }
 
     public TheScarabGod(final TheScarabGod card) {
diff --git a/Mage.Sets/src/mage/cards/t/TheScorpionGod.java b/Mage.Sets/src/mage/cards/t/TheScorpionGod.java
index b973513f0e..b912a6d28e 100644
--- a/Mage.Sets/src/mage/cards/t/TheScorpionGod.java
+++ b/Mage.Sets/src/mage/cards/t/TheScorpionGod.java
@@ -6,7 +6,7 @@ import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.DelayedTriggeredAbility;
 import mage.abilities.TriggeredAbilityImpl;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
@@ -63,7 +63,7 @@ public final class TheScorpionGod extends CardImpl {
         this.addAbility(ability);
 
         // When The Scorpion God dies, return it to its owner's hand at the beginning of the next end step.
-        this.addAbility(new DiesTriggeredAbility(new TheScorpionGodEffect()));
+        this.addAbility(new DiesSourceTriggeredAbility(new TheScorpionGodEffect()));
     }
 
     public TheScorpionGod(final TheScorpionGod card) {
diff --git a/Mage.Sets/src/mage/cards/t/ThoughtSponge.java b/Mage.Sets/src/mage/cards/t/ThoughtSponge.java
index 6721f81d76..da1d3a75f0 100644
--- a/Mage.Sets/src/mage/cards/t/ThoughtSponge.java
+++ b/Mage.Sets/src/mage/cards/t/ThoughtSponge.java
@@ -2,7 +2,7 @@ package mage.cards.t;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldAbility;
 import mage.abilities.dynamicvalue.DynamicValue;
 import mage.abilities.dynamicvalue.common.SourcePermanentPowerCount;
@@ -45,7 +45,7 @@ public final class ThoughtSponge extends CardImpl {
         ), new CardsDrawnThisTurnWatcher());
 
         // When Thought Sponge dies, draw cards equal to its power.
-        this.addAbility(new DiesTriggeredAbility(
+        this.addAbility(new DiesSourceTriggeredAbility(
                 new DrawCardSourceControllerEffect(xValue).setText("draw cards equal to its power")
         ));
     }
diff --git a/Mage.Sets/src/mage/cards/t/TibaltsRager.java b/Mage.Sets/src/mage/cards/t/TibaltsRager.java
index 26d3282571..8599f5a070 100644
--- a/Mage.Sets/src/mage/cards/t/TibaltsRager.java
+++ b/Mage.Sets/src/mage/cards/t/TibaltsRager.java
@@ -2,7 +2,7 @@ package mage.cards.t;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.common.DamageTargetEffect;
@@ -29,7 +29,7 @@ public final class TibaltsRager extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Tibalt's Rager dies, it deals 1 damage to any target.
-        Ability ability = new DiesTriggeredAbility(new DamageTargetEffect(1, "it"));
+        Ability ability = new DiesSourceTriggeredAbility(new DamageTargetEffect(1, "it"));
         ability.addTarget(new TargetAnyTarget());
         this.addAbility(ability);
 
diff --git a/Mage.Sets/src/mage/cards/t/TreacherousVampire.java b/Mage.Sets/src/mage/cards/t/TreacherousVampire.java
index b95c66a613..59d0625cc4 100644
--- a/Mage.Sets/src/mage/cards/t/TreacherousVampire.java
+++ b/Mage.Sets/src/mage/cards/t/TreacherousVampire.java
@@ -5,7 +5,7 @@ import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.AttacksOrBlocksTriggeredAbility;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.condition.common.CardsInControllerGraveCondition;
 import mage.abilities.costs.common.ExileFromGraveCost;
@@ -55,7 +55,7 @@ public final class TreacherousVampire extends CardImpl {
                 new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield), new CardsInControllerGraveCondition(7),
                 "As long as seven or more cards are in your graveyard, {this} gets +2/+2"));
         Effect effect = new ConditionalContinuousEffect(
-                new GainAbilitySourceEffect(new DiesTriggeredAbility(new LoseLifeSourceControllerEffect(6))),
+                new GainAbilitySourceEffect(new DiesSourceTriggeredAbility(new LoseLifeSourceControllerEffect(6))),
                 new CardsInControllerGraveCondition(7), "and has \"When {this} dies, you lose 6 life.\""
         );
         ability.addEffect(effect);
diff --git a/Mage.Sets/src/mage/cards/t/TreacherousWerewolf.java b/Mage.Sets/src/mage/cards/t/TreacherousWerewolf.java
index 1f565dea59..4d980d39d1 100644
--- a/Mage.Sets/src/mage/cards/t/TreacherousWerewolf.java
+++ b/Mage.Sets/src/mage/cards/t/TreacherousWerewolf.java
@@ -4,7 +4,7 @@ package mage.cards.t;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.condition.common.CardsInControllerGraveCondition;
 import mage.abilities.decorator.ConditionalContinuousEffect;
@@ -39,7 +39,7 @@ public final class TreacherousWerewolf extends CardImpl {
                 new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield), new CardsInControllerGraveCondition(7),
                 "As long as seven or more cards are in your graveyard, {this} gets +2/+2"));
         Effect effect = new ConditionalContinuousEffect(
-                new GainAbilitySourceEffect(new DiesTriggeredAbility(new LoseLifeSourceControllerEffect(4))),
+                new GainAbilitySourceEffect(new DiesSourceTriggeredAbility(new LoseLifeSourceControllerEffect(4))),
                 new CardsInControllerGraveCondition(7), "and has \"When {this} dies, you lose 4 life.\""
         );
         ability.addEffect(effect);
diff --git a/Mage.Sets/src/mage/cards/t/TreasureKeeper.java b/Mage.Sets/src/mage/cards/t/TreasureKeeper.java
index a8752827b0..cd6019caee 100644
--- a/Mage.Sets/src/mage/cards/t/TreasureKeeper.java
+++ b/Mage.Sets/src/mage/cards/t/TreasureKeeper.java
@@ -3,7 +3,7 @@ package mage.cards.t;
 import mage.MageInt;
 import mage.MageObjectReference;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.cards.Card;
 import mage.cards.CardImpl;
@@ -32,7 +32,7 @@ public final class TreasureKeeper extends CardImpl {
 
         // When Treasure Keeper dies, reveal cards from the top of your library until you reveal a nonland card with converted mana cost 3 or less.
         // You may cast that card without paying its mana cost. Put all revealed cards not cast this way on the bottom of your library in a random order.
-        this.addAbility(new DiesTriggeredAbility(new TreasureKeeperEffect()));
+        this.addAbility(new DiesSourceTriggeredAbility(new TreasureKeeperEffect()));
     }
 
     public TreasureKeeper(final TreasureKeeper card) {
diff --git a/Mage.Sets/src/mage/cards/t/TreeshakerChimera.java b/Mage.Sets/src/mage/cards/t/TreeshakerChimera.java
index d9f9476d45..d61b3b66bd 100644
--- a/Mage.Sets/src/mage/cards/t/TreeshakerChimera.java
+++ b/Mage.Sets/src/mage/cards/t/TreeshakerChimera.java
@@ -1,7 +1,7 @@
 package mage.cards.t;
 
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.abilities.effects.common.combat.MustBeBlockedByAllSourceEffect;
@@ -28,7 +28,7 @@ public final class TreeshakerChimera extends CardImpl {
         this.addAbility(new SimpleStaticAbility(new MustBeBlockedByAllSourceEffect()));
 
         // When Treeshaker Chimera dies, draw three cards.
-        this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(3)));
+        this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(3)));
     }
 
     private TreeshakerChimera(final TreeshakerChimera card) {
diff --git a/Mage.Sets/src/mage/cards/t/TukatongueThallid.java b/Mage.Sets/src/mage/cards/t/TukatongueThallid.java
index 4d87a9caf8..ba0779473a 100644
--- a/Mage.Sets/src/mage/cards/t/TukatongueThallid.java
+++ b/Mage.Sets/src/mage/cards/t/TukatongueThallid.java
@@ -4,7 +4,7 @@ package mage.cards.t;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -25,7 +25,7 @@ public final class TukatongueThallid extends CardImpl {
         this.power = new MageInt(1);
         this.toughness = new MageInt(1);
 
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new SaprolingToken()), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new SaprolingToken()), false));
     }
 
     public TukatongueThallid(final TukatongueThallid card) {
diff --git a/Mage.Sets/src/mage/cards/t/TuktukTheExplorer.java b/Mage.Sets/src/mage/cards/t/TuktukTheExplorer.java
index 29eb1f246b..b6a67f4bd4 100644
--- a/Mage.Sets/src/mage/cards/t/TuktukTheExplorer.java
+++ b/Mage.Sets/src/mage/cards/t/TuktukTheExplorer.java
@@ -3,7 +3,7 @@ package mage.cards.t;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.abilities.keyword.HasteAbility;
 import mage.cards.CardImpl;
@@ -27,7 +27,7 @@ public final class TuktukTheExplorer extends CardImpl {
         this.power = new MageInt(1);
         this.toughness = new MageInt(1);
         this.addAbility(HasteAbility.getInstance());
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new TuktukTheReturnedToken(expansionSetCode))));
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new TuktukTheReturnedToken(expansionSetCode))));
     }
 
     public TuktukTheExplorer(final TuktukTheExplorer card) {
diff --git a/Mage.Sets/src/mage/cards/u/UndeadExecutioner.java b/Mage.Sets/src/mage/cards/u/UndeadExecutioner.java
index 51f086068f..681b724702 100644
--- a/Mage.Sets/src/mage/cards/u/UndeadExecutioner.java
+++ b/Mage.Sets/src/mage/cards/u/UndeadExecutioner.java
@@ -4,7 +4,7 @@ package mage.cards.u;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.continuous.BoostTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -28,7 +28,7 @@ public final class UndeadExecutioner extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Undead Executioner dies, you may have target creature get -2/-2 until end of turn.
-        Ability ability = new DiesTriggeredAbility(new BoostTargetEffect(-2, -2, Duration.EndOfTurn), true);
+        Ability ability = new DiesSourceTriggeredAbility(new BoostTargetEffect(-2, -2, Duration.EndOfTurn), true);
         ability.addTarget(new TargetCreaturePermanent());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/u/UnderworldCerberus.java b/Mage.Sets/src/mage/cards/u/UnderworldCerberus.java
index 85f0a72e52..e648342fc3 100644
--- a/Mage.Sets/src/mage/cards/u/UnderworldCerberus.java
+++ b/Mage.Sets/src/mage/cards/u/UnderworldCerberus.java
@@ -4,7 +4,7 @@ package mage.cards.u;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.common.CantBeTargetedCardsGraveyardsEffect;
 import mage.abilities.effects.common.ExileSourceEffect;
@@ -37,7 +37,7 @@ public final class UnderworldCerberus extends CardImpl {
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantBeTargetedCardsGraveyardsEffect()));
 
         // When Underworld Cerberus dies, exile it and each player returns all creature cards from their graveyard to their hand.
-        Ability ability = new DiesTriggeredAbility(new ExileSourceEffect());
+        Ability ability = new DiesSourceTriggeredAbility(new ExileSourceEffect());
         ability.addEffect(new ReturnToHandFromGraveyardAllEffect(new FilterCreatureCard("creature cards")));
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/u/UnderworldSentinel.java b/Mage.Sets/src/mage/cards/u/UnderworldSentinel.java
index 1ad813bb1a..40db7ee597 100644
--- a/Mage.Sets/src/mage/cards/u/UnderworldSentinel.java
+++ b/Mage.Sets/src/mage/cards/u/UnderworldSentinel.java
@@ -3,7 +3,7 @@ package mage.cards.u;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.AttacksTriggeredAbility;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.ExileTargetForSourceEffect;
 import mage.cards.CardImpl;
@@ -40,7 +40,7 @@ public final class UnderworldSentinel extends CardImpl {
         this.addAbility(ability);
 
         // When Underworld Sentinel dies, put all cards exiled with it onto the battlefield.
-        this.addAbility(new DiesTriggeredAbility(new UnderworldSentinelEffect()));
+        this.addAbility(new DiesSourceTriggeredAbility(new UnderworldSentinelEffect()));
     }
 
     private UnderworldSentinel(final UnderworldSentinel card) {
diff --git a/Mage.Sets/src/mage/cards/u/UndyingBeast.java b/Mage.Sets/src/mage/cards/u/UndyingBeast.java
index 8dad549bf4..201f45d8b4 100644
--- a/Mage.Sets/src/mage/cards/u/UndyingBeast.java
+++ b/Mage.Sets/src/mage/cards/u/UndyingBeast.java
@@ -4,7 +4,7 @@ package mage.cards.u;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.cards.Card;
 import mage.cards.CardImpl;
@@ -29,7 +29,7 @@ public final class UndyingBeast extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Undying Beast dies, put it on top of its owner's library.
-        this.addAbility(new DiesTriggeredAbility(new UndyingBeastEffect()));
+        this.addAbility(new DiesSourceTriggeredAbility(new UndyingBeastEffect()));
     }
 
     public UndyingBeast(final UndyingBeast card) {
diff --git a/Mage.Sets/src/mage/cards/v/VastwoodHydra.java b/Mage.Sets/src/mage/cards/v/VastwoodHydra.java
index 0aaed1b552..eea7f827e3 100644
--- a/Mage.Sets/src/mage/cards/v/VastwoodHydra.java
+++ b/Mage.Sets/src/mage/cards/v/VastwoodHydra.java
@@ -4,7 +4,7 @@ package mage.cards.v;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldAbility;
 import mage.abilities.dynamicvalue.common.CountersSourceCount;
 import mage.abilities.effects.OneShotEffect;
@@ -45,7 +45,7 @@ public final class VastwoodHydra extends CardImpl {
         this.addAbility(new EntersBattlefieldAbility(new EntersBattlefieldWithXCountersEffect(CounterType.P1P1.createInstance())));
 
         // When Vastwood Hydra dies, you may distribute a number of +1/+1 counters equal to the number of +1/+1 counters on Vastwood Hydra among any number of creatures you control.
-        Ability ability = new DiesTriggeredAbility(new VastwoodHydraDistributeEffect(), true);
+        Ability ability = new DiesSourceTriggeredAbility(new VastwoodHydraDistributeEffect(), true);
         ability.addTarget(new TargetCreaturePermanentAmount(new CountersSourceCount(CounterType.P1P1), filter));
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/v/VenerableKnight.java b/Mage.Sets/src/mage/cards/v/VenerableKnight.java
index dc14f248f3..475806ee62 100644
--- a/Mage.Sets/src/mage/cards/v/VenerableKnight.java
+++ b/Mage.Sets/src/mage/cards/v/VenerableKnight.java
@@ -2,7 +2,7 @@ package mage.cards.v;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.counter.AddCountersTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -31,7 +31,7 @@ public final class VenerableKnight extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Venerable Knight dies, put a +1/+1 counter on target Knight you control.
-        Ability ability = new DiesTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance()));
+        Ability ability = new DiesSourceTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance()));
         ability.addTarget(new TargetPermanent(filter));
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/v/VengeantVampire.java b/Mage.Sets/src/mage/cards/v/VengeantVampire.java
index 5e15e894da..b8b89d0249 100644
--- a/Mage.Sets/src/mage/cards/v/VengeantVampire.java
+++ b/Mage.Sets/src/mage/cards/v/VengeantVampire.java
@@ -2,7 +2,7 @@ package mage.cards.v;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DestroyTargetEffect;
 import mage.abilities.effects.common.GainLifeEffect;
 import mage.abilities.keyword.LifelinkAbility;
@@ -32,7 +32,7 @@ public final class VengeantVampire extends CardImpl {
         this.addAbility(LifelinkAbility.getInstance());
 
         // When Vengeant Vampire dies, destroy target creature an opponent controls and you gain 4 life.
-        Ability ability = new DiesTriggeredAbility(new DestroyTargetEffect());
+        Ability ability = new DiesSourceTriggeredAbility(new DestroyTargetEffect());
         ability.addEffect(new GainLifeEffect(4).concatBy("and"));
         ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE));
         this.addAbility(ability);
diff --git a/Mage.Sets/src/mage/cards/v/VerdantRebirth.java b/Mage.Sets/src/mage/cards/v/VerdantRebirth.java
index db3c9e55dc..6197df2355 100644
--- a/Mage.Sets/src/mage/cards/v/VerdantRebirth.java
+++ b/Mage.Sets/src/mage/cards/v/VerdantRebirth.java
@@ -3,7 +3,7 @@ package mage.cards.v;
 
 import java.util.UUID;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.abilities.effects.common.ReturnToHandSourceEffect;
@@ -24,7 +24,7 @@ public final class VerdantRebirth extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}");
 
         // Until end of turn, target creature gains "When this creature dies, return it to its owner's hand."
-        Ability gainedAbility = new DiesTriggeredAbility(new ReturnToHandSourceEffect(), false);
+        Ability gainedAbility = new DiesSourceTriggeredAbility(new ReturnToHandSourceEffect(), false);
         Effect effect = new GainAbilityTargetEffect(gainedAbility, Duration.EndOfTurn);
         effect.setText("Until end of turn, target creature gains \"When this creature dies, return it to its owner's hand.\"");
         this.getSpellAbility().addEffect(effect);
diff --git a/Mage.Sets/src/mage/cards/v/VeteranExplorer.java b/Mage.Sets/src/mage/cards/v/VeteranExplorer.java
index 0e34e7fb8c..a77af3879e 100644
--- a/Mage.Sets/src/mage/cards/v/VeteranExplorer.java
+++ b/Mage.Sets/src/mage/cards/v/VeteranExplorer.java
@@ -6,7 +6,7 @@ import java.util.List;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -36,7 +36,7 @@ public final class VeteranExplorer extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Veteran Explorer dies, each player may search their library for up to two basic land cards and put them onto the battlefield. Then each player who searched their library this way shuffles it.
-        this.addAbility(new DiesTriggeredAbility(new VeteranExplorerEffect()));
+        this.addAbility(new DiesSourceTriggeredAbility(new VeteranExplorerEffect()));
     }
 
     public VeteranExplorer(final VeteranExplorer card) {
diff --git a/Mage.Sets/src/mage/cards/v/VexingSphinx.java b/Mage.Sets/src/mage/cards/v/VexingSphinx.java
index a7c10ca324..7e98531098 100644
--- a/Mage.Sets/src/mage/cards/v/VexingSphinx.java
+++ b/Mage.Sets/src/mage/cards/v/VexingSphinx.java
@@ -3,7 +3,7 @@ package mage.cards.v;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.costs.common.DiscardCardCost;
 import mage.abilities.keyword.FlyingAbility;
 import mage.abilities.dynamicvalue.common.CountersSourceCount;
@@ -35,7 +35,7 @@ public final class VexingSphinx extends CardImpl {
         this.addAbility(new CumulativeUpkeepAbility(new DiscardCardCost()));
 
         // When Vexing Sphinx dies, draw a card for each age counter on it.
-        this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(new CountersSourceCount(CounterType.AGE))));
+        this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(new CountersSourceCount(CounterType.AGE))));
 
     }
 
diff --git a/Mage.Sets/src/mage/cards/v/VindictiveLich.java b/Mage.Sets/src/mage/cards/v/VindictiveLich.java
index 281345c030..1ffd56cfa8 100644
--- a/Mage.Sets/src/mage/cards/v/VindictiveLich.java
+++ b/Mage.Sets/src/mage/cards/v/VindictiveLich.java
@@ -3,7 +3,7 @@ package mage.cards.v;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.Mode;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.LoseLifeTargetEffect;
 import mage.abilities.effects.common.SacrificeEffect;
 import mage.abilities.effects.common.discard.DiscardTargetEffect;
@@ -34,7 +34,7 @@ public final class VindictiveLich extends CardImpl {
         // When Vindictive Lich dies, choose one or more. Each mode must target a different player.
 
         // * Target opponent sacrifices a creature.
-        Ability ability = new DiesTriggeredAbility(new SacrificeEffect(StaticFilters.FILTER_PERMANENT_CREATURE, 1, "target opponent"));
+        Ability ability = new DiesSourceTriggeredAbility(new SacrificeEffect(StaticFilters.FILTER_PERMANENT_CREATURE, 1, "target opponent"));
         ability.getModes().setMinModes(1);
         ability.getModes().setMaxModes(3);
         ability.getModes().setEachModeOnlyOnce(true);
diff --git a/Mage.Sets/src/mage/cards/v/ViridianEmissary.java b/Mage.Sets/src/mage/cards/v/ViridianEmissary.java
index 9b4dc18c0a..5e5c53710d 100644
--- a/Mage.Sets/src/mage/cards/v/ViridianEmissary.java
+++ b/Mage.Sets/src/mage/cards/v/ViridianEmissary.java
@@ -2,7 +2,7 @@
 package mage.cards.v;
 
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -28,7 +28,7 @@ public final class ViridianEmissary extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Viridian Emissary dies, you may search your library for a basic land card, put it onto the battlefield tapped, then shuffle your library.
-        this.addAbility(new DiesTriggeredAbility(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true), true));
+        this.addAbility(new DiesSourceTriggeredAbility(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true), true));
     }
 
     public ViridianEmissary(final ViridianEmissary card) {
diff --git a/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java b/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java
index 9cfe01d0df..eaa0fb7424 100644
--- a/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java
+++ b/Mage.Sets/src/mage/cards/v/VodalianWarMachine.java
@@ -9,7 +9,7 @@ import java.util.UUID;
 import mage.MageInt;
 import mage.MageObjectReference;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.Cost;
 import mage.abilities.costs.common.TapTargetCost;
@@ -80,7 +80,7 @@ public final class VodalianWarMachine extends CardImpl {
     }
 }
 
-class VodalianWarMachineTriggeredAbility extends DiesTriggeredAbility {
+class VodalianWarMachineTriggeredAbility extends DiesSourceTriggeredAbility {
 
     public VodalianWarMachineTriggeredAbility() {
         super(new VodalianWarMachineEffect(), false);
diff --git a/Mage.Sets/src/mage/cards/v/VoiceOfResurgence.java b/Mage.Sets/src/mage/cards/v/VoiceOfResurgence.java
index dcdecac26e..265a2b732e 100644
--- a/Mage.Sets/src/mage/cards/v/VoiceOfResurgence.java
+++ b/Mage.Sets/src/mage/cards/v/VoiceOfResurgence.java
@@ -1,7 +1,7 @@
 package mage.cards.v;
 
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.SpellCastOpponentTriggeredAbility;
 import mage.abilities.condition.common.MyTurnCondition;
 import mage.abilities.decorator.ConditionalTriggeredAbility;
@@ -37,7 +37,7 @@ public final class VoiceOfResurgence extends CardImpl {
                         new SpellCastOpponentTriggeredAbility(null, new FilterSpell("a spell"), false),
                         MyTurnCondition.instance,
                         "Whenever an opponent casts a spell during your turn, "),
-                new DiesTriggeredAbility(null, false));
+                new DiesSourceTriggeredAbility(null, false));
         ability.setLeavesTheBattlefieldTrigger(true);
         ability.addHint(MyTurnHint.instance);
         ability.addHint(CreaturesYouControlHint.instance);
diff --git a/Mage.Sets/src/mage/cards/v/VolatileRig.java b/Mage.Sets/src/mage/cards/v/VolatileRig.java
index a3091c63a4..cd5be00e17 100644
--- a/Mage.Sets/src/mage/cards/v/VolatileRig.java
+++ b/Mage.Sets/src/mage/cards/v/VolatileRig.java
@@ -4,7 +4,7 @@ import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.TriggeredAbilityImpl;
 import mage.abilities.common.AttacksEachCombatStaticAbility;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.keyword.TrampleAbility;
 import mage.cards.CardImpl;
@@ -42,7 +42,7 @@ public final class VolatileRig extends CardImpl {
         this.addAbility(new VolatileRigTriggeredAbility());
 
         // When Volatile Rig dies, flip a coin. If you lose the flip, it deals 4 damage to each creature and each player.
-        this.addAbility(new DiesTriggeredAbility(new VolatileRigEffect2()));
+        this.addAbility(new DiesSourceTriggeredAbility(new VolatileRigEffect2()));
 
     }
 
diff --git a/Mage.Sets/src/mage/cards/w/WantedScoundrels.java b/Mage.Sets/src/mage/cards/w/WantedScoundrels.java
index 3c55abe065..df8e00ce74 100644
--- a/Mage.Sets/src/mage/cards/w/WantedScoundrels.java
+++ b/Mage.Sets/src/mage/cards/w/WantedScoundrels.java
@@ -4,7 +4,7 @@ package mage.cards.w;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -28,7 +28,7 @@ public final class WantedScoundrels extends CardImpl {
         this.toughness = new MageInt(3);
 
         // When Wanted Scoundrels dies, target opponent creates two colorless Treasure artifact tokens with "T, Sacrifice this artifact: Add one mana of any color."
-        Ability ability = new DiesTriggeredAbility(new CreateTokenTargetEffect(new TreasureToken(), 2), false);
+        Ability ability = new DiesSourceTriggeredAbility(new CreateTokenTargetEffect(new TreasureToken(), 2), false);
         ability.addTarget(new TargetOpponent());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/w/WeatherseedTreefolk.java b/Mage.Sets/src/mage/cards/w/WeatherseedTreefolk.java
index 491122aa0e..8e70080ca5 100644
--- a/Mage.Sets/src/mage/cards/w/WeatherseedTreefolk.java
+++ b/Mage.Sets/src/mage/cards/w/WeatherseedTreefolk.java
@@ -3,7 +3,7 @@ package mage.cards.w;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.ReturnToHandSourceEffect;
 import mage.abilities.keyword.TrampleAbility;
 import mage.cards.CardImpl;
@@ -27,7 +27,7 @@ public final class WeatherseedTreefolk extends CardImpl {
         // Trample
         this.addAbility(TrampleAbility.getInstance());
         // When Weatherseed Treefolk dies, return it to its owner's hand.
-        this.addAbility(new DiesTriggeredAbility(new ReturnToHandSourceEffect()));
+        this.addAbility(new DiesSourceTriggeredAbility(new ReturnToHandSourceEffect()));
     }
 
     public WeatherseedTreefolk(final WeatherseedTreefolk card) {
diff --git a/Mage.Sets/src/mage/cards/w/WelkinHawk.java b/Mage.Sets/src/mage/cards/w/WelkinHawk.java
index 2cbd7fb5b2..6228cc3c94 100644
--- a/Mage.Sets/src/mage/cards/w/WelkinHawk.java
+++ b/Mage.Sets/src/mage/cards/w/WelkinHawk.java
@@ -3,7 +3,7 @@ package mage.cards.w;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
@@ -37,7 +37,7 @@ public final class WelkinHawk extends CardImpl {
 
         // When Welkin Hawk dies, you may search your library for a card named Welkin Hawk, reveal that card, put it into your hand, then shuffle your library.
         TargetCardInLibrary target = new TargetCardInLibrary(1, 1, filter);
-        this.addAbility(new DiesTriggeredAbility(new SearchLibraryPutInHandEffect(target, true, true), true));
+        this.addAbility(new DiesSourceTriggeredAbility(new SearchLibraryPutInHandEffect(target, true, true), true));
     }
 
     public WelkinHawk(final WelkinHawk card) {
diff --git a/Mage.Sets/src/mage/cards/w/WhirlpoolDrake.java b/Mage.Sets/src/mage/cards/w/WhirlpoolDrake.java
index 35f578eccf..73d9dea2dd 100644
--- a/Mage.Sets/src/mage/cards/w/WhirlpoolDrake.java
+++ b/Mage.Sets/src/mage/cards/w/WhirlpoolDrake.java
@@ -3,7 +3,7 @@ package mage.cards.w;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.effects.common.ShuffleHandIntoLibraryDrawThatManySourceEffect;
 import mage.abilities.keyword.FlyingAbility;
@@ -31,7 +31,7 @@ public final class WhirlpoolDrake extends CardImpl {
         this.addAbility(new EntersBattlefieldTriggeredAbility(new ShuffleHandIntoLibraryDrawThatManySourceEffect(), false));
 
         // When Whirlpool Drake dies, shuffle the cards from your hand into your library, then draw that many cards.
-        this.addAbility(new DiesTriggeredAbility(new ShuffleHandIntoLibraryDrawThatManySourceEffect(), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new ShuffleHandIntoLibraryDrawThatManySourceEffect(), false));
     }
 
     public WhirlpoolDrake(final WhirlpoolDrake card) {
diff --git a/Mage.Sets/src/mage/cards/w/WhisperwoodElemental.java b/Mage.Sets/src/mage/cards/w/WhisperwoodElemental.java
index 52b1ca830c..fd8ff5063b 100644
--- a/Mage.Sets/src/mage/cards/w/WhisperwoodElemental.java
+++ b/Mage.Sets/src/mage/cards/w/WhisperwoodElemental.java
@@ -5,7 +5,7 @@ import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.BeginningOfEndStepTriggeredAbility;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.common.SacrificeSourceCost;
 import mage.abilities.effects.Effect;
@@ -46,7 +46,7 @@ public final class WhisperwoodElemental extends CardImpl {
         this.addAbility(new BeginningOfEndStepTriggeredAbility(new ManifestEffect(1), TargetController.YOU, false));
         
         // Sacrifice Whisperwood Elemental: Until end of turn, face-up, nontoken creatures you control gain "When this creature dies, manifest the top card of your library."
-        Ability abilityToGain = new DiesTriggeredAbility(new ManifestEffect(1));
+        Ability abilityToGain = new DiesSourceTriggeredAbility(new ManifestEffect(1));
         Effect effect = new GainAbilityControlledEffect(abilityToGain, Duration.EndOfTurn, filter);
         effect.setText("Until end of turn, face-up, nontoken creatures you control gain \"When this creature dies, manifest the top card of your library.\"");
         this.addAbility(new SimpleActivatedAbility(
diff --git a/Mage.Sets/src/mage/cards/w/WirewoodHerald.java b/Mage.Sets/src/mage/cards/w/WirewoodHerald.java
index c55fdbfb25..b0300130b0 100644
--- a/Mage.Sets/src/mage/cards/w/WirewoodHerald.java
+++ b/Mage.Sets/src/mage/cards/w/WirewoodHerald.java
@@ -3,7 +3,7 @@ package mage.cards.w;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -32,7 +32,7 @@ public final class WirewoodHerald extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Wirewood Herald dies, you may search your library for an Elf card, reveal that card, put it into your hand, then shuffle your library.
-        this.addAbility(new DiesTriggeredAbility(
+        this.addAbility(new DiesSourceTriggeredAbility(
                 new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter), true, true),
                 true));
     }
diff --git a/Mage.Sets/src/mage/cards/w/WorkshopAssistant.java b/Mage.Sets/src/mage/cards/w/WorkshopAssistant.java
index 9adc768912..9b2bf29ef3 100644
--- a/Mage.Sets/src/mage/cards/w/WorkshopAssistant.java
+++ b/Mage.Sets/src/mage/cards/w/WorkshopAssistant.java
@@ -4,7 +4,7 @@ package mage.cards.w;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect;
 import mage.cards.CardImpl;
@@ -36,7 +36,7 @@ public final class WorkshopAssistant extends CardImpl {
         // When Workshop Assistant dies, return another target artifact card from your graveyard to your hand.
         Effect effect = new ReturnFromGraveyardToHandTargetEffect();
         effect.setText("return another target artifact card from your graveyard to your hand");
-        Ability ability = new DiesTriggeredAbility(effect);
+        Ability ability = new DiesSourceTriggeredAbility(effect);
         ability.addTarget(new TargetCardInYourGraveyard(filter));
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/w/WorldShaper.java b/Mage.Sets/src/mage/cards/w/WorldShaper.java
index e134a44a4f..ce75ae985e 100644
--- a/Mage.Sets/src/mage/cards/w/WorldShaper.java
+++ b/Mage.Sets/src/mage/cards/w/WorldShaper.java
@@ -5,7 +5,7 @@ import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.AttacksTriggeredAbility;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.PutTopCardOfLibraryIntoGraveControllerEffect;
 import mage.cards.CardImpl;
@@ -36,7 +36,7 @@ public final class WorldShaper extends CardImpl {
         this.addAbility(new AttacksTriggeredAbility(new PutTopCardOfLibraryIntoGraveControllerEffect(3), true));
 
         // When World Shaper dies, put all land cards from your graveyard onto the battlefield tapped.
-        this.addAbility(new DiesTriggeredAbility(new WorldShaperEffect(), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new WorldShaperEffect(), false));
     }
 
     public WorldShaper(final WorldShaper card) {
diff --git a/Mage.Sets/src/mage/cards/w/WorldspineWurm.java b/Mage.Sets/src/mage/cards/w/WorldspineWurm.java
index db6cafe74a..ebd27a0ebe 100644
--- a/Mage.Sets/src/mage/cards/w/WorldspineWurm.java
+++ b/Mage.Sets/src/mage/cards/w/WorldspineWurm.java
@@ -1,7 +1,7 @@
 package mage.cards.w;
 
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.PutIntoGraveFromAnywhereSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.abilities.effects.common.ShuffleIntoLibrarySourceEffect;
@@ -30,7 +30,7 @@ public final class WorldspineWurm extends CardImpl {
         this.addAbility(TrampleAbility.getInstance());
 
         // When Worldspine Wurm dies, create three 5/5 green Wurm creature tokens with trample.
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new WurmWithTrampleToken(), 3)));
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new WurmWithTrampleToken(), 3)));
 
         // When Worldspine Wurm is put into a graveyard from anywhere, shuffle it into its owner's library.
         this.addAbility(new PutIntoGraveFromAnywhereSourceTriggeredAbility(new ShuffleIntoLibrarySourceEffect()));
diff --git a/Mage.Sets/src/mage/cards/w/WretchedCamel.java b/Mage.Sets/src/mage/cards/w/WretchedCamel.java
index 195f091fa0..c5d2d7e2c0 100644
--- a/Mage.Sets/src/mage/cards/w/WretchedCamel.java
+++ b/Mage.Sets/src/mage/cards/w/WretchedCamel.java
@@ -4,7 +4,7 @@ package mage.cards.w;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.condition.OrCondition;
 import mage.abilities.condition.common.CardsInControllerGraveCondition;
 import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
@@ -42,7 +42,7 @@ public final class WretchedCamel extends CardImpl {
 
         // When Wretched Camel dies, if you control a Desert or there is a Desert card in your graveyard, target player discards a card.
         Ability ability = new ConditionalInterveningIfTriggeredAbility(
-                new DiesTriggeredAbility(new DiscardTargetEffect(1)),
+                new DiesSourceTriggeredAbility(new DiscardTargetEffect(1)),
                 new OrCondition(
                         new PermanentsOnTheBattlefieldCondition(new FilterControlledPermanent(filterDesertPermanent)),
                         new CardsInControllerGraveCondition(1, filterDesertCard)),
diff --git a/Mage.Sets/src/mage/cards/w/WurmcoilEngine.java b/Mage.Sets/src/mage/cards/w/WurmcoilEngine.java
index 76e82878d3..f73a0a7348 100644
--- a/Mage.Sets/src/mage/cards/w/WurmcoilEngine.java
+++ b/Mage.Sets/src/mage/cards/w/WurmcoilEngine.java
@@ -2,7 +2,7 @@ package mage.cards.w;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.abilities.keyword.DeathtouchAbility;
 import mage.abilities.keyword.LifelinkAbility;
@@ -31,7 +31,7 @@ public final class WurmcoilEngine extends CardImpl {
         this.addAbility(LifelinkAbility.getInstance());
 
         // When Wurmcoil Engine dies, create a 3/3 colorless Wurm artifact creature token with deathtouch and a 3/3 colorless Wurm artifact creature token with lifelink.
-        Ability ability = new DiesTriggeredAbility(new CreateTokenEffect(new WurmWithDeathtouchToken()), false);
+        Ability ability = new DiesSourceTriggeredAbility(new CreateTokenEffect(new WurmWithDeathtouchToken()), false);
         ability.addEffect(new CreateTokenEffect(new WurmWithLifelinkToken()));
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/y/YavimayaElder.java b/Mage.Sets/src/mage/cards/y/YavimayaElder.java
index 33687685e8..bc46c0636a 100644
--- a/Mage.Sets/src/mage/cards/y/YavimayaElder.java
+++ b/Mage.Sets/src/mage/cards/y/YavimayaElder.java
@@ -3,7 +3,7 @@ package mage.cards.y;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.common.SacrificeSourceCost;
 import mage.abilities.costs.mana.GenericManaCost;
@@ -34,7 +34,7 @@ public final class YavimayaElder extends CardImpl {
         this.toughness = new MageInt(1);
 
         // When Yavimaya Elder dies, you may search your library for up to two basic land cards, reveal them, and put them into your hand. If you do, shuffle your library.
-        this.addAbility(new DiesTriggeredAbility(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(0, 2, StaticFilters.FILTER_CARD_BASIC_LAND), true), true));
+        this.addAbility(new DiesSourceTriggeredAbility(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(0, 2, StaticFilters.FILTER_CARD_BASIC_LAND), true), true));
         // {2}, Sacrifice Yavimaya Elder: Draw a card.
         Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), new GenericManaCost(2));
         ability.addCost(new SacrificeSourceCost());
diff --git a/Mage.Sets/src/mage/cards/y/YoseiTheMorningStar.java b/Mage.Sets/src/mage/cards/y/YoseiTheMorningStar.java
index 1eed365c5a..32c1b591a1 100644
--- a/Mage.Sets/src/mage/cards/y/YoseiTheMorningStar.java
+++ b/Mage.Sets/src/mage/cards/y/YoseiTheMorningStar.java
@@ -5,7 +5,7 @@ import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.Mode;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.SkipNextPlayerUntapStepEffect;
 import mage.abilities.keyword.FlyingAbility;
@@ -44,7 +44,7 @@ public final class YoseiTheMorningStar extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
 
         // When Yosei, the Morning Star dies, target player skips their next untap step. Tap up to five target permanents that player controls.
-        Ability ability = new DiesTriggeredAbility(new SkipNextPlayerUntapStepEffect("target"));
+        Ability ability = new DiesSourceTriggeredAbility(new SkipNextPlayerUntapStepEffect("target"));
         ability.addTarget(new TargetPlayer());
         ability.addTarget(new YoseiTheMorningStarTarget());
         ability.addEffect(new YoseiTheMorningStarTapEffect());
diff --git a/Mage.Sets/src/mage/cards/y/YouthfulScholar.java b/Mage.Sets/src/mage/cards/y/YouthfulScholar.java
index ed3422bd95..dda5ec2eaa 100644
--- a/Mage.Sets/src/mage/cards/y/YouthfulScholar.java
+++ b/Mage.Sets/src/mage/cards/y/YouthfulScholar.java
@@ -3,7 +3,7 @@ package mage.cards.y;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -24,7 +24,7 @@ public final class YouthfulScholar extends CardImpl {
         this.toughness = new MageInt(2);
 
         // When Youthful Scholar dies, draw two cards.
-        this.addAbility(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(2), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(2), false));
     }
 
     public YouthfulScholar(final YouthfulScholar card) {
diff --git a/Mage.Sets/src/mage/cards/z/ZodiacDragon.java b/Mage.Sets/src/mage/cards/z/ZodiacDragon.java
index e9b40d218c..5a862db733 100644
--- a/Mage.Sets/src/mage/cards/z/ZodiacDragon.java
+++ b/Mage.Sets/src/mage/cards/z/ZodiacDragon.java
@@ -3,7 +3,7 @@ package mage.cards.z;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.ReturnToHandSourceEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -23,7 +23,7 @@ public final class ZodiacDragon extends CardImpl {
         this.toughness = new MageInt(8);
 
         // When Zodiac Dragon is put into your graveyard from the battlefield, you may return it to your hand.
-        this.addAbility(new DiesTriggeredAbility(new ReturnToHandSourceEffect(), true));
+        this.addAbility(new DiesSourceTriggeredAbility(new ReturnToHandSourceEffect(), true));
     }
 
     public ZodiacDragon(final ZodiacDragon card) {
diff --git a/Mage/src/main/java/mage/abilities/common/DiesSourceTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DiesSourceTriggeredAbility.java
new file mode 100644
index 0000000000..9a9d219669
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/common/DiesSourceTriggeredAbility.java
@@ -0,0 +1,79 @@
+package mage.abilities.common;
+
+import mage.MageObject;
+import mage.abilities.effects.Effect;
+import mage.constants.Zone;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+import mage.game.events.ZoneChangeEvent;
+import mage.game.permanent.Permanent;
+import mage.game.permanent.PermanentToken;
+
+/**
+ * @author BetaSteward_at_googlemail.com
+ */
+public class DiesSourceTriggeredAbility extends ZoneChangeTriggeredAbility {
+
+    public DiesSourceTriggeredAbility(Effect effect, boolean optional) {
+        super(Zone.BATTLEFIELD, Zone.GRAVEYARD, effect, "When {this} dies, ", optional);
+    }
+
+    public DiesSourceTriggeredAbility(Effect effect) {
+        this(effect, false);
+    }
+
+    public DiesSourceTriggeredAbility(DiesSourceTriggeredAbility ability) {
+        super(ability);
+    }
+
+    @Override
+    public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
+        // check it was previously on battlefield
+        Permanent before = ((ZoneChangeEvent) event).getTarget();
+        if (before == null) {
+            return false;
+        }
+        if (!this.hasSourceObjectAbility(game, before, event)) { // the permanent does not have the ability so no trigger
+            return false;
+        }
+        // check now it is in graveyard if it is no token
+        if (!(before instanceof PermanentToken) && before.getZoneChangeCounter(game) + 1 == game.getState().getZoneChangeCounter(sourceId)) {
+            Zone after = game.getState().getZone(sourceId);
+            return after != null && Zone.GRAVEYARD.match(after);
+        } else {
+            // Already moved to another zone, so guess it's ok
+            return true;
+        }
+    }
+
+    @Override
+    public boolean checkEventType(GameEvent event, Game game) {
+        if (super.checkEventType(event, game)) {
+            return ((ZoneChangeEvent) event).getFromZone() == Zone.BATTLEFIELD && ((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD;
+        }
+        return false;
+    }
+
+    @Override
+    public DiesSourceTriggeredAbility copy() {
+        return new DiesSourceTriggeredAbility(this);
+    }
+
+    @Override
+    public boolean checkTrigger(GameEvent event, Game game) {
+        if (super.checkTrigger(event, game)) {
+            ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
+            if (zEvent.getTarget().isTransformable()) {
+                if (!zEvent.getTarget().getAbilities().contains(this)) {
+                    return false;
+                }
+            }
+            for (Effect effect : getEffects()) {
+                effect.setValue("permanentLeftBattlefield", zEvent.getTarget());
+            }
+            return true;
+        }
+        return false;
+    }
+
+}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/ReturnToBattlefieldUnderOwnerControlTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ReturnToBattlefieldUnderOwnerControlTargetEffect.java
index db66028de2..a801160d0f 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/ReturnToBattlefieldUnderOwnerControlTargetEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/ReturnToBattlefieldUnderOwnerControlTargetEffect.java
@@ -52,7 +52,7 @@ public class ReturnToBattlefieldUnderOwnerControlTargetEffect extends OneShotEff
     }
 
     private void updateText() {
-        this.staticText = "return " + this.returnName
+        this.staticText = "then return " + this.returnName
                 + " to the battlefield" + (tapped ? " tapped" : "")
                 + " under " + this.returnUnderControlName + " control";
     }
diff --git a/Mage/src/main/java/mage/abilities/keyword/AfflictAbility.java b/Mage/src/main/java/mage/abilities/keyword/AfflictAbility.java
index 2e8feb1cee..0bf1615332 100644
--- a/Mage/src/main/java/mage/abilities/keyword/AfflictAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/AfflictAbility.java
@@ -1,13 +1,13 @@
 package mage.abilities.keyword;
 
 import java.util.UUID;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.common.LoseLifeTargetEffect;
 import mage.game.Game;
 import mage.game.events.GameEvent;
 import mage.target.targetpointer.FixedTarget;
 
-public class AfflictAbility extends BecomesBlockedTriggeredAbility {
+public class AfflictAbility extends BecomesBlockedSourceTriggeredAbility {
 
     private final int lifeLoss;
 
diff --git a/Mage/src/main/java/mage/abilities/keyword/AfterlifeAbility.java b/Mage/src/main/java/mage/abilities/keyword/AfterlifeAbility.java
index 2fa1c7ecf3..9f87d2039b 100644
--- a/Mage/src/main/java/mage/abilities/keyword/AfterlifeAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/AfterlifeAbility.java
@@ -1,11 +1,11 @@
 package mage.abilities.keyword;
 
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.game.permanent.token.WhiteBlackSpiritToken;
 import mage.util.CardUtil;
 
-public class AfterlifeAbility extends DiesTriggeredAbility {
+public class AfterlifeAbility extends DiesSourceTriggeredAbility {
 
     private final int tokenCount;
 
diff --git a/Mage/src/main/java/mage/abilities/keyword/CumulativeUpkeepAbility.java b/Mage/src/main/java/mage/abilities/keyword/CumulativeUpkeepAbility.java
index 546de34cf2..6436544db8 100644
--- a/Mage/src/main/java/mage/abilities/keyword/CumulativeUpkeepAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/CumulativeUpkeepAbility.java
@@ -1,4 +1,3 @@
-
 package mage.abilities.keyword;
 
 import mage.abilities.Ability;
diff --git a/Mage/src/main/java/mage/abilities/keyword/ModularAbility.java b/Mage/src/main/java/mage/abilities/keyword/ModularAbility.java
index 70c22a4484..f0155b945a 100644
--- a/Mage/src/main/java/mage/abilities/keyword/ModularAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/ModularAbility.java
@@ -5,7 +5,7 @@ import java.util.List;
 import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.StaticAbility;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.EntersBattlefieldEffect;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.counter.AddCountersSourceEffect;
@@ -38,7 +38,7 @@ import mage.util.CardUtil;
  *
  * @author Loki, LevelX2
  */
-public class ModularAbility extends DiesTriggeredAbility {
+public class ModularAbility extends DiesSourceTriggeredAbility {
 
     private static final FilterArtifactPermanent filter = new FilterArtifactPermanent("artifact creature");
 
diff --git a/Mage/src/main/java/mage/abilities/keyword/MorphAbility.java b/Mage/src/main/java/mage/abilities/keyword/MorphAbility.java
index e6482cc92e..203e7686ed 100644
--- a/Mage/src/main/java/mage/abilities/keyword/MorphAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/MorphAbility.java
@@ -126,7 +126,8 @@ public class MorphAbility extends StaticAbility implements AlternativeSourceCost
 
         ruleText = sb.toString();
 
-        Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BecomesFaceDownCreatureEffect(morphCosts, (megamorph ? FaceDownType.MEGAMORPHED : FaceDownType.MORPHED)));
+        Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BecomesFaceDownCreatureEffect(
+                morphCosts, (megamorph ? FaceDownType.MEGAMORPHED : FaceDownType.MORPHED)));
         ability.setWorksFaceDown(true);
         ability.setRuleVisible(false);
         addSubAbility(ability);
diff --git a/Mage/src/main/java/mage/abilities/keyword/PersistAbility.java b/Mage/src/main/java/mage/abilities/keyword/PersistAbility.java
index 43aa4bea32..adf31aceaf 100644
--- a/Mage/src/main/java/mage/abilities/keyword/PersistAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/PersistAbility.java
@@ -2,7 +2,7 @@
 package mage.abilities.keyword;
 
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect;
 import mage.constants.Outcome;
@@ -13,7 +13,7 @@ import mage.game.events.GameEvent;
 import mage.game.events.ZoneChangeEvent;
 import mage.game.permanent.Permanent;
 
-public class PersistAbility extends DiesTriggeredAbility {
+public class PersistAbility extends DiesSourceTriggeredAbility {
 
     public PersistAbility() {
         super(new PersistEffect());
diff --git a/Mage/src/main/java/mage/abilities/keyword/RampageAbility.java b/Mage/src/main/java/mage/abilities/keyword/RampageAbility.java
index 00b7bf47be..a7c95e2c1f 100644
--- a/Mage/src/main/java/mage/abilities/keyword/RampageAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/RampageAbility.java
@@ -1,7 +1,7 @@
 package mage.abilities.keyword;
 
 import mage.abilities.Ability;
-import mage.abilities.common.BecomesBlockedTriggeredAbility;
+import mage.abilities.common.BecomesBlockedSourceTriggeredAbility;
 import mage.abilities.dynamicvalue.DynamicValue;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.continuous.BoostSourceEffect;
@@ -13,7 +13,7 @@ import mage.game.combat.CombatGroup;
  *
  * @author LoneFox
  */
-public class RampageAbility extends BecomesBlockedTriggeredAbility {
+public class RampageAbility extends BecomesBlockedSourceTriggeredAbility {
 
     private final String rule;
 
diff --git a/Mage/src/main/java/mage/abilities/keyword/RepairAbility.java b/Mage/src/main/java/mage/abilities/keyword/RepairAbility.java
index 9bde3078e5..9b9ee84a88 100644
--- a/Mage/src/main/java/mage/abilities/keyword/RepairAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/RepairAbility.java
@@ -9,7 +9,7 @@ import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.TriggeredAbilityImpl;
 import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.condition.common.SourceHasCounterCondition;
 import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
 import mage.abilities.dynamicvalue.common.StaticValue;
@@ -30,7 +30,7 @@ import mage.game.events.GameEvent;
  *
  * @author Styxo
  */
-public class RepairAbility extends DiesTriggeredAbility {
+public class RepairAbility extends DiesSourceTriggeredAbility {
 
     private String ruleText;
 
diff --git a/Mage/src/main/java/mage/abilities/keyword/SoulshiftAbility.java b/Mage/src/main/java/mage/abilities/keyword/SoulshiftAbility.java
index 0ccefbb914..fd5ff1e9df 100644
--- a/Mage/src/main/java/mage/abilities/keyword/SoulshiftAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/SoulshiftAbility.java
@@ -3,7 +3,7 @@
 package mage.abilities.keyword;
 
 import mage.constants.ComparisonType;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.dynamicvalue.DynamicValue;
 import mage.abilities.dynamicvalue.common.StaticValue;
 import mage.abilities.effects.common.ReturnToHandTargetEffect;
@@ -27,7 +27,7 @@ import java.util.UUID;
  *
  * @author Loki, LevelX2
  */
-public class SoulshiftAbility extends DiesTriggeredAbility {
+public class SoulshiftAbility extends DiesSourceTriggeredAbility {
     
     private final DynamicValue amount;
 
@@ -57,7 +57,7 @@ public class SoulshiftAbility extends DiesTriggeredAbility {
     }
 
     @Override
-    public DiesTriggeredAbility copy() {
+    public DiesSourceTriggeredAbility copy() {
         return new SoulshiftAbility(this);
     }
 
diff --git a/Mage/src/main/java/mage/abilities/keyword/UndyingAbility.java b/Mage/src/main/java/mage/abilities/keyword/UndyingAbility.java
index 66df34d1fb..c5c31546ec 100644
--- a/Mage/src/main/java/mage/abilities/keyword/UndyingAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/UndyingAbility.java
@@ -1,7 +1,7 @@
 package mage.abilities.keyword;
 
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect;
 import mage.constants.Outcome;
@@ -15,7 +15,7 @@ import mage.game.permanent.Permanent;
 /**
  * @author Loki
  */
-public class UndyingAbility extends DiesTriggeredAbility {
+public class UndyingAbility extends DiesSourceTriggeredAbility {
 
     public UndyingAbility() {
         super(new UndyingEffect());
@@ -27,7 +27,7 @@ public class UndyingAbility extends DiesTriggeredAbility {
     }
 
     @Override
-    public DiesTriggeredAbility copy() {
+    public DiesSourceTriggeredAbility copy() {
         return new UndyingAbility(this);
     }
 
diff --git a/Mage/src/main/java/mage/game/permanent/token/ATATToken.java b/Mage/src/main/java/mage/game/permanent/token/ATATToken.java
index f2d7c93246..e58cb29e90 100644
--- a/Mage/src/main/java/mage/game/permanent/token/ATATToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/ATATToken.java
@@ -4,7 +4,7 @@ package mage.game.permanent.token;
 
 import mage.constants.CardType;
 import mage.constants.SubType;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 
 /**
@@ -19,7 +19,7 @@ public final class ATATToken extends TokenImpl {
         cardType.add(CardType.CREATURE);
         cardType.add(CardType.ARTIFACT);
         color.setWhite(true);
-        addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new TrooperToken(), 2)));
+        addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new TrooperToken(), 2)));
         subtype.add(SubType.ATAT);
     }
 
diff --git a/Mage/src/main/java/mage/game/permanent/token/DevilToken.java b/Mage/src/main/java/mage/game/permanent/token/DevilToken.java
index bb7da534fe..9e30bee955 100644
--- a/Mage/src/main/java/mage/game/permanent/token/DevilToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/DevilToken.java
@@ -5,7 +5,7 @@ import java.util.Collections;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.DamageTargetEffect;
 import mage.constants.CardType;
@@ -29,7 +29,7 @@ public final class DevilToken extends TokenImpl {
         toughness = new MageInt(1);
         Effect effect = new DamageTargetEffect(1);
         effect.setText("it deals 1 damage to any target");
-        Ability ability = new DiesTriggeredAbility(effect);
+        Ability ability = new DiesSourceTriggeredAbility(effect);
         ability.addTarget(new TargetAnyTarget());
         this.addAbility(ability);
     }
diff --git a/Mage/src/main/java/mage/game/permanent/token/FesteringGoblinToken.java b/Mage/src/main/java/mage/game/permanent/token/FesteringGoblinToken.java
index fa5a9797d5..5629aad50c 100644
--- a/Mage/src/main/java/mage/game/permanent/token/FesteringGoblinToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/FesteringGoblinToken.java
@@ -5,7 +5,7 @@ import mage.constants.CardType;
 import mage.constants.SubType;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.continuous.BoostTargetEffect;
 import mage.constants.Duration;
 import mage.target.common.TargetCreaturePermanent;
@@ -26,7 +26,7 @@ public final class FesteringGoblinToken extends TokenImpl {
         power = new MageInt(1);
         toughness = new MageInt(1);
 
-        Ability ability = new DiesTriggeredAbility(new BoostTargetEffect(-1, -1, Duration.EndOfTurn), false);
+        Ability ability = new DiesSourceTriggeredAbility(new BoostTargetEffect(-1, -1, Duration.EndOfTurn), false);
         ability.addTarget(new TargetCreaturePermanent());
         this.addAbility(ability);
     }
diff --git a/Mage/src/main/java/mage/game/permanent/token/GarrukCursedHuntsmanToken.java b/Mage/src/main/java/mage/game/permanent/token/GarrukCursedHuntsmanToken.java
index fd5b1f0c08..2fb450d53e 100644
--- a/Mage/src/main/java/mage/game/permanent/token/GarrukCursedHuntsmanToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/GarrukCursedHuntsmanToken.java
@@ -1,7 +1,7 @@
 package mage.game.permanent.token;
 
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.counter.AddCountersAllEffect;
 import mage.constants.CardType;
 import mage.constants.SubType;
@@ -26,7 +26,7 @@ public final class GarrukCursedHuntsmanToken extends TokenImpl {
         power = new MageInt(2);
         toughness = new MageInt(2);
 
-        this.addAbility(new DiesTriggeredAbility(new AddCountersAllEffect(CounterType.LOYALTY.createInstance(), filter)));
+        this.addAbility(new DiesSourceTriggeredAbility(new AddCountersAllEffect(CounterType.LOYALTY.createInstance(), filter)));
     }
 
     public GarrukCursedHuntsmanToken(final GarrukCursedHuntsmanToken token) {
diff --git a/Mage/src/main/java/mage/game/permanent/token/NestingDragonToken.java b/Mage/src/main/java/mage/game/permanent/token/NestingDragonToken.java
index e33ce89329..b2b640913c 100644
--- a/Mage/src/main/java/mage/game/permanent/token/NestingDragonToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/NestingDragonToken.java
@@ -1,7 +1,7 @@
 package mage.game.permanent.token;
 
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.abilities.keyword.DefenderAbility;
 import mage.constants.CardType;
@@ -30,7 +30,7 @@ public final class NestingDragonToken extends TokenImpl {
         power = new MageInt(0);
         toughness = new MageInt(2);
         addAbility(DefenderAbility.getInstance());
-        this.addAbility(new DiesTriggeredAbility(
+        this.addAbility(new DiesSourceTriggeredAbility(
                 new CreateTokenEffect(new DragonEggDragonToken()), false
         ));
     }
diff --git a/Mage/src/main/java/mage/game/permanent/token/Ooze2Token.java b/Mage/src/main/java/mage/game/permanent/token/Ooze2Token.java
index 42736bec1e..513573d97f 100644
--- a/Mage/src/main/java/mage/game/permanent/token/Ooze2Token.java
+++ b/Mage/src/main/java/mage/game/permanent/token/Ooze2Token.java
@@ -4,7 +4,7 @@ package mage.game.permanent.token;
 import mage.constants.CardType;
 import mage.constants.SubType;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 
 /**
@@ -20,7 +20,7 @@ public final class Ooze2Token extends TokenImpl {
         color.setGreen(true);
         power = new MageInt(2);
         toughness = new MageInt(2);
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new OozeToken(1, 1), 2), false));
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new OozeToken(1, 1), 2), false));
     }
 
     public Ooze2Token(final Ooze2Token token) {
diff --git a/Mage/src/main/java/mage/game/permanent/token/ReefWormFishToken.java b/Mage/src/main/java/mage/game/permanent/token/ReefWormFishToken.java
index 1ac90fc567..70c9cad349 100644
--- a/Mage/src/main/java/mage/game/permanent/token/ReefWormFishToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/ReefWormFishToken.java
@@ -4,7 +4,7 @@ package mage.game.permanent.token;
 import mage.constants.CardType;
 import mage.constants.SubType;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 
 /**
@@ -22,7 +22,7 @@ public final class ReefWormFishToken extends TokenImpl {
         power = new MageInt(3);
         toughness = new MageInt(3);
 
-        addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new ReefWormWhaleToken())));
+        addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new ReefWormWhaleToken())));
     }
 
     public ReefWormFishToken(final ReefWormFishToken token) {
diff --git a/Mage/src/main/java/mage/game/permanent/token/ReefWormWhaleToken.java b/Mage/src/main/java/mage/game/permanent/token/ReefWormWhaleToken.java
index 634a8a8b59..02b9f7d19f 100644
--- a/Mage/src/main/java/mage/game/permanent/token/ReefWormWhaleToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/ReefWormWhaleToken.java
@@ -4,7 +4,7 @@ package mage.game.permanent.token;
 import mage.constants.CardType;
 import mage.constants.SubType;
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 
 /**
@@ -22,7 +22,7 @@ public final class ReefWormWhaleToken extends TokenImpl {
         power = new MageInt(6);
         toughness = new MageInt(6);
 
-        addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new ReefWormKrakenToken())));
+        addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new ReefWormKrakenToken())));
     }
 
     public ReefWormWhaleToken(final ReefWormWhaleToken token) {
diff --git a/Mage/src/main/java/mage/game/permanent/token/WolfsQuarryToken.java b/Mage/src/main/java/mage/game/permanent/token/WolfsQuarryToken.java
index 60f4b9f96d..889e46a3d7 100644
--- a/Mage/src/main/java/mage/game/permanent/token/WolfsQuarryToken.java
+++ b/Mage/src/main/java/mage/game/permanent/token/WolfsQuarryToken.java
@@ -1,7 +1,7 @@
 package mage.game.permanent.token;
 
 import mage.MageInt;
-import mage.abilities.common.DiesTriggeredAbility;
+import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.effects.common.CreateTokenEffect;
 import mage.constants.CardType;
 import mage.constants.SubType;
@@ -19,7 +19,7 @@ public final class WolfsQuarryToken extends TokenImpl {
         power = new MageInt(1);
         toughness = new MageInt(1);
 
-        this.addAbility(new DiesTriggeredAbility(new CreateTokenEffect(new FoodToken())));
+        this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new FoodToken())));
     }
 
     private WolfsQuarryToken(final WolfsQuarryToken token) {

From 368faa37dd7718351c9e77bbbaefde54419b83d3 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Mon, 29 Jun 2020 18:02:18 +0200
Subject: [PATCH 559/586] * Added test for Dream Leash.

---
 .../cards/targets/TargetRestrictionsTest.java | 89 +++++++++++++++++++
 .../mage/abilities/keyword/EmergeAbility.java |  2 +-
 2 files changed, 90 insertions(+), 1 deletion(-)
 create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/targets/TargetRestrictionsTest.java

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/targets/TargetRestrictionsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/targets/TargetRestrictionsTest.java
new file mode 100644
index 0000000000..e7bbd5b477
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/targets/TargetRestrictionsTest.java
@@ -0,0 +1,89 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.mage.test.cards.targets;
+
+import mage.constants.PhaseStep;
+import mage.constants.Zone;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestPlayerBase;
+
+/**
+ *
+ * @author LevelX2
+ */
+
+public class TargetRestrictionsTest extends CardTestPlayerBase {
+
+    @Test
+    public void testDreamLeashWorks() {
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 6);
+        // Enchant permanent
+        // You can't choose an untapped permanent as Dream Leash's target as you cast Dream Leash.
+        // You control enchanted permanent.        
+        addCard(Zone.HAND, playerA, "Dream Leash"); // Enchantment {3}{U}{U}
+        
+        // Tap target creature. It doesn't untap during its controller's next untap step.
+        addCard(Zone.HAND, playerA, "Take into Custody"); // Instant {U}
+        
+        addCard(Zone.BATTLEFIELD, playerB, "Sejiri Merfolk");
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Take into Custody", "Sejiri Merfolk");
+        
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dream Leash", "Sejiri Merfolk");
+
+        setStrictChooseMode(true);
+        setStopAt(3, PhaseStep.BEGIN_COMBAT);
+        execute();
+        
+        assertAllCommandsUsed();
+        
+        assertPermanentCount(playerA, "Dream Leash", 1);
+        assertPermanentCount(playerA, "Sejiri Merfolk", 1);
+        
+        assertLife(playerA, 20);
+        assertLife(playerB, 20);
+    }
+    
+    @Test
+    public void testDreamLeashUntappingAsResponseToCast() {
+        addCard(Zone.BATTLEFIELD, playerA, "Island", 6);
+        // Enchant permanent
+        // You can't choose an untapped permanent as Dream Leash's target as you cast Dream Leash.
+        // You control enchanted permanent.        
+        addCard(Zone.HAND, playerA, "Dream Leash"); // Enchantment {3}{U}{U}
+        
+        // Tap target creature. It doesn't untap during its controller's next untap step.
+        addCard(Zone.HAND, playerA, "Take into Custody"); // Instant {U}
+        
+        // Untap target creature. It gets +1/+3 until end of turn.
+        addCard(Zone.HAND, playerB, "Ornamental Courage"); // Instant {G}       
+
+        addCard(Zone.BATTLEFIELD, playerB, "Forest");
+        addCard(Zone.BATTLEFIELD, playerB, "Sejiri Merfolk");
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Take into Custody", "Sejiri Merfolk");
+        
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dream Leash", "Sejiri Merfolk");
+        
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Ornamental Courage", "Sejiri Merfolk", "Dream Leash");
+
+        setStrictChooseMode(true);
+        setStopAt(3, PhaseStep.BEGIN_COMBAT);
+        execute();
+        
+        assertAllCommandsUsed();
+        
+        
+        assertGraveyardCount(playerA, "Take into Custody", 1);
+        assertGraveyardCount(playerB, "Ornamental Courage", 1);
+        assertPermanentCount(playerA, "Dream Leash", 1);
+        assertPermanentCount(playerA, "Sejiri Merfolk", 1);
+        assertPowerToughness(playerA, "Sejiri Merfolk", 2, 1);
+        
+        assertLife(playerA, 20);
+        assertLife(playerB, 20);
+    }
+}
\ No newline at end of file
diff --git a/Mage/src/main/java/mage/abilities/keyword/EmergeAbility.java b/Mage/src/main/java/mage/abilities/keyword/EmergeAbility.java
index 8045bc39bb..0aa1eddbd6 100644
--- a/Mage/src/main/java/mage/abilities/keyword/EmergeAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/EmergeAbility.java
@@ -29,7 +29,7 @@ public class EmergeAbility extends SpellAbility {
     public EmergeAbility(Card card, ManaCosts<ManaCost> emergeCost) {
         super(card.getSpellAbility());
         this.emergeCost = emergeCost.copy();
-        this.newId(); // Why is this neccessary? will create new id anyway or not?
+        this.newId(); // Set newId because cards spell ability is copied and needs own id
         this.setCardName(card.getName() + " with emerge");
         zone = Zone.HAND;
         spellAbilityType = SpellAbilityType.BASE_ALTERNATE;

From fd3ee45a1ecd5a5e74fede4aba28b79c0a3887d6 Mon Sep 17 00:00:00 2001
From: arcox <10953229+arcox@users.noreply.github.com>
Date: Mon, 29 Jun 2020 16:37:32 +0000
Subject: [PATCH 560/586] Implement Sawtooth Ogre from WTH (#6725)

---
 Mage.Sets/src/mage/cards/s/SawtoothOgre.java | 43 ++++++++++++++++++++
 Mage.Sets/src/mage/sets/Weatherlight.java    |  1 +
 2 files changed, 44 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/s/SawtoothOgre.java

diff --git a/Mage.Sets/src/mage/cards/s/SawtoothOgre.java b/Mage.Sets/src/mage/cards/s/SawtoothOgre.java
new file mode 100644
index 0000000000..a07b337cf9
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/SawtoothOgre.java
@@ -0,0 +1,43 @@
+package mage.cards.s;
+
+import java.util.UUID;
+
+import mage.MageInt;
+import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility;
+import mage.abilities.effects.Effect;
+import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
+import mage.abilities.effects.common.DamageTargetEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+
+/**
+ * @author arcox
+ */
+public final class SawtoothOgre extends CardImpl {
+
+    public SawtoothOgre(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{R}");
+        this.subtype.add(SubType.OGRE);
+
+        this.power = new MageInt(3);
+        this.toughness = new MageInt(3);
+
+        // Whenever Sawtooth Ogre blocks or becomes blocked by a creature, Sawtooth Ogre deals 1 damage to that creature at end of combat.
+        Effect effect = new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfCombatDelayedTriggeredAbility(
+                new DamageTargetEffect(1)), true)
+                .setText("{this} deals 1 damage to that creature at end of combat");
+        this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(effect, false));
+    }
+
+    public SawtoothOgre(final SawtoothOgre card) {
+        super(card);
+    }
+
+    @Override
+    public SawtoothOgre copy() {
+        return new SawtoothOgre(this);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/Weatherlight.java b/Mage.Sets/src/mage/sets/Weatherlight.java
index 5112b953c6..4f13129d79 100644
--- a/Mage.Sets/src/mage/sets/Weatherlight.java
+++ b/Mage.Sets/src/mage/sets/Weatherlight.java
@@ -152,6 +152,7 @@ public final class Weatherlight extends ExpansionSet {
         cards.add(new SetCardInfo("Roc Hatchling", 113, Rarity.UNCOMMON, mage.cards.r.RocHatchling.class));
         cards.add(new SetCardInfo("Rogue Elephant", 139, Rarity.COMMON, mage.cards.r.RogueElephant.class));
         cards.add(new SetCardInfo("Sage Owl", 52, Rarity.COMMON, mage.cards.s.SageOwl.class));
+        cards.add(new SetCardInfo("Sawtooth Ogre", 114, Rarity.COMMON, mage.cards.s.SawtoothOgre.class));
         cards.add(new SetCardInfo("Scorched Ruins", 166, Rarity.RARE, mage.cards.s.ScorchedRuins.class));
         cards.add(new SetCardInfo("Serenity", 24, Rarity.RARE, mage.cards.s.Serenity.class));
         cards.add(new SetCardInfo("Serra's Blessing", 25, Rarity.UNCOMMON, mage.cards.s.SerrasBlessing.class));

From a4d659176e0d851ec95bb132a81587a21b82cdd0 Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Mon, 29 Jun 2020 11:45:26 -0500
Subject: [PATCH 561/586] - Refactored BlocksOrBecomesBlockedTriggeredAbility
 to BlocksOrBecomesBlockedSourceTriggeredAbility

---
 Mage.Sets/src/mage/cards/a/Abomination.java   |  4 +-
 .../src/mage/cards/a/AislingLeprechaun.java   |  4 +-
 Mage.Sets/src/mage/cards/a/AshmouthHound.java |  4 +-
 .../src/mage/cards/a/AssembledAlphas.java     |  4 +-
 Mage.Sets/src/mage/cards/b/Brushwagg.java     |  4 +-
 Mage.Sets/src/mage/cards/c/ChubToad.java      |  4 +-
 Mage.Sets/src/mage/cards/c/Cockatrice.java    |  4 +-
 Mage.Sets/src/mage/cards/c/CorrosiveOoze.java |  4 +-
 Mage.Sets/src/mage/cards/d/Deathgazer.java    |  4 +-
 Mage.Sets/src/mage/cards/d/DreadSpecter.java  |  4 +-
 Mage.Sets/src/mage/cards/d/Dromosaur.java     |  4 +-
 .../src/mage/cards/e/EngulfingSlagwurm.java   |  4 +-
 Mage.Sets/src/mage/cards/e/EscapedNull.java   |  4 +-
 Mage.Sets/src/mage/cards/f/FlailingDrake.java |  4 +-
 .../src/mage/cards/f/FlameheartWerewolf.java  |  4 +-
 Mage.Sets/src/mage/cards/g/GhostHounds.java   |  4 +-
 Mage.Sets/src/mage/cards/g/GiantShark.java    |  4 +-
 .../src/mage/cards/g/GiftOfTheWoods.java      |  4 +-
 Mage.Sets/src/mage/cards/g/GoblinCadets.java  |  4 +-
 .../src/mage/cards/g/GoblinEliteInfantry.java |  4 +-
 .../src/mage/cards/g/GoblinFlotilla.java      |  4 +-
 Mage.Sets/src/mage/cards/g/GorgonRecluse.java |  4 +-
 .../src/mage/cards/i/InfernoElemental.java    |  4 +-
 .../src/mage/cards/k/KarnSilverGolem.java     |  4 +-
 .../src/mage/cards/k/KessigForgemaster.java   |  4 +-
 Mage.Sets/src/mage/cards/l/LimDulsCohort.java |  4 +-
 .../src/mage/cards/m/MammothHarness.java      |  4 +-
 Mage.Sets/src/mage/cards/m/MirrorShield.java  |  4 +-
 Mage.Sets/src/mage/cards/o/OrneryGoblin.java  |  4 +-
 Mage.Sets/src/mage/cards/r/RagingGorilla.java |  4 +-
 Mage.Sets/src/mage/cards/r/RockBasilisk.java  |  4 +-
 Mage.Sets/src/mage/cards/s/SawtoothOgre.java  |  4 +-
 Mage.Sets/src/mage/cards/s/ShapeStealer.java  |  4 +-
 Mage.Sets/src/mage/cards/s/SlinkingGiant.java |  4 +-
 Mage.Sets/src/mage/cards/s/SpittingSlug.java  |  4 +-
 .../src/mage/cards/t/TalruumChampion.java     |  4 +-
 Mage.Sets/src/mage/cards/t/TangleAsp.java     |  4 +-
 .../src/mage/cards/t/ThicketBasilisk.java     |  4 +-
 .../src/mage/cards/t/TreefolkMystic.java      |  4 +-
 .../src/mage/cards/v/VenomousDragonfly.java   |  4 +-
 .../src/mage/cards/w/WitherscaleWurm.java     |  4 +-
 ...rBecomesBlockedSourceTriggeredAbility.java | 91 +++++++++++++++++++
 42 files changed, 173 insertions(+), 82 deletions(-)
 create mode 100644 Mage/src/main/java/mage/abilities/common/BlocksOrBecomesBlockedSourceTriggeredAbility.java

diff --git a/Mage.Sets/src/mage/cards/a/Abomination.java b/Mage.Sets/src/mage/cards/a/Abomination.java
index 167f58ad8e..e9b37fb336 100644
--- a/Mage.Sets/src/mage/cards/a/Abomination.java
+++ b/Mage.Sets/src/mage/cards/a/Abomination.java
@@ -4,7 +4,7 @@ package mage.cards.a;
 import java.util.UUID;
 import mage.MageInt;
 import mage.ObjectColor;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
@@ -39,7 +39,7 @@ public final class Abomination extends CardImpl {
         // Whenever Abomination blocks or becomes blocked by a green or white creature, destroy that creature at end of combat.
         Effect effect = new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfCombatDelayedTriggeredAbility(new DestroyTargetEffect()), true);
         effect.setText("destroy that creature at end of combat");
-        this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(effect, filter, false));
+        this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, filter, false));
     }
 
     public Abomination(final Abomination card) {
diff --git a/Mage.Sets/src/mage/cards/a/AislingLeprechaun.java b/Mage.Sets/src/mage/cards/a/AislingLeprechaun.java
index 94dc800975..b1bee8438d 100644
--- a/Mage.Sets/src/mage/cards/a/AislingLeprechaun.java
+++ b/Mage.Sets/src/mage/cards/a/AislingLeprechaun.java
@@ -5,7 +5,7 @@ import java.util.UUID;
 import mage.MageInt;
 import mage.ObjectColor;
 import mage.abilities.Ability;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.continuous.BecomesColorTargetEffect;
 import mage.cards.CardImpl;
@@ -29,7 +29,7 @@ public final class AislingLeprechaun extends CardImpl {
         // Whenever Aisling Leprechaun blocks or becomes blocked by a creature, that creature becomes green.
         Effect effect = new BecomesColorTargetEffect(ObjectColor.GREEN, Duration.EndOfGame);
         effect.setText("that creature becomes green");
-        Ability ability = new BlocksOrBecomesBlockedTriggeredAbility(effect, false);
+        Ability ability = new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, false);
         this.addAbility(ability);
     }
 
diff --git a/Mage.Sets/src/mage/cards/a/AshmouthHound.java b/Mage.Sets/src/mage/cards/a/AshmouthHound.java
index dda0221e81..e3e5ed4fb4 100644
--- a/Mage.Sets/src/mage/cards/a/AshmouthHound.java
+++ b/Mage.Sets/src/mage/cards/a/AshmouthHound.java
@@ -3,7 +3,7 @@ package mage.cards.a;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.common.DamageTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -25,7 +25,7 @@ public final class AshmouthHound extends CardImpl {
         this.toughness = new MageInt(1);
 
         // Whenever Ashmouth Hound blocks or becomes blocked by a creature, Ashmouth Hound deals 1 damage to that creature.
-        this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(new DamageTargetEffect(1, true, "that creature"), false));
+        this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(new DamageTargetEffect(1, true, "that creature"), false));
     }
 
     public AshmouthHound(final AshmouthHound card) {
diff --git a/Mage.Sets/src/mage/cards/a/AssembledAlphas.java b/Mage.Sets/src/mage/cards/a/AssembledAlphas.java
index 95a23a8ece..03807132bb 100644
--- a/Mage.Sets/src/mage/cards/a/AssembledAlphas.java
+++ b/Mage.Sets/src/mage/cards/a/AssembledAlphas.java
@@ -4,7 +4,7 @@ package mage.cards.a;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.DamageTargetControllerEffect;
 import mage.abilities.effects.common.DamageTargetEffect;
@@ -27,7 +27,7 @@ public final class AssembledAlphas extends CardImpl {
         this.toughness = new MageInt(5);
 
         // Whenever Assembled Alphas blocks or becomes blocked by a creature, Assembled Alphas deals 3 damage to that creature and 3 damage to that creature's controller.
-        Ability ability = new BlocksOrBecomesBlockedTriggeredAbility(
+        Ability ability = new BlocksOrBecomesBlockedSourceTriggeredAbility(
                 new DamageTargetEffect(3, true, "that creature"), StaticFilters.FILTER_PERMANENT_CREATURE, false, null, true);
 
         Effect effect = new DamageTargetControllerEffect(3);
diff --git a/Mage.Sets/src/mage/cards/b/Brushwagg.java b/Mage.Sets/src/mage/cards/b/Brushwagg.java
index 147d23b284..d6c6e568b6 100644
--- a/Mage.Sets/src/mage/cards/b/Brushwagg.java
+++ b/Mage.Sets/src/mage/cards/b/Brushwagg.java
@@ -3,7 +3,7 @@ package mage.cards.b;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.common.continuous.BoostSourceEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -24,7 +24,7 @@ public final class Brushwagg extends CardImpl {
         this.toughness = new MageInt(2);
 
         // Whenever Brushwagg blocks or becomes blocked, it gets -2/+2 until end of turn.
-        this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(new BoostSourceEffect(-2, 2, Duration.EndOfTurn), false));
+        this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(new BoostSourceEffect(-2, 2, Duration.EndOfTurn), false));
     }
 
     public Brushwagg(final Brushwagg card) {
diff --git a/Mage.Sets/src/mage/cards/c/ChubToad.java b/Mage.Sets/src/mage/cards/c/ChubToad.java
index 8cc95ab155..ab57c1310b 100644
--- a/Mage.Sets/src/mage/cards/c/ChubToad.java
+++ b/Mage.Sets/src/mage/cards/c/ChubToad.java
@@ -4,7 +4,7 @@ package mage.cards.c;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.continuous.BoostSourceEffect;
 import mage.cards.CardImpl;
@@ -28,7 +28,7 @@ public final class ChubToad extends CardImpl {
         // Whenever Chub Toad blocks or becomes blocked, it gets +2/+2 until end of turn.
         Effect effect = new BoostSourceEffect(+2, +2, Duration.EndOfTurn);
         effect.setText("it gets +2/+2 until end of turn");
-        Ability ability = new BlocksOrBecomesBlockedTriggeredAbility(effect, false);
+        Ability ability = new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, false);
         this.addAbility(ability);
     }
 
diff --git a/Mage.Sets/src/mage/cards/c/Cockatrice.java b/Mage.Sets/src/mage/cards/c/Cockatrice.java
index cd45ae14a3..d8cf0bb72d 100644
--- a/Mage.Sets/src/mage/cards/c/Cockatrice.java
+++ b/Mage.Sets/src/mage/cards/c/Cockatrice.java
@@ -3,7 +3,7 @@ package mage.cards.c;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
@@ -41,7 +41,7 @@ public final class Cockatrice extends CardImpl {
         // Whenever Cockatrice blocks or becomes blocked by a non-Wall creature, destroy that creature at end of combat.
         Effect effect = new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfCombatDelayedTriggeredAbility(new DestroyTargetEffect()), true);
         effect.setText("destroy that creature at end of combat");
-        this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(effect, filter, false));
+        this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, filter, false));
     }
 
     public Cockatrice(final Cockatrice card) {
diff --git a/Mage.Sets/src/mage/cards/c/CorrosiveOoze.java b/Mage.Sets/src/mage/cards/c/CorrosiveOoze.java
index 543b50c756..eaae709b7c 100644
--- a/Mage.Sets/src/mage/cards/c/CorrosiveOoze.java
+++ b/Mage.Sets/src/mage/cards/c/CorrosiveOoze.java
@@ -3,7 +3,7 @@ package mage.cards.c;
 import mage.MageInt;
 import mage.MageObjectReference;
 import mage.abilities.Ability;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.OneShotEffect;
@@ -44,7 +44,7 @@ public final class CorrosiveOoze extends CardImpl {
 
         // Whenever Corrosive Ooze blocks or becomes blocked by an equipped creature, destroy all Equipment attached to that creature at end of combat.
         Effect effect = new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfCombatDelayedTriggeredAbility(new CorrosiveOozeEffect()), true);
-        this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(effect, filter, false), new CorrosiveOozeCombatWatcher());
+        this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, filter, false), new CorrosiveOozeCombatWatcher());
     }
 
     public CorrosiveOoze(final CorrosiveOoze card) {
diff --git a/Mage.Sets/src/mage/cards/d/Deathgazer.java b/Mage.Sets/src/mage/cards/d/Deathgazer.java
index ac8463fba9..4ff33fe235 100644
--- a/Mage.Sets/src/mage/cards/d/Deathgazer.java
+++ b/Mage.Sets/src/mage/cards/d/Deathgazer.java
@@ -4,7 +4,7 @@ package mage.cards.d;
 import java.util.UUID;
 import mage.MageInt;
 import mage.ObjectColor;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
@@ -39,7 +39,7 @@ public final class Deathgazer extends CardImpl {
         // Whenever Deathgazer blocks or becomes blocked by a nonblack creature, destroy that creature at end of combat.
         Effect effect = new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfCombatDelayedTriggeredAbility(new DestroyTargetEffect()), true);
         effect.setText("destroy that creature at end of combat");
-        this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(effect, filter, false));
+        this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, filter, false));
 
     }
 
diff --git a/Mage.Sets/src/mage/cards/d/DreadSpecter.java b/Mage.Sets/src/mage/cards/d/DreadSpecter.java
index c3cb1d8e77..50e5aca675 100644
--- a/Mage.Sets/src/mage/cards/d/DreadSpecter.java
+++ b/Mage.Sets/src/mage/cards/d/DreadSpecter.java
@@ -4,7 +4,7 @@ package mage.cards.d;
 import java.util.UUID;
 import mage.MageInt;
 import mage.ObjectColor;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
@@ -38,7 +38,7 @@ public final class DreadSpecter extends CardImpl {
         // Whenever Dread Specter blocks or becomes blocked by a nonblack creature, destroy that creature at end of combat.
         Effect effect = new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfCombatDelayedTriggeredAbility(new DestroyTargetEffect()), true);
         effect.setText("destroy that creature at end of combat");
-        this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(effect, filter, false));
+        this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, filter, false));
     }
 
     public DreadSpecter(final DreadSpecter card) {
diff --git a/Mage.Sets/src/mage/cards/d/Dromosaur.java b/Mage.Sets/src/mage/cards/d/Dromosaur.java
index edc3d67690..99cac6e8ce 100644
--- a/Mage.Sets/src/mage/cards/d/Dromosaur.java
+++ b/Mage.Sets/src/mage/cards/d/Dromosaur.java
@@ -3,7 +3,7 @@ package mage.cards.d;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.common.continuous.BoostSourceEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -25,7 +25,7 @@ public final class Dromosaur extends CardImpl {
         this.toughness = new MageInt(3);
 
         // Whenever Dromosaur blocks or becomes blocked by a creature, it gets +2/-2 until end of turn.
-        this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(new BoostSourceEffect(2, -2, Duration.EndOfTurn), false));
+        this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(new BoostSourceEffect(2, -2, Duration.EndOfTurn), false));
     }
 
     public Dromosaur(final Dromosaur card) {
diff --git a/Mage.Sets/src/mage/cards/e/EngulfingSlagwurm.java b/Mage.Sets/src/mage/cards/e/EngulfingSlagwurm.java
index 7a9919f7c4..2ff0a9b403 100644
--- a/Mage.Sets/src/mage/cards/e/EngulfingSlagwurm.java
+++ b/Mage.Sets/src/mage/cards/e/EngulfingSlagwurm.java
@@ -4,7 +4,7 @@ package mage.cards.e;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.DestroyTargetEffect;
 import mage.cards.CardImpl;
@@ -30,7 +30,7 @@ public final class EngulfingSlagwurm extends CardImpl {
         this.toughness = new MageInt(7);
 
         // Whenever Engulfing Slagwurm blocks or becomes blocked by a creature, destroy that creature. You gain life equal to that creature's toughness.
-        Ability ability = new BlocksOrBecomesBlockedTriggeredAbility(new DestroyTargetEffect(), false);
+        Ability ability = new BlocksOrBecomesBlockedSourceTriggeredAbility(new DestroyTargetEffect(), false);
         ability.addEffect(new EngulfingSlagwurmEffect());
         this.addAbility(ability);
     }
diff --git a/Mage.Sets/src/mage/cards/e/EscapedNull.java b/Mage.Sets/src/mage/cards/e/EscapedNull.java
index fdfcb2db58..2a72bd2b0f 100644
--- a/Mage.Sets/src/mage/cards/e/EscapedNull.java
+++ b/Mage.Sets/src/mage/cards/e/EscapedNull.java
@@ -3,7 +3,7 @@ package mage.cards.e;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.common.continuous.BoostSourceEffect;
 import mage.abilities.keyword.LifelinkAbility;
 import mage.cards.CardImpl;
@@ -27,7 +27,7 @@ public final class EscapedNull extends CardImpl {
         this.toughness = new MageInt(2);
 
         this.addAbility(LifelinkAbility.getInstance());
-        this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(new BoostSourceEffect(5, 0, Duration.EndOfTurn), false));
+        this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(new BoostSourceEffect(5, 0, Duration.EndOfTurn), false));
     }
 
     public EscapedNull(final EscapedNull card) {
diff --git a/Mage.Sets/src/mage/cards/f/FlailingDrake.java b/Mage.Sets/src/mage/cards/f/FlailingDrake.java
index 3083f1c918..db8dd6a855 100644
--- a/Mage.Sets/src/mage/cards/f/FlailingDrake.java
+++ b/Mage.Sets/src/mage/cards/f/FlailingDrake.java
@@ -4,7 +4,7 @@ package mage.cards.f;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.continuous.BoostTargetEffect;
 import mage.abilities.keyword.FlyingAbility;
@@ -32,7 +32,7 @@ public final class FlailingDrake extends CardImpl {
         // Whenever Flailing Drake blocks or becomes blocked by a creature, that creature gets +1/+1 until end of turn.
         Effect effect = new BoostTargetEffect(+1, +1, Duration.EndOfTurn);
         effect.setText("that creature gets +1/+1 until end of turn");
-        Ability ability = new BlocksOrBecomesBlockedTriggeredAbility(effect, new FilterCreaturePermanent("a creature"), false, null, true);
+        Ability ability = new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, new FilterCreaturePermanent("a creature"), false, null, true);
         this.addAbility(ability);
     }
 
diff --git a/Mage.Sets/src/mage/cards/f/FlameheartWerewolf.java b/Mage.Sets/src/mage/cards/f/FlameheartWerewolf.java
index 05ebb2bfe6..10fcf26864 100644
--- a/Mage.Sets/src/mage/cards/f/FlameheartWerewolf.java
+++ b/Mage.Sets/src/mage/cards/f/FlameheartWerewolf.java
@@ -5,7 +5,7 @@ import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.TriggeredAbility;
 import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition;
 import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
 import mage.abilities.effects.common.DamageTargetEffect;
@@ -36,7 +36,7 @@ public final class FlameheartWerewolf extends CardImpl {
         this.transformable = true;
 
         // Whenever Flameheart Werewolf blocks or becomes blocked by a creature, Flameheart Werewolf deals 2 damage to that creature.
-        this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(new DamageTargetEffect(2, true, "that creature"),
+        this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(new DamageTargetEffect(2, true, "that creature"),
                 StaticFilters.FILTER_PERMANENT_CREATURE, false, null, true));
 
         // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Flameheart Werewolf.
diff --git a/Mage.Sets/src/mage/cards/g/GhostHounds.java b/Mage.Sets/src/mage/cards/g/GhostHounds.java
index cac1255c0a..6ec9ac4172 100644
--- a/Mage.Sets/src/mage/cards/g/GhostHounds.java
+++ b/Mage.Sets/src/mage/cards/g/GhostHounds.java
@@ -4,7 +4,7 @@ package mage.cards.g;
 import java.util.UUID;
 import mage.MageInt;
 import mage.ObjectColor;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
 import mage.abilities.keyword.FirstStrikeAbility;
 import mage.abilities.keyword.VigilanceAbility;
@@ -40,7 +40,7 @@ public final class GhostHounds extends CardImpl {
         this.addAbility(VigilanceAbility.getInstance());
 
         // Whenever Ghost Hounds blocks or becomes blocked by a white creature, Ghost Hounds gains first strike until end of turn.
-        this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(new GainAbilitySourceEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn), filter, false));
+        this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(new GainAbilitySourceEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn), filter, false));
 
     }
 
diff --git a/Mage.Sets/src/mage/cards/g/GiantShark.java b/Mage.Sets/src/mage/cards/g/GiantShark.java
index 3adccfc3a9..9ebb586001 100644
--- a/Mage.Sets/src/mage/cards/g/GiantShark.java
+++ b/Mage.Sets/src/mage/cards/g/GiantShark.java
@@ -2,7 +2,7 @@ package mage.cards.g;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.common.ControlsPermanentsControllerTriggeredAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.common.SacrificeSourceEffect;
@@ -43,7 +43,7 @@ public final class GiantShark extends CardImpl {
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackUnlessDefenderControllsPermanent(filter)));
 
         // Whenever Giant Shark blocks or becomes blocked by a creature that has been dealt damage this turn, Giant Shark gets +2/+0 and gains trample until end of turn.
-        Ability ability = new BlocksOrBecomesBlockedTriggeredAbility(new BoostSourceEffect(2, 0, Duration.EndOfTurn).setText("{this} gets +2/+0"), filter2, false);
+        Ability ability = new BlocksOrBecomesBlockedSourceTriggeredAbility(new BoostSourceEffect(2, 0, Duration.EndOfTurn).setText("{this} gets +2/+0"), filter2, false);
         ability.addEffect(new GainAbilitySourceEffect(TrampleAbility.getInstance(), Duration.EndOfTurn).setText("and gains trample until end of turn"));
         this.addAbility(ability);
 
diff --git a/Mage.Sets/src/mage/cards/g/GiftOfTheWoods.java b/Mage.Sets/src/mage/cards/g/GiftOfTheWoods.java
index 185f1a9eb7..1d88c170a7 100644
--- a/Mage.Sets/src/mage/cards/g/GiftOfTheWoods.java
+++ b/Mage.Sets/src/mage/cards/g/GiftOfTheWoods.java
@@ -3,7 +3,7 @@ package mage.cards.g;
 
 import java.util.UUID;
 import mage.abilities.Ability;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.common.AttachEffect;
 import mage.abilities.effects.common.GainLifeEffect;
 import mage.abilities.effects.common.continuous.BoostEnchantedEffect;
@@ -37,7 +37,7 @@ public final class GiftOfTheWoods extends CardImpl {
 
         // Whenever enchanted creature blocks or becomes blocked, it gets +0/+3 until
         // end of turn and you gain 1 life.
-        Ability ability2 = new BlocksOrBecomesBlockedTriggeredAbility(
+        Ability ability2 = new BlocksOrBecomesBlockedSourceTriggeredAbility(
                 new BoostEnchantedEffect(0, 3, Duration.EndOfTurn), false);
         ability2.addEffect(new GainLifeEffect(1).concatBy("and"));
         this.addAbility(ability2);
diff --git a/Mage.Sets/src/mage/cards/g/GoblinCadets.java b/Mage.Sets/src/mage/cards/g/GoblinCadets.java
index dff65dab69..318f94c7f2 100644
--- a/Mage.Sets/src/mage/cards/g/GoblinCadets.java
+++ b/Mage.Sets/src/mage/cards/g/GoblinCadets.java
@@ -4,7 +4,7 @@ package mage.cards.g;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.ContinuousEffectImpl;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -27,7 +27,7 @@ public final class GoblinCadets extends CardImpl {
         this.toughness = new MageInt(1);
 
         // Whenever Goblin Cadets blocks or becomes blocked, target opponent gains control of it.
-        Ability ability = new BlocksOrBecomesBlockedTriggeredAbility(new GoblinCadetsChangeControlEffect(), false);
+        Ability ability = new BlocksOrBecomesBlockedSourceTriggeredAbility(new GoblinCadetsChangeControlEffect(), false);
         ability.addTarget(new TargetOpponent());
         this.addAbility(ability);
 
diff --git a/Mage.Sets/src/mage/cards/g/GoblinEliteInfantry.java b/Mage.Sets/src/mage/cards/g/GoblinEliteInfantry.java
index 4207a963e5..bc9233e335 100644
--- a/Mage.Sets/src/mage/cards/g/GoblinEliteInfantry.java
+++ b/Mage.Sets/src/mage/cards/g/GoblinEliteInfantry.java
@@ -3,7 +3,7 @@ package mage.cards.g;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.common.continuous.BoostSourceEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -24,7 +24,7 @@ public final class GoblinEliteInfantry extends CardImpl {
 
         this.power = new MageInt(2);
         this.toughness = new MageInt(2);
-        this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(new BoostSourceEffect(-1, -1, Duration.EndOfTurn), false));
+        this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(new BoostSourceEffect(-1, -1, Duration.EndOfTurn), false));
     }
 
     public GoblinEliteInfantry(final GoblinEliteInfantry card) {
diff --git a/Mage.Sets/src/mage/cards/g/GoblinFlotilla.java b/Mage.Sets/src/mage/cards/g/GoblinFlotilla.java
index 2abda79241..dd281d5386 100644
--- a/Mage.Sets/src/mage/cards/g/GoblinFlotilla.java
+++ b/Mage.Sets/src/mage/cards/g/GoblinFlotilla.java
@@ -3,7 +3,7 @@ package mage.cards.g;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.common.BeginningOfCombatTriggeredAbility;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.DoUnlessControllerPaysEffect;
@@ -37,7 +37,7 @@ public final class GoblinFlotilla extends CardImpl {
         // At the beginning of each combat, unless you pay {R}, whenever Goblin Flotilla blocks or becomes blocked by a creature this combat, that creature gains first strike until end of turn.
         Effect effect = new DoUnlessControllerPaysEffect(
                 new GainAbilitySourceEffect(
-                        new BlocksOrBecomesBlockedTriggeredAbility(
+                        new BlocksOrBecomesBlockedSourceTriggeredAbility(
                                 new GainAbilityTargetEffect(FirstStrikeAbility.getInstance(),
                                         Duration.EndOfTurn,
                                         "Blocks or Blocked by Goblin Flotilla"),
diff --git a/Mage.Sets/src/mage/cards/g/GorgonRecluse.java b/Mage.Sets/src/mage/cards/g/GorgonRecluse.java
index f9730324bd..1fa3627c58 100644
--- a/Mage.Sets/src/mage/cards/g/GorgonRecluse.java
+++ b/Mage.Sets/src/mage/cards/g/GorgonRecluse.java
@@ -4,7 +4,7 @@ package mage.cards.g;
 import java.util.UUID;
 import mage.MageInt;
 import mage.ObjectColor;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.Effect;
@@ -40,7 +40,7 @@ public final class GorgonRecluse extends CardImpl {
         // Whenever Gorgon Recluse blocks or becomes blocked by a nonblack creature, destroy that creature at end of combat.
         Effect effect = new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfCombatDelayedTriggeredAbility(new DestroyTargetEffect()), true);
         effect.setText("destroy that creature at end of combat");
-        this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(effect, filter, false));
+        this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, filter, false));
 
         // Madness {B}{B}
         this.addAbility(new MadnessAbility(this, new ManaCostsImpl("{B}{B}")));
diff --git a/Mage.Sets/src/mage/cards/i/InfernoElemental.java b/Mage.Sets/src/mage/cards/i/InfernoElemental.java
index 6815e8a26a..1e3b74c9a4 100644
--- a/Mage.Sets/src/mage/cards/i/InfernoElemental.java
+++ b/Mage.Sets/src/mage/cards/i/InfernoElemental.java
@@ -3,7 +3,7 @@ package mage.cards.i;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.common.DamageTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -24,7 +24,7 @@ public final class InfernoElemental extends CardImpl {
         this.toughness = new MageInt(4);
 
         // Whenever Inferno Elemental blocks or becomes blocked by a creature, Inferno Elemental deals 3 damage to that creature.
-        this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(new DamageTargetEffect(3, true, "that creature"), false));
+        this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(new DamageTargetEffect(3, true, "that creature"), false));
     }
 
     public InfernoElemental(final InfernoElemental card) {
diff --git a/Mage.Sets/src/mage/cards/k/KarnSilverGolem.java b/Mage.Sets/src/mage/cards/k/KarnSilverGolem.java
index 57e69f971b..141991d4e4 100644
--- a/Mage.Sets/src/mage/cards/k/KarnSilverGolem.java
+++ b/Mage.Sets/src/mage/cards/k/KarnSilverGolem.java
@@ -4,7 +4,7 @@ package mage.cards.k;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.ContinuousEffectImpl;
@@ -38,7 +38,7 @@ public final class KarnSilverGolem extends CardImpl {
         this.toughness = new MageInt(4);
 
         // Whenever Karn, Silver Golem blocks or becomes blocked, it gets -4/+4 until end of turn.
-        this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(new BoostSourceEffect(-4, +4, Duration.EndOfTurn), false));
+        this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(new BoostSourceEffect(-4, +4, Duration.EndOfTurn), false));
         
         // {1}: Target noncreature artifact becomes an artifact creature with power and toughness each equal to its converted mana cost until end of turn.
         Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new KarnSilverGolemEffect(), new ManaCostsImpl("{1"));
diff --git a/Mage.Sets/src/mage/cards/k/KessigForgemaster.java b/Mage.Sets/src/mage/cards/k/KessigForgemaster.java
index c8ffa6637f..13f65fe901 100644
--- a/Mage.Sets/src/mage/cards/k/KessigForgemaster.java
+++ b/Mage.Sets/src/mage/cards/k/KessigForgemaster.java
@@ -6,7 +6,7 @@ import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.TriggeredAbility;
 import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.condition.common.NoSpellsWereCastLastTurnCondition;
 import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
 import mage.abilities.effects.common.DamageTargetEffect;
@@ -36,7 +36,7 @@ public final class KessigForgemaster extends CardImpl {
         this.secondSideCardClazz = mage.cards.f.FlameheartWerewolf.class;
 
         // Whenever Kessig Forgemaster blocks or becomes blocked by a creature, Kessig Forgemaster deals 1 damage to that creature.
-        this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(new DamageTargetEffect(1, true, "that creature"),
+        this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(new DamageTargetEffect(1, true, "that creature"),
                 StaticFilters.FILTER_PERMANENT_CREATURE, false, null, true));
 
         // At the beginning of each upkeep, if no spells were cast last turn, transform Kessig Forgemaster.
diff --git a/Mage.Sets/src/mage/cards/l/LimDulsCohort.java b/Mage.Sets/src/mage/cards/l/LimDulsCohort.java
index fee4085bbd..af21382902 100644
--- a/Mage.Sets/src/mage/cards/l/LimDulsCohort.java
+++ b/Mage.Sets/src/mage/cards/l/LimDulsCohort.java
@@ -2,7 +2,7 @@ package mage.cards.l;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.common.CantBeRegeneratedTargetEffect;
 import mage.constants.SubType;
 import mage.cards.CardImpl;
@@ -25,7 +25,7 @@ public final class LimDulsCohort extends CardImpl {
         this.toughness = new MageInt(3);
 
         // Whenever Lim-Dûl's Cohort blocks or becomes blocked by a creature, that creature can't be regenerated this turn.
-        this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(
+        this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(
                 new CantBeRegeneratedTargetEffect(Duration.EndOfTurn),
                 new FilterCreaturePermanent(),
                 false,
diff --git a/Mage.Sets/src/mage/cards/m/MammothHarness.java b/Mage.Sets/src/mage/cards/m/MammothHarness.java
index aa126f999d..0df36ee9cf 100644
--- a/Mage.Sets/src/mage/cards/m/MammothHarness.java
+++ b/Mage.Sets/src/mage/cards/m/MammothHarness.java
@@ -3,7 +3,7 @@ package mage.cards.m;
 
 import java.util.UUID;
 import mage.abilities.Ability;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.common.AttachEffect;
 import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
@@ -61,7 +61,7 @@ public final class MammothHarness extends CardImpl {
     }
 }
 
-class MammothHarnessTriggeredAbility extends BlocksOrBecomesBlockedTriggeredAbility {
+class MammothHarnessTriggeredAbility extends BlocksOrBecomesBlockedSourceTriggeredAbility {
 
     public MammothHarnessTriggeredAbility() {
         super(new GainAbilityTargetEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn), StaticFilters.FILTER_PERMANENT_CREATURE, false, null, false);
diff --git a/Mage.Sets/src/mage/cards/m/MirrorShield.java b/Mage.Sets/src/mage/cards/m/MirrorShield.java
index e2d4db1cde..27d9368dc8 100644
--- a/Mage.Sets/src/mage/cards/m/MirrorShield.java
+++ b/Mage.Sets/src/mage/cards/m/MirrorShield.java
@@ -1,7 +1,7 @@
 package mage.cards.m;
 
 import mage.abilities.Ability;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.common.DestroyTargetEffect;
 import mage.abilities.effects.common.continuous.BoostEquippedEffect;
@@ -44,7 +44,7 @@ public final class MirrorShield extends CardImpl {
         ability.addEffect(new GainAbilityAttachedEffect(
                 HexproofAbility.getInstance(), AttachmentType.EQUIPMENT
         ).setText("and has hexproof"));
-        ability.addEffect(new GainAbilityAttachedEffect(new BlocksOrBecomesBlockedTriggeredAbility(
+        ability.addEffect(new GainAbilityAttachedEffect(new BlocksOrBecomesBlockedSourceTriggeredAbility(
                 new DestroyTargetEffect(), filter, false, rule + "", true
         ), AttachmentType.EQUIPMENT).setText("and \"" + rule + "\""));
         this.addAbility(ability);
diff --git a/Mage.Sets/src/mage/cards/o/OrneryGoblin.java b/Mage.Sets/src/mage/cards/o/OrneryGoblin.java
index 7e56c1249a..3f523c0f66 100644
--- a/Mage.Sets/src/mage/cards/o/OrneryGoblin.java
+++ b/Mage.Sets/src/mage/cards/o/OrneryGoblin.java
@@ -2,7 +2,7 @@ package mage.cards.o;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.common.DamageTargetEffect;
 import mage.constants.SubType;
 import mage.cards.CardImpl;
@@ -24,7 +24,7 @@ public final class OrneryGoblin extends CardImpl {
         this.toughness = new MageInt(1);
 
         // Whenever Ornery Goblin blocks or becomes blocked by a creature, Ornery Goblin deals 1 damage to that creature.
-        this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(
+        this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(
                 new DamageTargetEffect(1, true, "that creature"), false
         ));
     }
diff --git a/Mage.Sets/src/mage/cards/r/RagingGorilla.java b/Mage.Sets/src/mage/cards/r/RagingGorilla.java
index 8b11fd21f7..5c5df5d7f6 100644
--- a/Mage.Sets/src/mage/cards/r/RagingGorilla.java
+++ b/Mage.Sets/src/mage/cards/r/RagingGorilla.java
@@ -3,7 +3,7 @@ package mage.cards.r;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.common.continuous.BoostSourceEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -24,7 +24,7 @@ public final class RagingGorilla extends CardImpl {
         this.toughness = new MageInt(3);
 
         // Whenever Raging Gorilla blocks or becomes blocked, it gets +2/-2 until end of turn.
-        this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(new BoostSourceEffect(2, -2, Duration.EndOfTurn), false));
+        this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(new BoostSourceEffect(2, -2, Duration.EndOfTurn), false));
     }
 
     public RagingGorilla(final RagingGorilla card) {
diff --git a/Mage.Sets/src/mage/cards/r/RockBasilisk.java b/Mage.Sets/src/mage/cards/r/RockBasilisk.java
index 6fef87b86b..43581603bc 100644
--- a/Mage.Sets/src/mage/cards/r/RockBasilisk.java
+++ b/Mage.Sets/src/mage/cards/r/RockBasilisk.java
@@ -3,7 +3,7 @@ package mage.cards.r;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
@@ -38,7 +38,7 @@ public final class RockBasilisk extends CardImpl {
         Effect effect = new CreateDelayedTriggeredAbilityEffect(
                 new AtTheEndOfCombatDelayedTriggeredAbility(new DestroyTargetEffect()), true);
         effect.setText("destroy that creature at end of combat");
-        this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(effect, filter, false));
+        this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, filter, false));
     }
 
     public RockBasilisk(final RockBasilisk card) {
diff --git a/Mage.Sets/src/mage/cards/s/SawtoothOgre.java b/Mage.Sets/src/mage/cards/s/SawtoothOgre.java
index a07b337cf9..960fd59c7f 100644
--- a/Mage.Sets/src/mage/cards/s/SawtoothOgre.java
+++ b/Mage.Sets/src/mage/cards/s/SawtoothOgre.java
@@ -3,7 +3,7 @@ package mage.cards.s;
 import java.util.UUID;
 
 import mage.MageInt;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
@@ -29,7 +29,7 @@ public final class SawtoothOgre extends CardImpl {
         Effect effect = new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfCombatDelayedTriggeredAbility(
                 new DamageTargetEffect(1)), true)
                 .setText("{this} deals 1 damage to that creature at end of combat");
-        this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(effect, false));
+        this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, false));
     }
 
     public SawtoothOgre(final SawtoothOgre card) {
diff --git a/Mage.Sets/src/mage/cards/s/ShapeStealer.java b/Mage.Sets/src/mage/cards/s/ShapeStealer.java
index d2bc6f976b..d21b0e2c98 100644
--- a/Mage.Sets/src/mage/cards/s/ShapeStealer.java
+++ b/Mage.Sets/src/mage/cards/s/ShapeStealer.java
@@ -4,7 +4,7 @@ package mage.cards.s;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.ContinuousEffect;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.continuous.SetPowerToughnessSourceEffect;
@@ -37,7 +37,7 @@ public final class ShapeStealer extends CardImpl {
         // each one in succession. The first trigger put on the stack will be the last to resolve,
         // so that will set Shape Stealer's final power and toughness.
         // Whenever Shape Stealer blocks or becomes blocked by a creature, change Shape Stealer's base power and toughness to that creature's power and toughness until end of turn.
-        this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(new ShapeStealerEffect(), false));
+        this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(new ShapeStealerEffect(), false));
     }
 
     public ShapeStealer(final ShapeStealer card) {
diff --git a/Mage.Sets/src/mage/cards/s/SlinkingGiant.java b/Mage.Sets/src/mage/cards/s/SlinkingGiant.java
index 3d83f4a134..07a3d03ff2 100644
--- a/Mage.Sets/src/mage/cards/s/SlinkingGiant.java
+++ b/Mage.Sets/src/mage/cards/s/SlinkingGiant.java
@@ -3,7 +3,7 @@ package mage.cards.s;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.common.continuous.BoostSourceEffect;
 import mage.abilities.keyword.WitherAbility;
 import mage.cards.CardImpl;
@@ -28,7 +28,7 @@ public final class SlinkingGiant extends CardImpl {
 
         this.addAbility(WitherAbility.getInstance());
         // Whenever Slinking Giant blocks or becomes blocked, it gets -3/-0 until end of turn.
-        this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(new BoostSourceEffect(-3, 0, Duration.EndOfTurn), false));
+        this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(new BoostSourceEffect(-3, 0, Duration.EndOfTurn), false));
     }
 
     public SlinkingGiant(final SlinkingGiant card) {
diff --git a/Mage.Sets/src/mage/cards/s/SpittingSlug.java b/Mage.Sets/src/mage/cards/s/SpittingSlug.java
index 6907a53aab..4b52189692 100644
--- a/Mage.Sets/src/mage/cards/s/SpittingSlug.java
+++ b/Mage.Sets/src/mage/cards/s/SpittingSlug.java
@@ -4,7 +4,7 @@ package mage.cards.s;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.ContinuousEffect;
 import mage.abilities.effects.OneShotEffect;
@@ -39,7 +39,7 @@ public final class SpittingSlug extends CardImpl {
         this.toughness = new MageInt(4);
 
         // Whenever Spitting Slug blocks or becomes blocked, you may pay {1}{G}. If you do, Spitting Slug gains first strike until end of turn. Otherwise, each creature blocking or blocked by Spitting Slug gains first strike until end of turn.
-        this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(
+        this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(
             new DoIfCostPaid(new GainAbilitySourceEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn), 
                 new SpittingSlugEffect(), 
                 new ManaCostsImpl("{1}{G}")).setText("you may pay {1}{G}. If you do, {this} gains first strike until end of turn. Otherwise, each creature blocking or blocked by {this} gains first strike until end of turn"), 
diff --git a/Mage.Sets/src/mage/cards/t/TalruumChampion.java b/Mage.Sets/src/mage/cards/t/TalruumChampion.java
index abb97d8c05..96734c2cc1 100644
--- a/Mage.Sets/src/mage/cards/t/TalruumChampion.java
+++ b/Mage.Sets/src/mage/cards/t/TalruumChampion.java
@@ -3,7 +3,7 @@ package mage.cards.t;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.continuous.LoseAbilityTargetEffect;
 import mage.abilities.keyword.FirstStrikeAbility;
@@ -31,7 +31,7 @@ public final class TalruumChampion extends CardImpl {
         // Whenever Talruum Champion blocks or becomes blocked by a creature, that creature loses first strike until end of turn.
         Effect effect = new LoseAbilityTargetEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn);
         effect.setText("that creature loses first strike until end of turn");
-        this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(effect, false));
+        this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, false));
     }
 
     public TalruumChampion(final TalruumChampion card) {
diff --git a/Mage.Sets/src/mage/cards/t/TangleAsp.java b/Mage.Sets/src/mage/cards/t/TangleAsp.java
index ac1a74de78..a51b71dd67 100644
--- a/Mage.Sets/src/mage/cards/t/TangleAsp.java
+++ b/Mage.Sets/src/mage/cards/t/TangleAsp.java
@@ -3,7 +3,7 @@ package mage.cards.t;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
@@ -30,7 +30,7 @@ public final class TangleAsp extends CardImpl {
         // Whenever Tangle Asp blocks or becomes blocked by a creature, destroy that creature at end of combat.
         Effect effect = new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfCombatDelayedTriggeredAbility(new DestroyTargetEffect()), true);
         effect.setText("destroy that creature at end of combat");
-        this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(effect, false));
+        this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, false));
     }
 
     public TangleAsp(final TangleAsp card) {
diff --git a/Mage.Sets/src/mage/cards/t/ThicketBasilisk.java b/Mage.Sets/src/mage/cards/t/ThicketBasilisk.java
index ceb3f94855..989d765d6a 100644
--- a/Mage.Sets/src/mage/cards/t/ThicketBasilisk.java
+++ b/Mage.Sets/src/mage/cards/t/ThicketBasilisk.java
@@ -3,7 +3,7 @@ package mage.cards.t;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
@@ -37,7 +37,7 @@ public final class ThicketBasilisk extends CardImpl {
         // Whenever Thicket Basilisk blocks or becomes blocked by a non-Wall creature, destroy that creature at end of combat.
         Effect effect = new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfCombatDelayedTriggeredAbility(new DestroyTargetEffect()), true);
         effect.setText("destroy that creature at end of combat");
-        this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(effect, filter, false));
+        this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, filter, false));
     }
 
     public ThicketBasilisk(final ThicketBasilisk card) {
diff --git a/Mage.Sets/src/mage/cards/t/TreefolkMystic.java b/Mage.Sets/src/mage/cards/t/TreefolkMystic.java
index a7750637f7..e9a4807fc1 100644
--- a/Mage.Sets/src/mage/cards/t/TreefolkMystic.java
+++ b/Mage.Sets/src/mage/cards/t/TreefolkMystic.java
@@ -6,7 +6,7 @@ import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.Mode;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.effects.OneShotEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -30,7 +30,7 @@ public final class TreefolkMystic extends CardImpl {
         this.toughness = new MageInt(4);
 
         // Whenever Treefolk Mystic blocks or becomes blocked by a creature, destroy all Auras attached to that creature.
-        this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(new TreefolkMysticEffect(), false));
+        this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(new TreefolkMysticEffect(), false));
     }
 
     public TreefolkMystic(final TreefolkMystic card) {
diff --git a/Mage.Sets/src/mage/cards/v/VenomousDragonfly.java b/Mage.Sets/src/mage/cards/v/VenomousDragonfly.java
index 4815d4b859..7da8c928d2 100644
--- a/Mage.Sets/src/mage/cards/v/VenomousDragonfly.java
+++ b/Mage.Sets/src/mage/cards/v/VenomousDragonfly.java
@@ -3,7 +3,7 @@ package mage.cards.v;
 
 import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
@@ -32,7 +32,7 @@ public final class VenomousDragonfly extends CardImpl {
         // Whenever Venomous Dragonfly blocks or becomes blocked by a creature, destroy that creature at end of combat.
         Effect effect = new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfCombatDelayedTriggeredAbility(new DestroyTargetEffect()));
         effect.setText("destroy that creature at end of combat");
-        this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(effect, false));
+        this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, false));
     }
 
     public VenomousDragonfly(final VenomousDragonfly card) {
diff --git a/Mage.Sets/src/mage/cards/w/WitherscaleWurm.java b/Mage.Sets/src/mage/cards/w/WitherscaleWurm.java
index 6d8d46bc86..dab327b5f5 100644
--- a/Mage.Sets/src/mage/cards/w/WitherscaleWurm.java
+++ b/Mage.Sets/src/mage/cards/w/WitherscaleWurm.java
@@ -4,7 +4,7 @@ package mage.cards.w;
 import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
+import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
 import mage.abilities.common.DealsDamageToOpponentTriggeredAbility;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.RemoveAllCountersSourceEffect;
@@ -34,7 +34,7 @@ public final class WitherscaleWurm extends CardImpl {
         // Whenever Witherscale Wurm blocks or becomes blocked by a creature, that creature gains wither until end of turn.
         Effect effect = new GainAbilityTargetEffect(WitherAbility.getInstance(), Duration.EndOfTurn);
         effect.setText("that creature gains wither until end of turn");
-        Ability ability = new BlocksOrBecomesBlockedTriggeredAbility(effect, new FilterCreaturePermanent("a creature"), false, null, true);
+        Ability ability = new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, new FilterCreaturePermanent("a creature"), false, null, true);
         this.addAbility(ability);
 
         // Whenever Witherscale Wurm deals damage to an opponent, remove all -1/-1 counters from it.
diff --git a/Mage/src/main/java/mage/abilities/common/BlocksOrBecomesBlockedSourceTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BlocksOrBecomesBlockedSourceTriggeredAbility.java
new file mode 100644
index 0000000000..3863d19045
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/common/BlocksOrBecomesBlockedSourceTriggeredAbility.java
@@ -0,0 +1,91 @@
+
+package mage.abilities.common;
+
+import mage.abilities.TriggeredAbilityImpl;
+import mage.abilities.effects.Effect;
+import mage.constants.Zone;
+import mage.filter.FilterPermanent;
+import mage.filter.StaticFilters;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+import mage.game.permanent.Permanent;
+import mage.target.targetpointer.FixedTarget;
+
+/**
+ *
+ * @author North, Loki
+ */
+public class BlocksOrBecomesBlockedSourceTriggeredAbility extends TriggeredAbilityImpl {
+
+    protected FilterPermanent filter;
+    protected String rule;
+    protected boolean setTargetPointer;
+
+    public BlocksOrBecomesBlockedSourceTriggeredAbility(Effect effect, boolean optional) {
+        this(effect, StaticFilters.FILTER_PERMANENT_CREATURE, optional, null, true);
+    }
+
+    public BlocksOrBecomesBlockedSourceTriggeredAbility(Effect effect, FilterPermanent filter, boolean optional) {
+        this(effect, filter, optional, null, true);
+    }
+
+    public BlocksOrBecomesBlockedSourceTriggeredAbility(Effect effect, FilterPermanent filter, boolean optional, String rule) {
+        this(effect, filter, optional, rule, true);
+    }
+
+    public BlocksOrBecomesBlockedSourceTriggeredAbility(Effect effect, FilterPermanent filter, boolean optional, String rule, boolean setTargetPointer) {
+        super(Zone.BATTLEFIELD, effect, optional);
+        this.filter = filter;
+        this.rule = rule;
+        this.setTargetPointer = setTargetPointer;
+    }
+
+    public BlocksOrBecomesBlockedSourceTriggeredAbility(final BlocksOrBecomesBlockedSourceTriggeredAbility ability) {
+        super(ability);
+        this.filter = ability.filter;
+        this.rule = ability.rule;
+        this.setTargetPointer = ability.setTargetPointer;
+
+    }
+
+    @Override
+    public boolean checkEventType(GameEvent event, Game game) {
+        return event.getType() == GameEvent.EventType.BLOCKER_DECLARED;
+    }
+
+    @Override
+    public boolean checkTrigger(GameEvent event, Game game) {
+        if (event.getSourceId().equals(this.getSourceId())) {
+            Permanent blocked = game.getPermanent(event.getTargetId());
+            if (blocked != null && filter.match(blocked, game)) {
+                if (setTargetPointer) {
+                    this.getEffects().setTargetPointer(new FixedTarget(blocked, game));
+                }
+                return true;
+            }
+        }
+        if (event.getTargetId().equals(this.getSourceId())) {
+            Permanent blocker = game.getPermanent(event.getSourceId());
+            if (blocker != null && filter.match(blocker, game)) {
+                if (setTargetPointer) {
+                    this.getEffects().setTargetPointer(new FixedTarget(blocker, game));
+                }
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public String getRule() {
+        if (rule != null) {
+            return rule;
+        }
+        return "Whenever {this} blocks or becomes blocked" + (setTargetPointer ? " by a " + filter.getMessage() : "") + ", " + super.getRule();
+    }
+
+    @Override
+    public BlocksOrBecomesBlockedSourceTriggeredAbility copy() {
+        return new BlocksOrBecomesBlockedSourceTriggeredAbility(this);
+    }
+}

From fa5cf8b412287e188e20762052b46d3738c1f798 Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Mon, 29 Jun 2020 15:07:47 -0500
Subject: [PATCH 562/586] - Fixed #6751

---
 .../src/mage/cards/l/LiegeOfTheHollows.java   | 29 ++++++++++++-------
 1 file changed, 18 insertions(+), 11 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/l/LiegeOfTheHollows.java b/Mage.Sets/src/mage/cards/l/LiegeOfTheHollows.java
index 60e87dbe34..dc3267a00d 100644
--- a/Mage.Sets/src/mage/cards/l/LiegeOfTheHollows.java
+++ b/Mage.Sets/src/mage/cards/l/LiegeOfTheHollows.java
@@ -1,11 +1,11 @@
 package mage.cards.l;
 
+import java.util.HashMap;
+import java.util.Map;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.DiesSourceTriggeredAbility;
-import mage.abilities.effects.Effect;
 import mage.abilities.effects.OneShotEffect;
-import mage.abilities.effects.common.CreateTokenTargetEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
@@ -14,7 +14,6 @@ import mage.constants.SubType;
 import mage.game.Game;
 import mage.game.permanent.token.SquirrelToken;
 import mage.players.Player;
-import mage.target.targetpointer.FixedTarget;
 import mage.util.ManaUtil;
 
 import java.util.UUID;
@@ -32,7 +31,8 @@ public final class LiegeOfTheHollows extends CardImpl {
         this.toughness = new MageInt(4);
 
         // When Liege of the Hollows dies, each player may pay any amount of mana.
-        // Then each player creates a number of 1/1 green Squirrel creature tokens equal to the amount of mana they paid this way.
+        // Then each player creates a number of 1/1 green Squirrel creature tokens 
+        // equal to the amount of mana they paid this way.
         this.addAbility(new DiesSourceTriggeredAbility(new LiegeOfTheHollowsEffect()));
     }
 
@@ -66,19 +66,26 @@ class LiegeOfTheHollowsEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player controller = game.getPlayer(source.getControllerId());
+        Map<UUID, Integer> paidMana = new HashMap<>();
         if (controller != null) {
             for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
                 Player player = game.getPlayer(playerId);
                 if (player != null) {
-                    int numSquirrels = ManaUtil.playerPaysXGenericMana(false, "Liege of the Hollows", player, source, game);
-                    if (numSquirrels > 0) {
-                        Effect effect = new CreateTokenTargetEffect(new SquirrelToken(), numSquirrels);
-                        effect.setTargetPointer(new FixedTarget(playerId));
-                        effect.apply(game, source);
-                    }
+                    paidMana.put(player.getId(), ManaUtil.playerPaysXGenericMana(false, 
+                            "Liege of the Hollows", player, source, game));
                 }
             }
-
+            // create tokens
+            SquirrelToken token = new SquirrelToken();
+            for (Map.Entry<UUID, Integer> entry
+                    : paidMana.entrySet()) {
+                Player player = game.getPlayer(entry.getKey());
+                if (player != null) {
+                    token.putOntoBattlefield(entry.getValue(), game, source.getSourceId(), player.getId());
+                }
+            }
+            game.getState().processAction(game);
+            
             // prevent undo
             controller.resetStoredBookmark(game);
             return true;

From 2cbe607e7b62e36b91bbeb746126eea45d6a2320 Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Mon, 29 Jun 2020 15:41:37 -0500
Subject: [PATCH 563/586] - Fixed #6740

---
 .../src/mage/cards/t/TorbranThaneOfRedFell.java     | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/t/TorbranThaneOfRedFell.java b/Mage.Sets/src/mage/cards/t/TorbranThaneOfRedFell.java
index 659c6588b9..b4b838c227 100644
--- a/Mage.Sets/src/mage/cards/t/TorbranThaneOfRedFell.java
+++ b/Mage.Sets/src/mage/cards/t/TorbranThaneOfRedFell.java
@@ -30,7 +30,8 @@ public final class TorbranThaneOfRedFell extends CardImpl {
         this.power = new MageInt(2);
         this.toughness = new MageInt(4);
 
-        // If a red source you control would deal damage to an opponent or a permanent an opponent controls, it deals that much damage plus 2 instead.
+        // If a red source you control would deal damage to an opponent or a 
+        // permanent an opponent controls, it deals that much damage plus 2 instead.
         this.addAbility(new SimpleStaticAbility(new TorbranThaneOfRedFellEffect()));
     }
 
@@ -48,8 +49,8 @@ class TorbranThaneOfRedFellEffect extends ReplacementEffectImpl {
 
     TorbranThaneOfRedFellEffect() {
         super(Duration.WhileOnBattlefield, Outcome.Damage);
-        this.staticText = "If a red source you control would deal damage to an opponent " +
-                "or a permanent an opponent controls, it deals that much damage plus 2 instead.";
+        this.staticText = "If a red source you control would deal damage to an opponent "
+                + "or a permanent an opponent controls, it deals that much damage plus 2 instead.";
     }
 
     private TorbranThaneOfRedFellEffect(final TorbranThaneOfRedFellEffect effect) {
@@ -65,9 +66,9 @@ class TorbranThaneOfRedFellEffect extends ReplacementEffectImpl {
     @Override
     public boolean checksEventType(GameEvent event, Game game) {
         switch (event.getType()) {
-            case DAMAGE_CREATURE:
-            case DAMAGE_PLANESWALKER:
-            case DAMAGE_PLAYER:
+            case DAMAGED_CREATURE:
+            case DAMAGED_PLANESWALKER:
+            case DAMAGED_PLAYER:
                 return true;
             default:
                 return false;

From 0960794db450bff581af0a27da1d8f952b40b75e Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Mon, 29 Jun 2020 17:05:40 -0500
Subject: [PATCH 564/586] - Reverted previous "fix" for Torban, Thrane of Red
 Fell.

---
 Mage.Sets/src/mage/cards/t/TorbranThaneOfRedFell.java | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/t/TorbranThaneOfRedFell.java b/Mage.Sets/src/mage/cards/t/TorbranThaneOfRedFell.java
index b4b838c227..2e892e0752 100644
--- a/Mage.Sets/src/mage/cards/t/TorbranThaneOfRedFell.java
+++ b/Mage.Sets/src/mage/cards/t/TorbranThaneOfRedFell.java
@@ -66,9 +66,9 @@ class TorbranThaneOfRedFellEffect extends ReplacementEffectImpl {
     @Override
     public boolean checksEventType(GameEvent event, Game game) {
         switch (event.getType()) {
-            case DAMAGED_CREATURE:
-            case DAMAGED_PLANESWALKER:
-            case DAMAGED_PLAYER:
+            case DAMAGE_CREATURE:
+            case DAMAGE_PLANESWALKER:
+            case DAMAGE_PLAYER:
                 return true;
             default:
                 return false;

From e914c38986ba7d94aa228ab0b7d0c0e77bd51769 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 29 Jun 2020 20:05:38 -0400
Subject: [PATCH 565/586] Implemented Idol of Endurance

---
 .../src/mage/cards/i/IdolOfEndurance.java     | 265 ++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |   1 +
 .../cards/single/IdolOfEnduranceTest.java     | 125 +++++++++
 3 files changed, 391 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/i/IdolOfEndurance.java
 create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/single/IdolOfEnduranceTest.java

diff --git a/Mage.Sets/src/mage/cards/i/IdolOfEndurance.java b/Mage.Sets/src/mage/cards/i/IdolOfEndurance.java
new file mode 100644
index 0000000000..f57ff0ec57
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/i/IdolOfEndurance.java
@@ -0,0 +1,265 @@
+package mage.cards.i;
+
+import mage.MageObjectReference;
+import mage.abilities.Ability;
+import mage.abilities.DelayedTriggeredAbility;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.common.TapSourceCost;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.effects.AsThoughEffectImpl;
+import mage.abilities.effects.OneShotEffect;
+import mage.cards.*;
+import mage.constants.*;
+import mage.filter.FilterCard;
+import mage.filter.common.FilterCreatureCard;
+import mage.filter.predicate.mageobject.ConvertedManaCostPredicate;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+import mage.game.events.ZoneChangeEvent;
+import mage.game.permanent.Permanent;
+import mage.players.Player;
+import mage.watchers.Watcher;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * @author TheElk801
+ */
+public final class IdolOfEndurance extends CardImpl {
+
+    public IdolOfEndurance(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{W}");
+
+        // When Idol of Endurance enters the battlefield, exile all creature cards with converted mana cost 3 or less from your graveyard until Idol of Endurance leaves the battlefield.
+        this.addAbility(new EntersBattlefieldTriggeredAbility(new IdolOfEnduranceExileEffect()));
+
+        // {1}{W}, {T}: Until end of turn, you may cast a creature spell from among the cards exiled with Idol of Endurance without paying its mana cost.
+        Ability ability = new SimpleActivatedAbility(
+                new IdolOfEnduranceCastFromExileEffect(), new ManaCostsImpl<>("{1}{W}")
+        );
+        ability.addCost(new TapSourceCost());
+        this.addAbility(ability, new IdolOfEnduranceWatcher());
+    }
+
+    private IdolOfEndurance(final IdolOfEndurance card) {
+        super(card);
+    }
+
+    @Override
+    public IdolOfEndurance copy() {
+        return new IdolOfEndurance(this);
+    }
+}
+
+class IdolOfEnduranceExileEffect extends OneShotEffect {
+
+    private static final FilterCard filter = new FilterCreatureCard();
+
+    static {
+        filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4));
+    }
+
+    IdolOfEnduranceExileEffect() {
+        super(Outcome.Benefit);
+        staticText = "exile all creature cards with converted mana cost 3 or less from your graveyard until {this} leaves the battlefield";
+    }
+
+    private IdolOfEnduranceExileEffect(final IdolOfEnduranceExileEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public IdolOfEnduranceExileEffect copy() {
+        return new IdolOfEnduranceExileEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Player player = game.getPlayer(source.getControllerId());
+        Permanent permanent = source.getSourcePermanentIfItStillExists(game);
+        if (player == null || permanent == null) {
+            return false;
+        }
+        Cards cards = new CardsImpl(player.getGraveyard().getCards(filter, game));
+        MageObjectReference mor = new MageObjectReference(permanent, game);
+        player.moveCards(cards, Zone.EXILED, source, game);
+        Set<MageObjectReference> morSet = cards
+                .getCards(game)
+                .stream()
+                .filter(Objects::nonNull)
+                .map(card -> new MageObjectReference(card, game))
+                .collect(Collectors.toSet());
+        game.getState().setValue("" + mor.getSourceId() + mor.getZoneChangeCounter(), morSet);
+        game.addDelayedTriggeredAbility(new IdolOfEnduranceDelayedTrigger(morSet), source);
+        return true;
+    }
+}
+
+class IdolOfEnduranceDelayedTrigger extends DelayedTriggeredAbility {
+
+    IdolOfEnduranceDelayedTrigger(Set<MageObjectReference> morSet) {
+        super(new IdolOfEnduranceLeaveEffect(morSet), Duration.Custom, true, false);
+        this.usesStack = false;
+        this.setRuleVisible(false);
+    }
+
+    private IdolOfEnduranceDelayedTrigger(final IdolOfEnduranceDelayedTrigger ability) {
+        super(ability);
+    }
+
+    @Override
+    public boolean checkEventType(GameEvent event, Game game) {
+        return event.getType() == GameEvent.EventType.ZONE_CHANGE;
+    }
+
+    @Override
+    public boolean checkTrigger(GameEvent event, Game game) {
+        if (event.getTargetId().equals(this.getSourceId())) {
+            ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
+            if (zEvent.getFromZone() == Zone.BATTLEFIELD) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public IdolOfEnduranceDelayedTrigger copy() {
+        return new IdolOfEnduranceDelayedTrigger(this);
+    }
+}
+
+class IdolOfEnduranceLeaveEffect extends OneShotEffect {
+
+    private final Set<MageObjectReference> morSet = new HashSet<>();
+
+    IdolOfEnduranceLeaveEffect(Set<MageObjectReference> morSet) {
+        super(Outcome.Benefit);
+        this.morSet.addAll(morSet);
+    }
+
+    private IdolOfEnduranceLeaveEffect(final IdolOfEnduranceLeaveEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public IdolOfEnduranceLeaveEffect copy() {
+        return new IdolOfEnduranceLeaveEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Player player = game.getPlayer(source.getControllerId());
+        return player != null && player.moveCards(
+                morSet.stream()
+                        .map(mor -> mor.getCard(game))
+                        .filter(Objects::nonNull)
+                        .collect(Collectors.toSet()),
+                Zone.GRAVEYARD, source, game
+        );
+    }
+}
+
+class IdolOfEnduranceCastFromExileEffect extends AsThoughEffectImpl {
+
+    IdolOfEnduranceCastFromExileEffect() {
+        super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit);
+        staticText = "until end of turn, you may cast a creature spell from among the cards exiled with {this} without paying its mana cost";
+    }
+
+    private IdolOfEnduranceCastFromExileEffect(final IdolOfEnduranceCastFromExileEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        return true;
+    }
+
+    @Override
+    public IdolOfEnduranceCastFromExileEffect copy() {
+        return new IdolOfEnduranceCastFromExileEffect(this);
+    }
+
+    @Override
+    public void init(Ability source, Game game) {
+        super.init(source, game);
+        IdolOfEnduranceWatcher watcher = game.getState().getWatcher(IdolOfEnduranceWatcher.class);
+        if (watcher != null) {
+            watcher.addPlayable(source, game);
+        }
+    }
+
+    @Override
+    public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) {
+        IdolOfEnduranceWatcher watcher = game.getState().getWatcher(IdolOfEnduranceWatcher.class);
+        if (watcher == null || !watcher.checkPermission(affectedControllerId, source, game)) {
+            return false;
+        }
+        Object value = game.getState().getValue("" + source.getSourceId() + source.getSourceObjectZoneChangeCounter());
+        if (!(value instanceof Set)) {
+            discard();
+            return false;
+        }
+        Set<MageObjectReference> morSet = (Set<MageObjectReference>) value;
+        if (game.getState().getZone(sourceId) != Zone.EXILED
+                || morSet.stream().noneMatch(mor -> mor.refersTo(sourceId, game))) {
+            return false;
+        }
+        Card card = game.getCard(sourceId);
+        if (card == null || !card.isCreature() || card.isLand()) {
+            return false;
+        }
+        return allowCardToPlayWithoutMana(sourceId, source, affectedControllerId, game);
+    }
+}
+
+class IdolOfEnduranceWatcher extends Watcher {
+
+    private final Map<MageObjectReference, Map<UUID, Integer>> morMap = new HashMap<>();
+
+    IdolOfEnduranceWatcher() {
+        super(WatcherScope.GAME);
+    }
+
+    @Override
+    public void watch(GameEvent event, Game game) {
+        if (event.getType() == GameEvent.EventType.SPELL_CAST) {
+            if (event.getAdditionalReference() == null) {
+                return;
+            }
+            morMap.computeIfAbsent(event.getAdditionalReference(), m -> new HashMap<>())
+                    .compute(event.getPlayerId(), (u, i) -> i == null ? 0 : Integer.sum(i, -1));
+            return;
+        }
+    }
+
+    @Override
+    public void reset() {
+        morMap.clear();
+        super.reset();
+    }
+
+    boolean checkPermission(UUID playerId, Ability source, Game game) {
+        if (!playerId.equals(source.getControllerId())) {
+            return false;
+        }
+        MageObjectReference mor = new MageObjectReference(
+                source.getSourceId(), source.getSourceObjectZoneChangeCounter(), game
+        );
+        if (!morMap.containsKey(mor)) {
+            return false;
+        }
+        return morMap.get(mor).getOrDefault(playerId, 0) > 0;
+    }
+
+    void addPlayable(Ability source, Game game) {
+        MageObjectReference mor = new MageObjectReference(
+                source.getSourceId(), source.getSourceObjectZoneChangeCounter(), game
+        );
+        morMap.computeIfAbsent(mor, m -> new HashMap<>())
+                .compute(source.getControllerId(), (u, i) -> i == null ? 1 : Integer.sum(i, 1));
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 1c7be8b046..0b0123d146 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -142,6 +142,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Hobblefiend", 152, Rarity.COMMON, mage.cards.h.Hobblefiend.class));
         cards.add(new SetCardInfo("Hooded Blightfang", 104, Rarity.RARE, mage.cards.h.HoodedBlightfang.class));
         cards.add(new SetCardInfo("Hunter's Edge", 189, Rarity.COMMON, mage.cards.h.HuntersEdge.class));
+        cards.add(new SetCardInfo("Idol of Endurance", 23, Rarity.RARE, mage.cards.i.IdolOfEndurance.class));
         cards.add(new SetCardInfo("Igneous Cur", 153, Rarity.COMMON, mage.cards.i.IgneousCur.class));
         cards.add(new SetCardInfo("Indulging Patrician", 219, Rarity.UNCOMMON, mage.cards.i.IndulgingPatrician.class));
         cards.add(new SetCardInfo("Infernal Scarring", 105, Rarity.COMMON, mage.cards.i.InfernalScarring.class));
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/IdolOfEnduranceTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/IdolOfEnduranceTest.java
new file mode 100644
index 0000000000..63834e0496
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/IdolOfEnduranceTest.java
@@ -0,0 +1,125 @@
+package org.mage.test.cards.single;
+
+import mage.constants.PhaseStep;
+import mage.constants.Zone;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestPlayerBase;
+
+/**
+ * @author TheElk801
+ */
+public class IdolOfEnduranceTest extends CardTestPlayerBase {
+
+    private static final String idol = "Idol of Endurance";
+    private static final String key = "Voltaic Key";
+    private static final String sqr = "Squire";
+    private static final String glrskr = "Glory Seeker";
+
+    @Test
+    public void testIdol() {
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
+        addCard(Zone.HAND, playerA, idol);
+        addCard(Zone.GRAVEYARD, playerA, sqr);
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, idol);
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{W}");
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, sqr);
+
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerA, sqr, 1);
+    }
+
+    @Test
+    public void testIdol2() {
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
+        addCard(Zone.HAND, playerA, idol);
+        addCard(Zone.GRAVEYARD, playerA, sqr);
+        addCard(Zone.GRAVEYARD, playerA, glrskr);
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, idol);
+
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{W}");
+
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, sqr);
+
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, glrskr);
+
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+
+        assertPermanentCount(playerA, sqr, 1);
+        assertPermanentCount(playerA, glrskr, 0);
+    }
+
+    @Test
+    public void testIdolTwice() {
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 8);
+        addCard(Zone.BATTLEFIELD, playerA, key);
+        addCard(Zone.HAND, playerA, idol);
+        addCard(Zone.GRAVEYARD, playerA, sqr);
+        addCard(Zone.GRAVEYARD, playerA, glrskr);
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, idol);
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
+
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{W}");
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
+
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1},", idol);
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
+
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{W}");
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, sqr);
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, glrskr);
+
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerA, sqr, 1);
+        assertPermanentCount(playerA, glrskr, 1);
+    }
+
+    @Test
+    public void testIdolTwice2() {
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 8);
+        addCard(Zone.BATTLEFIELD, playerA, key);
+        addCard(Zone.HAND, playerA, idol);
+        addCard(Zone.GRAVEYARD, playerA, sqr);
+        addCard(Zone.GRAVEYARD, playerA, glrskr);
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, idol);
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
+
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{W}");
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, sqr);
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
+
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1},", idol);
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
+
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{W}");
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, glrskr);
+
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerA, sqr, 1);
+        assertPermanentCount(playerA, glrskr, 1);
+    }
+}

From e9672dce5e582bc02f45e644cc739e0e05630323 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 29 Jun 2020 20:06:12 -0400
Subject: [PATCH 566/586] updated JMP rarities

---
 Mage.Sets/src/mage/sets/Jumpstart.java | 2 +-
 Utils/mtg-cards-data.txt               | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index 1c1eeb8667..716e5fa540 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -39,7 +39,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Angel of the Dire Hour", 85, Rarity.RARE, mage.cards.a.AngelOfTheDireHour.class));
         cards.add(new SetCardInfo("Angelic Arbiter", 86, Rarity.RARE, mage.cards.a.AngelicArbiter.class));
         cards.add(new SetCardInfo("Angelic Edict", 87, Rarity.COMMON, mage.cards.a.AngelicEdict.class));
-        cards.add(new SetCardInfo("Angelic Page", 88, Rarity.UNCOMMON, mage.cards.a.AngelicPage.class));
+        cards.add(new SetCardInfo("Angelic Page", 88, Rarity.COMMON, mage.cards.a.AngelicPage.class));
         cards.add(new SetCardInfo("Arbor Armament", 376, Rarity.COMMON, mage.cards.a.ArborArmament.class));
         cards.add(new SetCardInfo("Arcane Encyclopedia", 459, Rarity.UNCOMMON, mage.cards.a.ArcaneEncyclopedia.class));
         cards.add(new SetCardInfo("Archaeomender", 9, Rarity.COMMON, mage.cards.a.Archaeomender.class));
diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt
index 0e99bd2dc9..d0a6b09d1c 100644
--- a/Utils/mtg-cards-data.txt
+++ b/Utils/mtg-cards-data.txt
@@ -37775,7 +37775,7 @@ Angel of Mercy|Jumpstart|84|C|{4}{W}|Creature - Angel|3|3|Flying$When Angel of M
 Angel of the Dire Hour|Jumpstart|85|R|{5}{W}{W}|Creature - Angel|5|4|Flash$Flying$When Angel of the Dire Hour enters the battlefield, if you cast it from your hand, exile all attacking creatures.|
 Angelic Arbiter|Jumpstart|86|R|{5}{W}{W}|Creature - Angel|5|6|Flying$Each opponent who cast a spell this turn can't attack with creatures.$Each opponent who attacked with a creature this turn can't cast spells.|
 Angelic Edict|Jumpstart|87|C|{4}{W}|Sorcery|||Exile target creature or enchantment.|
-Angelic Page|Jumpstart|88|U|{1}{W}|Creature - Angel Spirit|1|1|Flying${T}: Target attacking or blocking creature gets +1/+1 until end of turn.|
+Angelic Page|Jumpstart|88|C|{1}{W}|Creature - Angel Spirit|1|1|Flying${T}: Target attacking or blocking creature gets +1/+1 until end of turn.|
 Archon of Justice|Jumpstart|89|R|{3}{W}{W}|Creature - Archon|4|4|Flying$When Archon of Justice dies, exile target permanent.|
 Archon of Redemption|Jumpstart|90|R|{3}{W}{W}|Creature - Archon|3|4|Flying$Whenever Archon of Redemption or another creature with flying enters the battlefield under your control, you may gain life equal to that creature's power.|
 Battlefield Promotion|Jumpstart|91|C|{1}{W}|Instant|||Put a +1/+1 counter on target creature. That creature gains first strike until end of turn. You gain 2 life.|

From d70db82dd95143ec8cc2b8f2d6c58541bf7f53d7 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 29 Jun 2020 20:53:05 -0400
Subject: [PATCH 567/586] Implemented Teferi, Timeless Voyager

---
 .../mage/cards/t/TeferiTimelessVoyager.java   | 140 ++++++++++++++++++
 Mage.Sets/src/mage/sets/CoreSet2021.java      |   1 +
 2 files changed, 141 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/t/TeferiTimelessVoyager.java

diff --git a/Mage.Sets/src/mage/cards/t/TeferiTimelessVoyager.java b/Mage.Sets/src/mage/cards/t/TeferiTimelessVoyager.java
new file mode 100644
index 0000000000..ec3ab59fc1
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/t/TeferiTimelessVoyager.java
@@ -0,0 +1,140 @@
+package mage.cards.t;
+
+import mage.MageObjectReference;
+import mage.abilities.Ability;
+import mage.abilities.LoyaltyAbility;
+import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
+import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.DrawCardSourceControllerEffect;
+import mage.abilities.effects.common.PutOnLibraryTargetEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.*;
+import mage.filter.StaticFilters;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+import mage.game.permanent.Permanent;
+import mage.target.common.TargetCreaturePermanent;
+import mage.target.common.TargetOpponent;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class TeferiTimelessVoyager extends CardImpl {
+
+    public TeferiTimelessVoyager(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{4}{U}{U}");
+
+        this.addSuperType(SuperType.LEGENDARY);
+        this.subtype.add(SubType.TEFERI);
+        this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4));
+
+        // +1: Draw a card.
+        this.addAbility(new LoyaltyAbility(new DrawCardSourceControllerEffect(1), 1));
+
+        // −3: Put target creature on top of its owner's library.
+        Ability ability = new LoyaltyAbility(new PutOnLibraryTargetEffect(true), -3);
+        ability.addTarget(new TargetCreaturePermanent());
+        this.addAbility(ability);
+
+        // −8: Each creature target opponent controls phases out. Until the end of your next turn, they can't phase in.
+        ability = new LoyaltyAbility(new TeferiTimelessVoyagerEffect(), -8);
+        ability.addTarget(new TargetOpponent());
+        this.addAbility(ability);
+    }
+
+    private TeferiTimelessVoyager(final TeferiTimelessVoyager card) {
+        super(card);
+    }
+
+    @Override
+    public TeferiTimelessVoyager copy() {
+        return new TeferiTimelessVoyager(this);
+    }
+}
+
+class TeferiTimelessVoyagerEffect extends OneShotEffect {
+
+    TeferiTimelessVoyagerEffect() {
+        super(Outcome.Benefit);
+        staticText = "each creature target opponent controls phases out. " +
+                "Until the end of your next turn, they can't phase in";
+    }
+
+    private TeferiTimelessVoyagerEffect(final TeferiTimelessVoyagerEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public TeferiTimelessVoyagerEffect copy() {
+        return new TeferiTimelessVoyagerEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        for (Permanent permanent : game
+                .getBattlefield()
+                .getAllActivePermanents(
+                        StaticFilters.FILTER_PERMANENT_CREATURE, source.getFirstTarget(), game
+                )) {
+            MageObjectReference mor = new MageObjectReference(permanent, game);
+            permanent.phaseOut(game);
+            game.addEffect(new TeferiTimelessVoyagerPhaseEffect(mor), source);
+        }
+        return true;
+    }
+}
+
+class TeferiTimelessVoyagerPhaseEffect extends ContinuousRuleModifyingEffectImpl {
+
+    private int castOnTurn = 0;
+    private final MageObjectReference mor;
+
+    TeferiTimelessVoyagerPhaseEffect(MageObjectReference mor) {
+        super(Duration.Custom, Outcome.Neutral);
+        this.mor = mor;
+    }
+
+    private TeferiTimelessVoyagerPhaseEffect(final TeferiTimelessVoyagerPhaseEffect effect) {
+        super(effect);
+        this.castOnTurn = effect.castOnTurn;
+        this.mor = effect.mor;
+    }
+
+    @Override
+    public TeferiTimelessVoyagerPhaseEffect copy() {
+        return new TeferiTimelessVoyagerPhaseEffect(this);
+    }
+
+    @Override
+    public void init(Ability source, Game game) {
+        super.init(source, game);
+        castOnTurn = game.getTurnNum();
+    }
+
+    @Override
+    public boolean isInactive(Ability source, Game game) {
+        if (castOnTurn != game.getTurnNum() && game.getPhase().getStep().getType() == PhaseStep.END_TURN) {
+            return game.isActivePlayer(source.getControllerId());
+        }
+        return false;
+    }
+
+    @Override
+    public boolean checksEventType(GameEvent event, Game game) {
+        return event.getType() == GameEvent.EventType.PHASE_IN;
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        return true;
+    }
+
+    @Override
+    public boolean applies(GameEvent event, Ability source, Game game) {
+        return this.mor.refersTo(event.getTargetId(), game);
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 0b0123d146..f92cbd9526 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -272,6 +272,7 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Teferi's Tutelage", 78, Rarity.UNCOMMON, mage.cards.t.TeferisTutelage.class));
         cards.add(new SetCardInfo("Teferi's Wavecaster", 327, Rarity.RARE, mage.cards.t.TeferisWavecaster.class));
         cards.add(new SetCardInfo("Teferi, Master of Time", 75, Rarity.MYTHIC, mage.cards.t.TeferiMasterOfTime.class));
+        cards.add(new SetCardInfo("Teferi, Timeless Voyager", 324, Rarity.MYTHIC, mage.cards.t.TeferiTimelessVoyager.class));
         cards.add(new SetCardInfo("Tempered Veteran", 41, Rarity.UNCOMMON, mage.cards.t.TemperedVeteran.class));
         cards.add(new SetCardInfo("Temple of Epiphany", 252, Rarity.RARE, mage.cards.t.TempleOfEpiphany.class));
         cards.add(new SetCardInfo("Temple of Malady", 253, Rarity.RARE, mage.cards.t.TempleOfMalady.class));

From b885fffd9d7781c09bfaa2af92c4a0bb7d64403e Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Mon, 29 Jun 2020 21:12:36 -0400
Subject: [PATCH 568/586] added duplicates to M21 and JMP

---
 Mage.Sets/src/mage/sets/CoreSet2021.java | 280 ++++++++++++++++-------
 Mage.Sets/src/mage/sets/Jumpstart.java   |  35 +++
 Utils/mtg-cards-data.txt                 | 147 ++++++++++++
 3 files changed, 378 insertions(+), 84 deletions(-)

diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index f92cbd9526..5dceb3abbb 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -38,19 +38,28 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Alpine Houndmaster", 215, Rarity.UNCOMMON, mage.cards.a.AlpineHoundmaster.class));
         cards.add(new SetCardInfo("Alpine Watchdog", 2, Rarity.COMMON, mage.cards.a.AlpineWatchdog.class));
         cards.add(new SetCardInfo("Angelic Ascension", 3, Rarity.UNCOMMON, mage.cards.a.AngelicAscension.class));
-        cards.add(new SetCardInfo("Animal Sanctuary", 242, Rarity.RARE, mage.cards.a.AnimalSanctuary.class));
+        cards.add(new SetCardInfo("Animal Sanctuary", 242, Rarity.RARE, mage.cards.a.AnimalSanctuary.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Animal Sanctuary", 385, Rarity.RARE, mage.cards.a.AnimalSanctuary.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Anointed Chorister", 4, Rarity.COMMON, mage.cards.a.AnointedChorister.class));
         cards.add(new SetCardInfo("Archfiend's Vessel", 88, Rarity.UNCOMMON, mage.cards.a.ArchfiendsVessel.class));
         cards.add(new SetCardInfo("Aven Gagglemaster", 5, Rarity.UNCOMMON, mage.cards.a.AvenGagglemaster.class));
-        cards.add(new SetCardInfo("Azusa, Lost but Seeking", 173, Rarity.RARE, mage.cards.a.AzusaLostButSeeking.class));
+        cards.add(new SetCardInfo("Azusa, Lost but Seeking", 173, Rarity.RARE, mage.cards.a.AzusaLostButSeeking.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Azusa, Lost but Seeking", 372, Rarity.RARE, mage.cards.a.AzusaLostButSeeking.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Bad Deal", 89, Rarity.UNCOMMON, mage.cards.b.BadDeal.class));
-        cards.add(new SetCardInfo("Baneslayer Angel", 6, Rarity.MYTHIC, mage.cards.b.BaneslayerAngel.class));
-        cards.add(new SetCardInfo("Barrin, Tolarian Archmage", 45, Rarity.RARE, mage.cards.b.BarrinTolarianArchmage.class));
-        cards.add(new SetCardInfo("Basri Ket", 7, Rarity.MYTHIC, mage.cards.b.BasriKet.class));
-        cards.add(new SetCardInfo("Basri's Acolyte", 8, Rarity.COMMON, mage.cards.b.BasrisAcolyte.class));
+        cards.add(new SetCardInfo("Baneslayer Angel", 340, Rarity.MYTHIC, mage.cards.b.BaneslayerAngel.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Baneslayer Angel", 6, Rarity.MYTHIC, mage.cards.b.BaneslayerAngel.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Barrin, Tolarian Archmage", 348, Rarity.RARE, mage.cards.b.BarrinTolarianArchmage.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Barrin, Tolarian Archmage", 45, Rarity.RARE, mage.cards.b.BarrinTolarianArchmage.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Basri Ket", 280, Rarity.MYTHIC, mage.cards.b.BasriKet.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Basri Ket", 286, Rarity.MYTHIC, mage.cards.b.BasriKet.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Basri Ket", 7, Rarity.MYTHIC, mage.cards.b.BasriKet.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Basri's Acolyte", 287, Rarity.COMMON, mage.cards.b.BasrisAcolyte.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Basri's Acolyte", 8, Rarity.COMMON, mage.cards.b.BasrisAcolyte.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Basri's Aegis", 322, Rarity.RARE, mage.cards.b.BasrisAegis.class));
-        cards.add(new SetCardInfo("Basri's Lieutenant", 9, Rarity.RARE, mage.cards.b.BasrisLieutenant.class));
-        cards.add(new SetCardInfo("Basri's Solidarity", 10, Rarity.UNCOMMON, mage.cards.b.BasrisSolidarity.class));
+        cards.add(new SetCardInfo("Basri's Lieutenant", 288, Rarity.RARE, mage.cards.b.BasrisLieutenant.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Basri's Lieutenant", 9, Rarity.RARE, mage.cards.b.BasrisLieutenant.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Basri's Solidarity", 10, Rarity.UNCOMMON, mage.cards.b.BasrisSolidarity.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Basri's Solidarity", 289, Rarity.UNCOMMON, mage.cards.b.BasrisSolidarity.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Basri, Devoted Paladin", 320, Rarity.MYTHIC, mage.cards.b.BasriDevotedPaladin.class));
         cards.add(new SetCardInfo("Battle-Rattle Shaman", 130, Rarity.UNCOMMON, mage.cards.b.BattleRattleShaman.class));
         cards.add(new SetCardInfo("Blood Glutton", 90, Rarity.COMMON, mage.cards.b.BloodGlutton.class));
@@ -58,7 +67,8 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Blossoming Sands", 244, Rarity.COMMON, mage.cards.b.BlossomingSands.class));
         cards.add(new SetCardInfo("Bolt Hound", 131, Rarity.UNCOMMON, mage.cards.b.BoltHound.class));
         cards.add(new SetCardInfo("Bone Pit Brute", 132, Rarity.COMMON, mage.cards.b.BonePitBrute.class));
-        cards.add(new SetCardInfo("Brash Taunter", 133, Rarity.RARE, mage.cards.b.BrashTaunter.class));
+        cards.add(new SetCardInfo("Brash Taunter", 133, Rarity.RARE, mage.cards.b.BrashTaunter.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Brash Taunter", 363, Rarity.RARE, mage.cards.b.BrashTaunter.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Burlfist Oak", 174, Rarity.UNCOMMON, mage.cards.b.BurlfistOak.class));
         cards.add(new SetCardInfo("Burn Bright", 134, Rarity.COMMON, mage.cards.b.BurnBright.class));
         cards.add(new SetCardInfo("Caged Zombie", 91, Rarity.COMMON, mage.cards.c.CagedZombie.class));
@@ -68,90 +78,129 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Carrion Grub", 92, Rarity.UNCOMMON, mage.cards.c.CarrionGrub.class));
         cards.add(new SetCardInfo("Celestial Enforcer", 11, Rarity.COMMON, mage.cards.c.CelestialEnforcer.class));
         cards.add(new SetCardInfo("Chandra's Firemaw", 333, Rarity.RARE, mage.cards.c.ChandrasFiremaw.class));
-        cards.add(new SetCardInfo("Chandra's Incinerator", 136, Rarity.RARE, mage.cards.c.ChandrasIncinerator.class));
-        cards.add(new SetCardInfo("Chandra's Magmutt", 137, Rarity.COMMON, mage.cards.c.ChandrasMagmutt.class));
-        cards.add(new SetCardInfo("Chandra's Pyreling", 138, Rarity.UNCOMMON, mage.cards.c.ChandrasPyreling.class));
+        cards.add(new SetCardInfo("Chandra's Incinerator", 136, Rarity.RARE, mage.cards.c.ChandrasIncinerator.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Chandra's Incinerator", 302, Rarity.RARE, mage.cards.c.ChandrasIncinerator.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Chandra's Magmutt", 137, Rarity.COMMON, mage.cards.c.ChandrasMagmutt.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Chandra's Magmutt", 303, Rarity.COMMON, mage.cards.c.ChandrasMagmutt.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Chandra's Pyreling", 138, Rarity.UNCOMMON, mage.cards.c.ChandrasPyreling.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Chandra's Pyreling", 304, Rarity.UNCOMMON, mage.cards.c.ChandrasPyreling.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Chandra, Flame's Catalyst", 332, Rarity.MYTHIC, mage.cards.c.ChandraFlamesCatalyst.class));
-        cards.add(new SetCardInfo("Chandra, Heart of Fire", 135, Rarity.MYTHIC, mage.cards.c.ChandraHeartOfFire.class));
-        cards.add(new SetCardInfo("Chromatic Orrery", 228, Rarity.MYTHIC, mage.cards.c.ChromaticOrrery.class));
+        cards.add(new SetCardInfo("Chandra, Heart of Fire", 135, Rarity.MYTHIC, mage.cards.c.ChandraHeartOfFire.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Chandra, Heart of Fire", 283, Rarity.MYTHIC, mage.cards.c.ChandraHeartOfFire.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Chandra, Heart of Fire", 301, Rarity.MYTHIC, mage.cards.c.ChandraHeartOfFire.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Chromatic Orrery", 228, Rarity.MYTHIC, mage.cards.c.ChromaticOrrery.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Chromatic Orrery", 382, Rarity.MYTHIC, mage.cards.c.ChromaticOrrery.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Chrome Replicator", 229, Rarity.UNCOMMON, mage.cards.c.ChromeReplicator.class));
         cards.add(new SetCardInfo("Colossal Dreadmaw", 176, Rarity.COMMON, mage.cards.c.ColossalDreadmaw.class));
         cards.add(new SetCardInfo("Conclave Mentor", 216, Rarity.UNCOMMON, mage.cards.c.ConclaveMentor.class));
         cards.add(new SetCardInfo("Concordia Pegasus", 12, Rarity.COMMON, mage.cards.c.ConcordiaPegasus.class));
-        cards.add(new SetCardInfo("Conspicuous Snoop", 139, Rarity.RARE, mage.cards.c.ConspicuousSnoop.class));
-        cards.add(new SetCardInfo("Containment Priest", 13, Rarity.RARE, mage.cards.c.ContainmentPriest.class));
+        cards.add(new SetCardInfo("Conspicuous Snoop", 139, Rarity.RARE, mage.cards.c.ConspicuousSnoop.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Conspicuous Snoop", 364, Rarity.RARE, mage.cards.c.ConspicuousSnoop.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Containment Priest", 13, Rarity.RARE, mage.cards.c.ContainmentPriest.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Containment Priest", 314, Rarity.RARE, mage.cards.c.ContainmentPriest.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Crash Through", 140, Rarity.COMMON, mage.cards.c.CrashThrough.class));
         cards.add(new SetCardInfo("Crypt Lurker", 93, Rarity.COMMON, mage.cards.c.CryptLurker.class));
-        cards.add(new SetCardInfo("Cultivate", 177, Rarity.UNCOMMON, mage.cards.c.Cultivate.class));
+        cards.add(new SetCardInfo("Cultivate", 177, Rarity.UNCOMMON, mage.cards.c.Cultivate.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Cultivate", 317, Rarity.RARE, mage.cards.c.Cultivate.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Daybreak Charger", 14, Rarity.COMMON, mage.cards.d.DaybreakCharger.class));
         cards.add(new SetCardInfo("Deathbloom Thallid", 94, Rarity.COMMON, mage.cards.d.DeathbloomThallid.class));
         cards.add(new SetCardInfo("Defiant Strike", 15, Rarity.COMMON, mage.cards.d.DefiantStrike.class));
-        cards.add(new SetCardInfo("Demonic Embrace", 95, Rarity.RARE, mage.cards.d.DemonicEmbrace.class));
+        cards.add(new SetCardInfo("Demonic Embrace", 356, Rarity.RARE, mage.cards.d.DemonicEmbrace.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Demonic Embrace", 95, Rarity.RARE, mage.cards.d.DemonicEmbrace.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Destructive Tampering", 141, Rarity.COMMON, mage.cards.d.DestructiveTampering.class));
         cards.add(new SetCardInfo("Dire Fleet Warmonger", 217, Rarity.UNCOMMON, mage.cards.d.DireFleetWarmonger.class));
-        cards.add(new SetCardInfo("Discontinuity", 48, Rarity.MYTHIC, mage.cards.d.Discontinuity.class));
+        cards.add(new SetCardInfo("Discontinuity", 349, Rarity.MYTHIC, mage.cards.d.Discontinuity.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Discontinuity", 48, Rarity.MYTHIC, mage.cards.d.Discontinuity.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Dismal Backwater", 245, Rarity.COMMON, mage.cards.d.DismalBackwater.class));
-        cards.add(new SetCardInfo("Double Vision", 142, Rarity.RARE, mage.cards.d.DoubleVision.class));
+        cards.add(new SetCardInfo("Double Vision", 142, Rarity.RARE, mage.cards.d.DoubleVision.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Double Vision", 365, Rarity.RARE, mage.cards.d.DoubleVision.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Drowsing Tyrannodon", 178, Rarity.COMMON, mage.cards.d.DrowsingTyrannodon.class));
         cards.add(new SetCardInfo("Dub", 16, Rarity.COMMON, mage.cards.d.Dub.class));
         cards.add(new SetCardInfo("Duress", 96, Rarity.COMMON, mage.cards.d.Duress.class));
-        cards.add(new SetCardInfo("Elder Gargaroth", 179, Rarity.MYTHIC, mage.cards.e.ElderGargaroth.class));
-        cards.add(new SetCardInfo("Eliminate", 97, Rarity.UNCOMMON, mage.cards.e.Eliminate.class));
+        cards.add(new SetCardInfo("Elder Gargaroth", 179, Rarity.MYTHIC, mage.cards.e.ElderGargaroth.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Elder Gargaroth", 373, Rarity.MYTHIC, mage.cards.e.ElderGargaroth.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Eliminate", 395, Rarity.UNCOMMON, mage.cards.e.Eliminate.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Eliminate", 97, Rarity.UNCOMMON, mage.cards.e.Eliminate.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Enthralling Hold", 49, Rarity.UNCOMMON, mage.cards.e.EnthrallingHold.class));
         cards.add(new SetCardInfo("Epitaph Golem", 230, Rarity.UNCOMMON, mage.cards.e.EpitaphGolem.class));
         cards.add(new SetCardInfo("Experimental Overload", 218, Rarity.UNCOMMON, mage.cards.e.ExperimentalOverload.class));
-        cards.add(new SetCardInfo("Fabled Passage", 246, Rarity.RARE, mage.cards.f.FabledPassage.class));
+        cards.add(new SetCardInfo("Fabled Passage", 246, Rarity.RARE, mage.cards.f.FabledPassage.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Fabled Passage", 386, Rarity.RARE, mage.cards.f.FabledPassage.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Faith's Fetters", 17, Rarity.UNCOMMON, mage.cards.f.FaithsFetters.class));
         cards.add(new SetCardInfo("Falconer Adept", 18, Rarity.UNCOMMON, mage.cards.f.FalconerAdept.class));
         cards.add(new SetCardInfo("Feat of Resistance", 19, Rarity.COMMON, mage.cards.f.FeatOfResistance.class));
-        cards.add(new SetCardInfo("Feline Sovereign", 180, Rarity.RARE, mage.cards.f.FelineSovereign.class));
+        cards.add(new SetCardInfo("Feline Sovereign", 180, Rarity.RARE, mage.cards.f.FelineSovereign.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Feline Sovereign", 374, Rarity.RARE, mage.cards.f.FelineSovereign.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Fetid Imp", 98, Rarity.COMMON, mage.cards.f.FetidImp.class));
         cards.add(new SetCardInfo("Fierce Empath", 181, Rarity.UNCOMMON, mage.cards.f.FierceEmpath.class));
-        cards.add(new SetCardInfo("Fiery Emancipation", 143, Rarity.MYTHIC, mage.cards.f.FieryEmancipation.class));
+        cards.add(new SetCardInfo("Fiery Emancipation", 143, Rarity.MYTHIC, mage.cards.f.FieryEmancipation.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Fiery Emancipation", 366, Rarity.MYTHIC, mage.cards.f.FieryEmancipation.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Finishing Blow", 99, Rarity.COMMON, mage.cards.f.FinishingBlow.class));
         cards.add(new SetCardInfo("Forest", 272, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Forest", 273, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Forest", 274, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Forest", 313, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Forgotten Sentinel", 231, Rarity.COMMON, mage.cards.f.ForgottenSentinel.class));
-        cards.add(new SetCardInfo("Frantic Inventory", 50, Rarity.COMMON, mage.cards.f.FranticInventory.class));
+        cards.add(new SetCardInfo("Frantic Inventory", 394, Rarity.COMMON, mage.cards.f.FranticInventory.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Frantic Inventory", 50, Rarity.COMMON, mage.cards.f.FranticInventory.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Frost Breath", 51, Rarity.COMMON, mage.cards.f.FrostBreath.class));
         cards.add(new SetCardInfo("Fungal Rebirth", 182, Rarity.UNCOMMON, mage.cards.f.FungalRebirth.class));
         cards.add(new SetCardInfo("Furious Rise", 144, Rarity.UNCOMMON, mage.cards.f.FuriousRise.class));
         cards.add(new SetCardInfo("Furor of the Bitten", 145, Rarity.COMMON, mage.cards.f.FurorOfTheBitten.class));
-        cards.add(new SetCardInfo("Gadrak, the Crown-Scourge", 146, Rarity.RARE, mage.cards.g.GadrakTheCrownScourge.class));
+        cards.add(new SetCardInfo("Gadrak, the Crown-Scourge", 146, Rarity.RARE, mage.cards.g.GadrakTheCrownScourge.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Gadrak, the Crown-Scourge", 367, Rarity.RARE, mage.cards.g.GadrakTheCrownScourge.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Gale Swooper", 20, Rarity.COMMON, mage.cards.g.GaleSwooper.class));
-        cards.add(new SetCardInfo("Garruk's Gorehorn", 184, Rarity.COMMON, mage.cards.g.GarruksGorehorn.class));
-        cards.add(new SetCardInfo("Garruk's Harbinger", 185, Rarity.RARE, mage.cards.g.GarruksHarbinger.class));
-        cards.add(new SetCardInfo("Garruk's Uprising", 186, Rarity.UNCOMMON, mage.cards.g.GarruksUprising.class));
+        cards.add(new SetCardInfo("Garruk's Gorehorn", 184, Rarity.COMMON, mage.cards.g.GarruksGorehorn.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Garruk's Gorehorn", 306, Rarity.COMMON, mage.cards.g.GarruksGorehorn.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Garruk's Harbinger", 185, Rarity.RARE, mage.cards.g.GarruksHarbinger.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Garruk's Harbinger", 307, Rarity.RARE, mage.cards.g.GarruksHarbinger.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Garruk's Uprising", 186, Rarity.UNCOMMON, mage.cards.g.GarruksUprising.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Garruk's Uprising", 308, Rarity.UNCOMMON, mage.cards.g.GarruksUprising.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Garruk's Warsteed", 337, Rarity.RARE, mage.cards.g.GarruksWarsteed.class));
         cards.add(new SetCardInfo("Garruk, Savage Herald", 336, Rarity.MYTHIC, mage.cards.g.GarrukSavageHerald.class));
-        cards.add(new SetCardInfo("Garruk, Unleashed", 183, Rarity.MYTHIC, mage.cards.g.GarrukUnleashed.class));
-        cards.add(new SetCardInfo("Ghostly Pilferer", 52, Rarity.RARE, mage.cards.g.GhostlyPilferer.class));
+        cards.add(new SetCardInfo("Garruk, Unleashed", 183, Rarity.MYTHIC, mage.cards.g.GarrukUnleashed.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Garruk, Unleashed", 284, Rarity.MYTHIC, mage.cards.g.GarrukUnleashed.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Garruk, Unleashed", 305, Rarity.MYTHIC, mage.cards.g.GarrukUnleashed.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Ghostly Pilferer", 350, Rarity.RARE, mage.cards.g.GhostlyPilferer.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Ghostly Pilferer", 52, Rarity.RARE, mage.cards.g.GhostlyPilferer.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Gloom Sower", 100, Rarity.COMMON, mage.cards.g.GloomSower.class));
-        cards.add(new SetCardInfo("Glorious Anthem", 21, Rarity.RARE, mage.cards.g.GloriousAnthem.class));
+        cards.add(new SetCardInfo("Glorious Anthem", 21, Rarity.RARE, mage.cards.g.GloriousAnthem.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Glorious Anthem", 341, Rarity.RARE, mage.cards.g.GloriousAnthem.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Gnarled Sage", 187, Rarity.COMMON, mage.cards.g.GnarledSage.class));
         cards.add(new SetCardInfo("Goblin Arsonist", 147, Rarity.COMMON, mage.cards.g.GoblinArsonist.class));
         cards.add(new SetCardInfo("Goblin Wizardry", 148, Rarity.COMMON, mage.cards.g.GoblinWizardry.class));
         cards.add(new SetCardInfo("Goremand", 101, Rarity.UNCOMMON, mage.cards.g.Goremand.class));
         cards.add(new SetCardInfo("Grasp of Darkness", 102, Rarity.COMMON, mage.cards.g.GraspOfDarkness.class));
         cards.add(new SetCardInfo("Griffin Aerie", 22, Rarity.UNCOMMON, mage.cards.g.GriffinAerie.class));
-        cards.add(new SetCardInfo("Grim Tutor", 103, Rarity.MYTHIC, mage.cards.g.GrimTutor.class));
+        cards.add(new SetCardInfo("Grim Tutor", 103, Rarity.MYTHIC, mage.cards.g.GrimTutor.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Grim Tutor", 315, Rarity.MYTHIC, mage.cards.g.GrimTutor.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Havoc Jester", 149, Rarity.UNCOMMON, mage.cards.h.HavocJester.class));
-        cards.add(new SetCardInfo("Heartfire Immolator", 150, Rarity.UNCOMMON, mage.cards.h.HeartfireImmolator.class));
+        cards.add(new SetCardInfo("Heartfire Immolator", 150, Rarity.UNCOMMON, mage.cards.h.HeartfireImmolator.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Heartfire Immolator", 396, Rarity.UNCOMMON, mage.cards.h.HeartfireImmolator.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Hellkite Punisher", 151, Rarity.UNCOMMON, mage.cards.h.HellkitePunisher.class));
-        cards.add(new SetCardInfo("Heroic Intervention", 188, Rarity.RARE, mage.cards.h.HeroicIntervention.class));
+        cards.add(new SetCardInfo("Heroic Intervention", 188, Rarity.RARE, mage.cards.h.HeroicIntervention.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Heroic Intervention", 375, Rarity.RARE, mage.cards.h.HeroicIntervention.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Historian of Zhalfir", 325, Rarity.UNCOMMON, mage.cards.h.HistorianOfZhalfir.class));
         cards.add(new SetCardInfo("Hobblefiend", 152, Rarity.COMMON, mage.cards.h.Hobblefiend.class));
-        cards.add(new SetCardInfo("Hooded Blightfang", 104, Rarity.RARE, mage.cards.h.HoodedBlightfang.class));
+        cards.add(new SetCardInfo("Hooded Blightfang", 104, Rarity.RARE, mage.cards.h.HoodedBlightfang.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Hooded Blightfang", 357, Rarity.RARE, mage.cards.h.HoodedBlightfang.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Hunter's Edge", 189, Rarity.COMMON, mage.cards.h.HuntersEdge.class));
-        cards.add(new SetCardInfo("Idol of Endurance", 23, Rarity.RARE, mage.cards.i.IdolOfEndurance.class));
+        cards.add(new SetCardInfo("Idol of Endurance", 23, Rarity.RARE, mage.cards.i.IdolOfEndurance.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Idol of Endurance", 342, Rarity.RARE, mage.cards.i.IdolOfEndurance.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Igneous Cur", 153, Rarity.COMMON, mage.cards.i.IgneousCur.class));
         cards.add(new SetCardInfo("Indulging Patrician", 219, Rarity.UNCOMMON, mage.cards.i.IndulgingPatrician.class));
         cards.add(new SetCardInfo("Infernal Scarring", 105, Rarity.COMMON, mage.cards.i.InfernalScarring.class));
         cards.add(new SetCardInfo("Invigorating Surge", 190, Rarity.UNCOMMON, mage.cards.i.InvigoratingSurge.class));
         cards.add(new SetCardInfo("Island", 263, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Island", 264, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Island", 265, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Island", 310, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Jeskai Elder", 53, Rarity.UNCOMMON, mage.cards.j.JeskaiElder.class));
-        cards.add(new SetCardInfo("Jolrael, Mwonvuli Recluse", 191, Rarity.RARE, mage.cards.j.JolraelMwonvuliRecluse.class));
+        cards.add(new SetCardInfo("Jolrael, Mwonvuli Recluse", 191, Rarity.RARE, mage.cards.j.JolraelMwonvuliRecluse.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Jolrael, Mwonvuli Recluse", 376, Rarity.RARE, mage.cards.j.JolraelMwonvuliRecluse.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Jungle Hollow", 247, Rarity.COMMON, mage.cards.j.JungleHollow.class));
-        cards.add(new SetCardInfo("Kaervek, the Spiteful", 106, Rarity.RARE, mage.cards.k.KaervekTheSpiteful.class));
+        cards.add(new SetCardInfo("Kaervek, the Spiteful", 106, Rarity.RARE, mage.cards.k.KaervekTheSpiteful.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Kaervek, the Spiteful", 358, Rarity.RARE, mage.cards.k.KaervekTheSpiteful.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Keen Glidemaster", 54, Rarity.COMMON, mage.cards.k.KeenGlidemaster.class));
         cards.add(new SetCardInfo("Keral Keep Disciples", 334, Rarity.UNCOMMON, mage.cards.k.KeralKeepDisciples.class));
         cards.add(new SetCardInfo("Kinetic Augur", 154, Rarity.UNCOMMON, mage.cards.k.KineticAugur.class));
@@ -161,49 +210,73 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Library Larcenist", 55, Rarity.COMMON, mage.cards.l.LibraryLarcenist.class));
         cards.add(new SetCardInfo("Life Goes On", 192, Rarity.COMMON, mage.cards.l.LifeGoesOn.class));
         cards.add(new SetCardInfo("Light of Promise", 25, Rarity.UNCOMMON, mage.cards.l.LightOfPromise.class));
-        cards.add(new SetCardInfo("Liliana's Devotee", 109, Rarity.UNCOMMON, mage.cards.l.LilianasDevotee.class));
+        cards.add(new SetCardInfo("Liliana's Devotee", 109, Rarity.UNCOMMON, mage.cards.l.LilianasDevotee.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Liliana's Devotee", 298, Rarity.UNCOMMON, mage.cards.l.LilianasDevotee.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Liliana's Scorn", 329, Rarity.RARE, mage.cards.l.LilianasScorn.class));
         cards.add(new SetCardInfo("Liliana's Scrounger", 330, Rarity.UNCOMMON, mage.cards.l.LilianasScrounger.class));
-        cards.add(new SetCardInfo("Liliana's Standard Bearer", 110, Rarity.RARE, mage.cards.l.LilianasStandardBearer.class));
-        cards.add(new SetCardInfo("Liliana's Steward", 111, Rarity.COMMON, mage.cards.l.LilianasSteward.class));
+        cards.add(new SetCardInfo("Liliana's Standard Bearer", 110, Rarity.RARE, mage.cards.l.LilianasStandardBearer.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Liliana's Standard Bearer", 299, Rarity.RARE, mage.cards.l.LilianasStandardBearer.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Liliana's Steward", 111, Rarity.COMMON, mage.cards.l.LilianasSteward.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Liliana's Steward", 300, Rarity.COMMON, mage.cards.l.LilianasSteward.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Liliana, Death Mage", 328, Rarity.MYTHIC, mage.cards.l.LilianaDeathMage.class));
-        cards.add(new SetCardInfo("Liliana, Waker of the Dead", 108, Rarity.MYTHIC, mage.cards.l.LilianaWakerOfTheDead.class));
-        cards.add(new SetCardInfo("Llanowar Visionary", 193, Rarity.COMMON, mage.cards.l.LlanowarVisionary.class));
+        cards.add(new SetCardInfo("Liliana, Waker of the Dead", 108, Rarity.MYTHIC, mage.cards.l.LilianaWakerOfTheDead.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Liliana, Waker of the Dead", 282, Rarity.MYTHIC, mage.cards.l.LilianaWakerOfTheDead.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Liliana, Waker of the Dead", 297, Rarity.MYTHIC, mage.cards.l.LilianaWakerOfTheDead.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Llanowar Visionary", 193, Rarity.COMMON, mage.cards.l.LlanowarVisionary.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Llanowar Visionary", 397, Rarity.COMMON, mage.cards.l.LlanowarVisionary.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Lofty Denial", 56, Rarity.COMMON, mage.cards.l.LoftyDenial.class));
         cards.add(new SetCardInfo("Lorescale Coatl", 221, Rarity.UNCOMMON, mage.cards.l.LorescaleCoatl.class));
         cards.add(new SetCardInfo("Makeshift Battalion", 26, Rarity.COMMON, mage.cards.m.MakeshiftBattalion.class));
         cards.add(new SetCardInfo("Malefic Scythe", 112, Rarity.UNCOMMON, mage.cards.m.MaleficScythe.class));
-        cards.add(new SetCardInfo("Mangara, the Diplomat", 27, Rarity.MYTHIC, mage.cards.m.MangaraTheDiplomat.class));
+        cards.add(new SetCardInfo("Mangara, the Diplomat", 27, Rarity.MYTHIC, mage.cards.m.MangaraTheDiplomat.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Mangara, the Diplomat", 343, Rarity.MYTHIC, mage.cards.m.MangaraTheDiplomat.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Masked Blackguard", 113, Rarity.COMMON, mage.cards.m.MaskedBlackguard.class));
-        cards.add(new SetCardInfo("Massacre Wurm", 114, Rarity.MYTHIC, mage.cards.m.MassacreWurm.class));
-        cards.add(new SetCardInfo("Mazemind Tome", 232, Rarity.RARE, mage.cards.m.MazemindTome.class));
+        cards.add(new SetCardInfo("Massacre Wurm", 114, Rarity.MYTHIC, mage.cards.m.MassacreWurm.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Massacre Wurm", 316, Rarity.MYTHIC, mage.cards.m.MassacreWurm.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Mazemind Tome", 232, Rarity.RARE, mage.cards.m.MazemindTome.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Mazemind Tome", 383, Rarity.RARE, mage.cards.m.MazemindTome.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Meteorite", 233, Rarity.UNCOMMON, mage.cards.m.Meteorite.class));
         cards.add(new SetCardInfo("Mind Rot", 115, Rarity.COMMON, mage.cards.m.MindRot.class));
         cards.add(new SetCardInfo("Miscast", 57, Rarity.UNCOMMON, mage.cards.m.Miscast.class));
         cards.add(new SetCardInfo("Mistral Singer", 58, Rarity.COMMON, mage.cards.m.MistralSinger.class));
         cards.add(new SetCardInfo("Mountain", 269, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Mountain", 270, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Mountain", 271, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Mountain", 312, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Mystic Skyfish", 326, Rarity.COMMON, mage.cards.m.MysticSkyfish.class));
-        cards.add(new SetCardInfo("Necromentia", 116, Rarity.RARE, mage.cards.n.Necromentia.class));
-        cards.add(new SetCardInfo("Niambi, Esteemed Speaker", 222, Rarity.RARE, mage.cards.n.NiambiEsteemedSpeaker.class));
-        cards.add(new SetCardInfo("Nine Lives", 28, Rarity.RARE, mage.cards.n.NineLives.class));
+        cards.add(new SetCardInfo("Necromentia", 116, Rarity.RARE, mage.cards.n.Necromentia.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Necromentia", 359, Rarity.RARE, mage.cards.n.Necromentia.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Niambi, Esteemed Speaker", 222, Rarity.RARE, mage.cards.n.NiambiEsteemedSpeaker.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Niambi, Esteemed Speaker", 379, Rarity.RARE, mage.cards.n.NiambiEsteemedSpeaker.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Nine Lives", 28, Rarity.RARE, mage.cards.n.NineLives.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Nine Lives", 344, Rarity.RARE, mage.cards.n.NineLives.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Obsessive Stitcher", 223, Rarity.UNCOMMON, mage.cards.o.ObsessiveStitcher.class));
         cards.add(new SetCardInfo("Onakke Ogre", 155, Rarity.COMMON, mage.cards.o.OnakkeOgre.class));
         cards.add(new SetCardInfo("Opt", 59, Rarity.COMMON, mage.cards.o.Opt.class));
         cards.add(new SetCardInfo("Ornery Dilophosaur", 194, Rarity.COMMON, mage.cards.o.OrneryDilophosaur.class));
-        cards.add(new SetCardInfo("Pack Leader", 29, Rarity.RARE, mage.cards.p.PackLeader.class));
+        cards.add(new SetCardInfo("Pack Leader", 29, Rarity.RARE, mage.cards.p.PackLeader.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Pack Leader", 345, Rarity.RARE, mage.cards.p.PackLeader.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Pack Leader", 392, Rarity.RARE, mage.cards.p.PackLeader.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Palladium Myr", 234, Rarity.UNCOMMON, mage.cards.p.PalladiumMyr.class));
-        cards.add(new SetCardInfo("Peer into the Abyss", 117, Rarity.RARE, mage.cards.p.PeerIntoTheAbyss.class));
+        cards.add(new SetCardInfo("Peer into the Abyss", 117, Rarity.RARE, mage.cards.p.PeerIntoTheAbyss.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Peer into the Abyss", 360, Rarity.RARE, mage.cards.p.PeerIntoTheAbyss.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Pestilent Haze", 118, Rarity.UNCOMMON, mage.cards.p.PestilentHaze.class));
         cards.add(new SetCardInfo("Pitchburn Devils", 156, Rarity.COMMON, mage.cards.p.PitchburnDevils.class));
         cards.add(new SetCardInfo("Plains", 260, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Plains", 261, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Plains", 262, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Plains", 309, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Portcullis Vine", 195, Rarity.COMMON, mage.cards.p.PortcullisVine.class));
         cards.add(new SetCardInfo("Predatory Wurm", 338, Rarity.UNCOMMON, mage.cards.p.PredatoryWurm.class));
         cards.add(new SetCardInfo("Pridemalkin", 196, Rarity.COMMON, mage.cards.p.Pridemalkin.class));
-        cards.add(new SetCardInfo("Primal Might", 197, Rarity.RARE, mage.cards.p.PrimalMight.class));
+        cards.add(new SetCardInfo("Primal Might", 197, Rarity.RARE, mage.cards.p.PrimalMight.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Primal Might", 377, Rarity.RARE, mage.cards.p.PrimalMight.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Prismite", 235, Rarity.COMMON, mage.cards.p.Prismite.class));
-        cards.add(new SetCardInfo("Pursued Whale", 60, Rarity.RARE, mage.cards.p.PursuedWhale.class));
+        cards.add(new SetCardInfo("Pursued Whale", 351, Rarity.RARE, mage.cards.p.PursuedWhale.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Pursued Whale", 60, Rarity.RARE, mage.cards.p.PursuedWhale.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Quirion Dryad", 198, Rarity.UNCOMMON, mage.cards.q.QuirionDryad.class));
-        cards.add(new SetCardInfo("Radha, Heart of Keld", 224, Rarity.RARE, mage.cards.r.RadhaHeartOfKeld.class));
+        cards.add(new SetCardInfo("Radha, Heart of Keld", 224, Rarity.RARE, mage.cards.r.RadhaHeartOfKeld.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Radha, Heart of Keld", 380, Rarity.RARE, mage.cards.r.RadhaHeartOfKeld.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Radiant Fountain", 248, Rarity.COMMON, mage.cards.r.RadiantFountain.class));
         cards.add(new SetCardInfo("Rain of Revelation", 61, Rarity.UNCOMMON, mage.cards.r.RainOfRevelation.class));
         cards.add(new SetCardInfo("Rambunctious Mutt", 30, Rarity.COMMON, mage.cards.r.RambunctiousMutt.class));
@@ -220,24 +293,30 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Rousing Read", 67, Rarity.COMMON, mage.cards.r.RousingRead.class));
         cards.add(new SetCardInfo("Rugged Highlands", 249, Rarity.COMMON, mage.cards.r.RuggedHighlands.class));
         cards.add(new SetCardInfo("Run Afoul", 201, Rarity.COMMON, mage.cards.r.RunAfoul.class));
-        cards.add(new SetCardInfo("Runed Halo", 32, Rarity.RARE, mage.cards.r.RunedHalo.class));
+        cards.add(new SetCardInfo("Runed Halo", 32, Rarity.RARE, mage.cards.r.RunedHalo.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Runed Halo", 346, Rarity.RARE, mage.cards.r.RunedHalo.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Sabertooth Mauler", 202, Rarity.COMMON, mage.cards.s.SabertoothMauler.class));
-        cards.add(new SetCardInfo("Sanctum of All", 225, Rarity.RARE, mage.cards.s.SanctumOfAll.class));
+        cards.add(new SetCardInfo("Sanctum of All", 225, Rarity.RARE, mage.cards.s.SanctumOfAll.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Sanctum of All", 381, Rarity.RARE, mage.cards.s.SanctumOfAll.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Sanctum of Calm Waters", 68, Rarity.UNCOMMON, mage.cards.s.SanctumOfCalmWaters.class));
         cards.add(new SetCardInfo("Sanctum of Fruitful Harvest", 203, Rarity.UNCOMMON, mage.cards.s.SanctumOfFruitfulHarvest.class));
         cards.add(new SetCardInfo("Sanctum of Shattered Heights", 157, Rarity.UNCOMMON, mage.cards.s.SanctumOfShatteredHeights.class));
         cards.add(new SetCardInfo("Sanctum of Stone Fangs", 120, Rarity.UNCOMMON, mage.cards.s.SanctumOfStoneFangs.class));
         cards.add(new SetCardInfo("Sanctum of Tranquil Light", 33, Rarity.UNCOMMON, mage.cards.s.SanctumOfTranquilLight.class));
         cards.add(new SetCardInfo("Sanguine Indulgence", 121, Rarity.COMMON, mage.cards.s.SanguineIndulgence.class));
-        cards.add(new SetCardInfo("Scavenging Ooze", 204, Rarity.RARE, mage.cards.s.ScavengingOoze.class));
+        cards.add(new SetCardInfo("Scavenging Ooze", 204, Rarity.RARE, mage.cards.s.ScavengingOoze.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Scavenging Ooze", 318, Rarity.RARE, mage.cards.s.ScavengingOoze.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Scorching Dragonfire", 158, Rarity.COMMON, mage.cards.s.ScorchingDragonfire.class));
         cards.add(new SetCardInfo("Scoured Barrens", 250, Rarity.COMMON, mage.cards.s.ScouredBarrens.class));
         cards.add(new SetCardInfo("Seasoned Hallowblade", 34, Rarity.UNCOMMON, mage.cards.s.SeasonedHallowblade.class));
         cards.add(new SetCardInfo("Secure the Scene", 35, Rarity.COMMON, mage.cards.s.SecureTheScene.class));
-        cards.add(new SetCardInfo("See the Truth", 69, Rarity.RARE, mage.cards.s.SeeTheTruth.class));
-        cards.add(new SetCardInfo("Selfless Savior", 36, Rarity.UNCOMMON, mage.cards.s.SelflessSavior.class));
+        cards.add(new SetCardInfo("See the Truth", 352, Rarity.RARE, mage.cards.s.SeeTheTruth.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("See the Truth", 69, Rarity.RARE, mage.cards.s.SeeTheTruth.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Selfless Savior", 36, Rarity.UNCOMMON, mage.cards.s.SelflessSavior.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Selfless Savior", 393, Rarity.UNCOMMON, mage.cards.s.SelflessSavior.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Setessan Training", 205, Rarity.COMMON, mage.cards.s.SetessanTraining.class));
-        cards.add(new SetCardInfo("Shacklegeist", 70, Rarity.RARE, mage.cards.s.Shacklegeist.class));
+        cards.add(new SetCardInfo("Shacklegeist", 353, Rarity.RARE, mage.cards.s.Shacklegeist.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Shacklegeist", 70, Rarity.RARE, mage.cards.s.Shacklegeist.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Shipwreck Dowser", 71, Rarity.UNCOMMON, mage.cards.s.ShipwreckDowser.class));
         cards.add(new SetCardInfo("Shock", 159, Rarity.COMMON, mage.cards.s.Shock.class));
         cards.add(new SetCardInfo("Short Sword", 236, Rarity.COMMON, mage.cards.s.ShortSword.class));
@@ -249,38 +328,66 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Skyscanner", 238, Rarity.COMMON, mage.cards.s.Skyscanner.class));
         cards.add(new SetCardInfo("Skyway Sniper", 206, Rarity.UNCOMMON, mage.cards.s.SkywaySniper.class));
         cards.add(new SetCardInfo("Snarespinner", 207, Rarity.COMMON, mage.cards.s.Snarespinner.class));
-        cards.add(new SetCardInfo("Solemn Simulacrum", 239, Rarity.RARE, mage.cards.s.SolemnSimulacrum.class));
+        cards.add(new SetCardInfo("Solemn Simulacrum", 239, Rarity.RARE, mage.cards.s.SolemnSimulacrum.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Solemn Simulacrum", 319, Rarity.RARE, mage.cards.s.SolemnSimulacrum.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Soul Sear", 160, Rarity.UNCOMMON, mage.cards.s.SoulSear.class));
-        cards.add(new SetCardInfo("Sparkhunter Masticore", 240, Rarity.RARE, mage.cards.s.SparkhunterMasticore.class));
-        cards.add(new SetCardInfo("Speaker of the Heavens", 38, Rarity.RARE, mage.cards.s.SpeakerOfTheHeavens.class));
+        cards.add(new SetCardInfo("Sparkhunter Masticore", 240, Rarity.RARE, mage.cards.s.SparkhunterMasticore.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Sparkhunter Masticore", 384, Rarity.RARE, mage.cards.s.SparkhunterMasticore.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Speaker of the Heavens", 347, Rarity.RARE, mage.cards.s.SpeakerOfTheHeavens.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Speaker of the Heavens", 38, Rarity.RARE, mage.cards.s.SpeakerOfTheHeavens.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Spellgorger Weird", 161, Rarity.COMMON, mage.cards.s.SpellgorgerWeird.class));
         cards.add(new SetCardInfo("Spined Megalodon", 72, Rarity.COMMON, mage.cards.s.SpinedMegalodon.class));
         cards.add(new SetCardInfo("Spirit of Malevolence", 331, Rarity.COMMON, mage.cards.s.SpiritOfMalevolence.class));
-        cards.add(new SetCardInfo("Sporeweb Weaver", 208, Rarity.RARE, mage.cards.s.SporewebWeaver.class));
+        cards.add(new SetCardInfo("Sporeweb Weaver", 208, Rarity.RARE, mage.cards.s.SporewebWeaver.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Sporeweb Weaver", 378, Rarity.RARE, mage.cards.s.SporewebWeaver.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Staunch Shieldmate", 39, Rarity.COMMON, mage.cards.s.StaunchShieldmate.class));
         cards.add(new SetCardInfo("Storm Caller", 335, Rarity.COMMON, mage.cards.s.StormCaller.class));
-        cards.add(new SetCardInfo("Stormwing Entity", 73, Rarity.RARE, mage.cards.s.StormwingEntity.class));
-        cards.add(new SetCardInfo("Subira, Tulzidi Caravanner", 162, Rarity.RARE, mage.cards.s.SubiraTulzidiCaravanner.class));
-        cards.add(new SetCardInfo("Sublime Epiphany", 74, Rarity.RARE, mage.cards.s.SublimeEpiphany.class));
+        cards.add(new SetCardInfo("Stormwing Entity", 354, Rarity.RARE, mage.cards.s.StormwingEntity.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Stormwing Entity", 73, Rarity.RARE, mage.cards.s.StormwingEntity.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Subira, Tulzidi Caravanner", 162, Rarity.RARE, mage.cards.s.SubiraTulzidiCaravanner.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Subira, Tulzidi Caravanner", 368, Rarity.RARE, mage.cards.s.SubiraTulzidiCaravanner.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Sublime Epiphany", 355, Rarity.RARE, mage.cards.s.SublimeEpiphany.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Sublime Epiphany", 74, Rarity.RARE, mage.cards.s.SublimeEpiphany.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Sure Strike", 163, Rarity.COMMON, mage.cards.s.SureStrike.class));
         cards.add(new SetCardInfo("Swamp", 266, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Swamp", 267, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Swamp", 268, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Swamp", 311, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Swift Response", 40, Rarity.COMMON, mage.cards.s.SwiftResponse.class));
         cards.add(new SetCardInfo("Swiftwater Cliffs", 251, Rarity.COMMON, mage.cards.s.SwiftwaterCliffs.class));
         cards.add(new SetCardInfo("Tavern Swindler", 124, Rarity.UNCOMMON, mage.cards.t.TavernSwindler.class));
-        cards.add(new SetCardInfo("Teferi's Ageless Insight", 76, Rarity.RARE, mage.cards.t.TeferisAgelessInsight.class));
-        cards.add(new SetCardInfo("Teferi's Protege", 77, Rarity.COMMON, mage.cards.t.TeferisProtege.class));
-        cards.add(new SetCardInfo("Teferi's Tutelage", 78, Rarity.UNCOMMON, mage.cards.t.TeferisTutelage.class));
+        cards.add(new SetCardInfo("Teferi's Ageless Insight", 294, Rarity.RARE, mage.cards.t.TeferisAgelessInsight.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Teferi's Ageless Insight", 76, Rarity.RARE, mage.cards.t.TeferisAgelessInsight.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Teferi's Protege", 295, Rarity.COMMON, mage.cards.t.TeferisProtege.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Teferi's Protege", 77, Rarity.COMMON, mage.cards.t.TeferisProtege.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Teferi's Tutelage", 296, Rarity.UNCOMMON, mage.cards.t.TeferisTutelage.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Teferi's Tutelage", 78, Rarity.UNCOMMON, mage.cards.t.TeferisTutelage.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Teferi's Wavecaster", 327, Rarity.RARE, mage.cards.t.TeferisWavecaster.class));
-        cards.add(new SetCardInfo("Teferi, Master of Time", 75, Rarity.MYTHIC, mage.cards.t.TeferiMasterOfTime.class));
+        cards.add(new SetCardInfo("Teferi, Master of Time", 275, Rarity.MYTHIC, mage.cards.t.TeferiMasterOfTime.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Teferi, Master of Time", 276, Rarity.MYTHIC, mage.cards.t.TeferiMasterOfTime.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Teferi, Master of Time", 277, Rarity.MYTHIC, mage.cards.t.TeferiMasterOfTime.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Teferi, Master of Time", 281, Rarity.MYTHIC, mage.cards.t.TeferiMasterOfTime.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Teferi, Master of Time", 290, Rarity.MYTHIC, mage.cards.t.TeferiMasterOfTime.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Teferi, Master of Time", 291, Rarity.MYTHIC, mage.cards.t.TeferiMasterOfTime.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Teferi, Master of Time", 292, Rarity.MYTHIC, mage.cards.t.TeferiMasterOfTime.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Teferi, Master of Time", 293, Rarity.MYTHIC, mage.cards.t.TeferiMasterOfTime.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Teferi, Master of Time", 75, Rarity.MYTHIC, mage.cards.t.TeferiMasterOfTime.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Teferi, Timeless Voyager", 324, Rarity.MYTHIC, mage.cards.t.TeferiTimelessVoyager.class));
         cards.add(new SetCardInfo("Tempered Veteran", 41, Rarity.UNCOMMON, mage.cards.t.TemperedVeteran.class));
-        cards.add(new SetCardInfo("Temple of Epiphany", 252, Rarity.RARE, mage.cards.t.TempleOfEpiphany.class));
-        cards.add(new SetCardInfo("Temple of Malady", 253, Rarity.RARE, mage.cards.t.TempleOfMalady.class));
-        cards.add(new SetCardInfo("Temple of Mystery", 254, Rarity.RARE, mage.cards.t.TempleOfMystery.class));
-        cards.add(new SetCardInfo("Temple of Silence", 255, Rarity.RARE, mage.cards.t.TempleOfSilence.class));
-        cards.add(new SetCardInfo("Temple of Triumph", 256, Rarity.RARE, mage.cards.t.TempleOfTriumph.class));
-        cards.add(new SetCardInfo("Terror of the Peaks", 164, Rarity.MYTHIC, mage.cards.t.TerrorOfThePeaks.class));
-        cards.add(new SetCardInfo("Thieves' Guild Enforcer", 125, Rarity.RARE, mage.cards.t.ThievesGuildEnforcer.class));
+        cards.add(new SetCardInfo("Temple of Epiphany", 252, Rarity.RARE, mage.cards.t.TempleOfEpiphany.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Temple of Epiphany", 387, Rarity.RARE, mage.cards.t.TempleOfEpiphany.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Temple of Malady", 253, Rarity.RARE, mage.cards.t.TempleOfMalady.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Temple of Malady", 388, Rarity.RARE, mage.cards.t.TempleOfMalady.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Temple of Mystery", 254, Rarity.RARE, mage.cards.t.TempleOfMystery.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Temple of Mystery", 389, Rarity.RARE, mage.cards.t.TempleOfMystery.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Temple of Silence", 255, Rarity.RARE, mage.cards.t.TempleOfSilence.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Temple of Silence", 390, Rarity.RARE, mage.cards.t.TempleOfSilence.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Temple of Triumph", 256, Rarity.RARE, mage.cards.t.TempleOfTriumph.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Temple of Triumph", 391, Rarity.RARE, mage.cards.t.TempleOfTriumph.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Terror of the Peaks", 164, Rarity.MYTHIC, mage.cards.t.TerrorOfThePeaks.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Terror of the Peaks", 369, Rarity.MYTHIC, mage.cards.t.TerrorOfThePeaks.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Thieves' Guild Enforcer", 125, Rarity.RARE, mage.cards.t.ThievesGuildEnforcer.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Thieves' Guild Enforcer", 361, Rarity.RARE, mage.cards.t.ThievesGuildEnforcer.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Thornwood Falls", 257, Rarity.COMMON, mage.cards.t.ThornwoodFalls.class));
         cards.add(new SetCardInfo("Thrashing Brontodon", 209, Rarity.UNCOMMON, mage.cards.t.ThrashingBrontodon.class));
         cards.add(new SetCardInfo("Thrill of Possibility", 165, Rarity.COMMON, mage.cards.t.ThrillOfPossibility.class));
@@ -292,20 +399,25 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Track Down", 211, Rarity.COMMON, mage.cards.t.TrackDown.class));
         cards.add(new SetCardInfo("Traitorous Greed", 166, Rarity.UNCOMMON, mage.cards.t.TraitorousGreed.class));
         cards.add(new SetCardInfo("Tranquil Cove", 258, Rarity.COMMON, mage.cards.t.TranquilCove.class));
-        cards.add(new SetCardInfo("Transmogrify", 167, Rarity.RARE, mage.cards.t.Transmogrify.class));
+        cards.add(new SetCardInfo("Transmogrify", 167, Rarity.RARE, mage.cards.t.Transmogrify.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Transmogrify", 370, Rarity.RARE, mage.cards.t.Transmogrify.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Trufflesnout", 212, Rarity.COMMON, mage.cards.t.Trufflesnout.class));
         cards.add(new SetCardInfo("Turn to Slag", 168, Rarity.COMMON, mage.cards.t.TurnToSlag.class));
         cards.add(new SetCardInfo("Turret Ogre", 169, Rarity.COMMON, mage.cards.t.TurretOgre.class));
         cards.add(new SetCardInfo("Twinblade Assassins", 226, Rarity.UNCOMMON, mage.cards.t.TwinbladeAssassins.class));
-        cards.add(new SetCardInfo("Ugin, the Spirit Dragon", 1, Rarity.MYTHIC, mage.cards.u.UginTheSpiritDragon.class));
+        cards.add(new SetCardInfo("Ugin, the Spirit Dragon", 1, Rarity.MYTHIC, mage.cards.u.UginTheSpiritDragon.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Ugin, the Spirit Dragon", 279, Rarity.MYTHIC, mage.cards.u.UginTheSpiritDragon.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Ugin, the Spirit Dragon", 285, Rarity.MYTHIC, mage.cards.u.UginTheSpiritDragon.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Unleash Fury", 170, Rarity.UNCOMMON, mage.cards.u.UnleashFury.class));
         cards.add(new SetCardInfo("Unsubstantiate", 82, Rarity.UNCOMMON, mage.cards.u.Unsubstantiate.class));
         cards.add(new SetCardInfo("Valorous Steed", 42, Rarity.COMMON, mage.cards.v.ValorousSteed.class));
         cards.add(new SetCardInfo("Village Rites", 126, Rarity.COMMON, mage.cards.v.VillageRites.class));
-        cards.add(new SetCardInfo("Vito, Thorn of the Dusk Rose", 127, Rarity.RARE, mage.cards.v.VitoThornOfTheDuskRose.class));
+        cards.add(new SetCardInfo("Vito, Thorn of the Dusk Rose", 127, Rarity.RARE, mage.cards.v.VitoThornOfTheDuskRose.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Vito, Thorn of the Dusk Rose", 362, Rarity.RARE, mage.cards.v.VitoThornOfTheDuskRose.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Vodalian Arcanist", 83, Rarity.COMMON, mage.cards.v.VodalianArcanist.class));
         cards.add(new SetCardInfo("Volcanic Geyser", 171, Rarity.UNCOMMON, mage.cards.v.VolcanicGeyser.class));
-        cards.add(new SetCardInfo("Volcanic Salvo", 172, Rarity.RARE, mage.cards.v.VolcanicSalvo.class));
+        cards.add(new SetCardInfo("Volcanic Salvo", 172, Rarity.RARE, mage.cards.v.VolcanicSalvo.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Volcanic Salvo", 371, Rarity.RARE, mage.cards.v.VolcanicSalvo.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Vryn Wingmare", 43, Rarity.UNCOMMON, mage.cards.v.VrynWingmare.class));
         cards.add(new SetCardInfo("Waker of Waves", 84, Rarity.UNCOMMON, mage.cards.w.WakerOfWaves.class));
         cards.add(new SetCardInfo("Walking Corpse", 128, Rarity.COMMON, mage.cards.w.WalkingCorpse.class));
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index 716e5fa540..123c86c98a 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -179,6 +179,13 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Flurry of Horns", 321, Rarity.COMMON, mage.cards.f.FlurryOfHorns.class));
         cards.add(new SetCardInfo("Forced Worship", 104, Rarity.COMMON, mage.cards.f.ForcedWorship.class));
         cards.add(new SetCardInfo("Forest", 70, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Forest", 71, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Forest", 72, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Forest", 73, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Forest", 74, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Forest", 75, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Forest", 76, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Forest", 77, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Forge Devil", 322, Rarity.COMMON, mage.cards.f.ForgeDevil.class));
         cards.add(new SetCardInfo("Fortify", 105, Rarity.COMMON, mage.cards.f.Fortify.class));
         cards.add(new SetCardInfo("Funeral Rites", 235, Rarity.COMMON, mage.cards.f.FuneralRites.class));
@@ -232,6 +239,13 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Irresistible Prey", 406, Rarity.UNCOMMON, mage.cards.i.IrresistiblePrey.class));
         cards.add(new SetCardInfo("Isamaru, Hound of Konda", 113, Rarity.RARE, mage.cards.i.IsamaruHoundOfKonda.class));
         cards.add(new SetCardInfo("Island", 46, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Island", 47, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Island", 48, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Island", 49, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Island", 50, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Island", 51, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Island", 52, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Island", 53, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Jousting Dummy", 470, Rarity.COMMON, mage.cards.j.JoustingDummy.class));
         cards.add(new SetCardInfo("Juggernaut", 471, Rarity.UNCOMMON, mage.cards.j.Juggernaut.class));
         cards.add(new SetCardInfo("Kalastria Nightwatch", 245, Rarity.COMMON, mage.cards.k.KalastriaNightwatch.class));
@@ -292,6 +306,13 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Moment of Heroism", 124, Rarity.COMMON, mage.cards.m.MomentOfHeroism.class));
         cards.add(new SetCardInfo("Momentous Fall", 411, Rarity.RARE, mage.cards.m.MomentousFall.class));
         cards.add(new SetCardInfo("Mountain", 62, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Mountain", 63, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Mountain", 64, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Mountain", 65, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Mountain", 66, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Mountain", 67, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Mountain", 68, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Mountain", 69, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Mugging", 352, Rarity.COMMON, mage.cards.m.Mugging.class));
         cards.add(new SetCardInfo("Murmuring Phantasm", 157, Rarity.COMMON, mage.cards.m.MurmuringPhantasm.class));
         cards.add(new SetCardInfo("Muxus, Goblin Grandee", 24, Rarity.RARE, mage.cards.m.MuxusGoblinGrandee.class));
@@ -333,6 +354,13 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Pirate's Cutlass", 477, Rarity.COMMON, mage.cards.p.PiratesCutlass.class));
         cards.add(new SetCardInfo("Plagued Rusalka", 268, Rarity.COMMON, mage.cards.p.PlaguedRusalka.class));
         cards.add(new SetCardInfo("Plains", 38, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Plains", 39, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Plains", 40, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Plains", 41, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Plains", 42, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Plains", 43, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Plains", 44, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Plains", 45, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Pouncing Cheetah", 419, Rarity.COMMON, mage.cards.p.PouncingCheetah.class));
         cards.add(new SetCardInfo("Prescient Chimera", 164, Rarity.COMMON, mage.cards.p.PrescientChimera.class));
         cards.add(new SetCardInfo("Presence of Gond", 420, Rarity.COMMON, mage.cards.p.PresenceOfGond.class));
@@ -412,6 +440,13 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Supply Runners", 7, Rarity.UNCOMMON, mage.cards.s.SupplyRunners.class));
         cards.add(new SetCardInfo("Suspicious Bookcase", 487, Rarity.UNCOMMON, mage.cards.s.SuspiciousBookcase.class));
         cards.add(new SetCardInfo("Swamp", 54, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Swamp", 55, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Swamp", 56, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Swamp", 57, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Swamp", 58, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Swamp", 59, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Swamp", 60, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS));
+        cards.add(new SetCardInfo("Swamp", 61, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS));
         cards.add(new SetCardInfo("Swarm of Bloodflies", 282, Rarity.UNCOMMON, mage.cards.s.SwarmOfBloodflies.class));
         cards.add(new SetCardInfo("Sweep Away", 180, Rarity.COMMON, mage.cards.s.SweepAway.class));
         cards.add(new SetCardInfo("Sylvan Brushstrider", 434, Rarity.COMMON, mage.cards.s.SylvanBrushstrider.class));
diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt
index d0a6b09d1c..50a1a042a1 100644
--- a/Utils/mtg-cards-data.txt
+++ b/Utils/mtg-cards-data.txt
@@ -37698,11 +37698,65 @@ Thornwood Falls|Core Set 2021|257|C||Land|||Thornwood Falls enters the battlefie
 Tranquil Cove|Core Set 2021|258|C||Land|||Tranquil Cove enters the battlefield tapped.$When Tranquil Cove enters the battlefield, you gain 1 life.${T}: Add {W} or {U}.|
 Wind-Scarred Crag|Core Set 2021|259|C||Land|||Wind-Scarred Crag enters the battlefield tapped.$When Wind-Scarred Crag enters the battlefield, you gain 1 life.${T}: Add {R} or {W}.|
 Plains|Core Set 2021|260|C||Basic Land - Plains|||({T}: Add {W}.)|
+Plains|Core Set 2021|261|C||Basic Land - Plains|||({T}: Add {W}.)|
+Plains|Core Set 2021|262|C||Basic Land - Plains|||({T}: Add {W}.)|
 Island|Core Set 2021|263|C||Basic Land - Island|||({T}: Add {U}.)|
+Island|Core Set 2021|264|C||Basic Land - Island|||({T}: Add {U}.)|
+Island|Core Set 2021|265|C||Basic Land - Island|||({T}: Add {U}.)|
 Swamp|Core Set 2021|266|C||Basic Land - Swamp|||({T}: Add {B}.)|
+Swamp|Core Set 2021|267|C||Basic Land - Swamp|||({T}: Add {B}.)|
+Swamp|Core Set 2021|268|C||Basic Land - Swamp|||({T}: Add {B}.)|
 Mountain|Core Set 2021|269|C||Basic Land - Mountain|||({T}: Add {R}.)|
+Mountain|Core Set 2021|270|C||Basic Land - Mountain|||({T}: Add {R}.)|
+Mountain|Core Set 2021|271|C||Basic Land - Mountain|||({T}: Add {R}.)|
 Forest|Core Set 2021|272|C||Basic Land - Forest|||({T}: Add {G}.)|
+Forest|Core Set 2021|273|C||Basic Land - Forest|||({T}: Add {G}.)|
+Forest|Core Set 2021|274|C||Basic Land - Forest|||({T}: Add {G}.)|
+Teferi, Master of Time|Core Set 2021|275|M|{2}{U}{U}|Legendary Planeswalker - Teferi|3|You may activate loyalty abilities of Teferi, Master of Time on any player's turn any time you could cast an instant.$+1: Draw a card, then discard a card.$−3: Target creature you don't control phases out.$−10: Take two extra turns after this one.|
+Teferi, Master of Time|Core Set 2021|276|M|{2}{U}{U}|Legendary Planeswalker - Teferi|3|You may activate loyalty abilities of Teferi, Master of Time on any player's turn any time you could cast an instant.$+1: Draw a card, then discard a card.$−3: Target creature you don't control phases out.$−10: Take two extra turns after this one.|
+Teferi, Master of Time|Core Set 2021|277|M|{2}{U}{U}|Legendary Planeswalker - Teferi|3|You may activate loyalty abilities of Teferi, Master of Time on any player's turn any time you could cast an instant.$+1: Draw a card, then discard a card.$−3: Target creature you don't control phases out.$−10: Take two extra turns after this one.|
 Rin and Seri, Inseparable|Core Set 2021|278|M|{1}{R}{G}{W}|Legendary Creature - Dog Cat|4|4|Whenever you cast a Dog spell, create a 1/1 green Cat creature token.$Whenever you cast a Cat spell, create a 1/1 white Dog creature token.${R}{G}{W}, {T}: Rin and Seri, Inseparable deals damage to any target equal to the number of Dogs you control. You gain life equal to the number of Cats you control.|
+Ugin, the Spirit Dragon|Core Set 2021|279|M|{8}|Legendary Planeswalker - Ugin|7|+2: Ugin, the Spirit Dragon deals 3 damage to any target.$−X: Exile each permanent with converted mana cost X or less that's one or more colors.$−10: You gain 7 life, draw seven cards, then put up to seven permanent cards from your hand onto the battlefield.|
+Basri Ket|Core Set 2021|280|M|{1}{W}{W}|Legendary Planeswalker - Basri|3|+1: Put a +1/+1 counter on up to one target creature. It gains indestructible until end of turn.$−2: Whenever one or more nontoken creatures attack this turn, create that many 1/1 white Soldier creature tokens that are tapped and attacking.$−6: You get an emblem with "At the beginning of combat on your turn, create a 1/1 white Soldier creature token, then put a +1/+1 counter on each creature you control."|
+Teferi, Master of Time|Core Set 2021|281|M|{2}{U}{U}|Legendary Planeswalker - Teferi|3|You may activate loyalty abilities of Teferi, Master of Time on any player's turn any time you could cast an instant.$+1: Draw a card, then discard a card.$−3: Target creature you don't control phases out.$−10: Take two extra turns after this one.|
+Liliana, Waker of the Dead|Core Set 2021|282|M|{2}{B}{B}|Legendary Planeswalker - Liliana|4|+1: Each player discards a card. Each opponent who can't loses 3 life.$−3: Target creature gets -X/-X until end of turn, where X is the number of cards in your graveyard.$−7: You get an emblem with "At the beginning of combat on your turn, put target creature card from a graveyard onto the battlefield under your control. It gains haste."|
+Chandra, Heart of Fire|Core Set 2021|283|M|{3}{R}{R}|Legendary Planeswalker - Chandra|5|+1: Discard your hand, then exile the top three cards of your library. Until end of turn, you may play cards exiled this way.$+1: Chandra, Heart of Fire deals 2 damage to any target.$−9: Search your graveyard and library for any number of red instant and/or sorcery spells, exile them, then shuffle your library. You may cast them this turn. Add six {R}.|
+Garruk, Unleashed|Core Set 2021|284|M|{2}{G}{G}|Legendary Planeswalker - Garruk|4|+1: Up to one target creature gets +3/+3 and gains trample until end of turn.$−2: Create a 3/3 green Beast creature token. Then if an opponent controls more creatures than you, put a loyalty counter on Garruk, Unleashed.$−7: Create an emblem with "At the beginning of your end step, you may search your library for a creature card, put it onto the battlefield, then shuffle your library."|
+Ugin, the Spirit Dragon|Core Set 2021|285|M|{8}|Legendary Planeswalker - Ugin|7|+2: Ugin, the Spirit Dragon deals 3 damage to any target.$−X: Exile each permanent with converted mana cost X or less that's one or more colors.$−10: You gain 7 life, draw seven cards, then put up to seven permanent cards from your hand onto the battlefield.|
+Basri Ket|Core Set 2021|286|M|{1}{W}{W}|Legendary Planeswalker - Basri|3|+1: Put a +1/+1 counter on up to one target creature. It gains indestructible until end of turn.$−2: Whenever one or more nontoken creatures attack this turn, create that many 1/1 white Soldier creature tokens that are tapped and attacking.$−6: You get an emblem with "At the beginning of combat on your turn, create a 1/1 white Soldier creature token, then put a +1/+1 counter on each creature you control."|
+Basri's Acolyte|Core Set 2021|287|C|{2}{W}{W}|Creature - Cat Cleric|2|3|Lifelink$When Basri's Acolyte enters the battlefield, put a +1/+1 counter on each of up to two other target creatures you control.|
+Basri's Lieutenant|Core Set 2021|288|R|{3}{W}|Creature - Human Knight|3|4|Vigilance, protection from multicolored$When Basri's Lieutenant enters the battlefield, put a +1/+1 counter on target creature you control.$Whenever Basri's Lieutenant or another creature you control dies, if it had a +1/+1 counter on it, create a 2/2 white Knight creature token with vigilance.|
+Basri's Solidarity|Core Set 2021|289|U|{1}{W}|Sorcery|||Put a +1/+1 counter on each creature you control.|
+Teferi, Master of Time|Core Set 2021|290|M|{2}{U}{U}|Legendary Planeswalker - Teferi|3|You may activate loyalty abilities of Teferi, Master of Time on any player's turn any time you could cast an instant.$+1: Draw a card, then discard a card.$−3: Target creature you don't control phases out.$−10: Take two extra turns after this one.|
+Teferi, Master of Time|Core Set 2021|291|M|{2}{U}{U}|Legendary Planeswalker - Teferi|3|You may activate loyalty abilities of Teferi, Master of Time on any player's turn any time you could cast an instant.$+1: Draw a card, then discard a card.$−3: Target creature you don't control phases out.$−10: Take two extra turns after this one.|
+Teferi, Master of Time|Core Set 2021|292|M|{2}{U}{U}|Legendary Planeswalker - Teferi|3|You may activate loyalty abilities of Teferi, Master of Time on any player's turn any time you could cast an instant.$+1: Draw a card, then discard a card.$−3: Target creature you don't control phases out.$−10: Take two extra turns after this one.|
+Teferi, Master of Time|Core Set 2021|293|M|{2}{U}{U}|Legendary Planeswalker - Teferi|3|You may activate loyalty abilities of Teferi, Master of Time on any player's turn any time you could cast an instant.$+1: Draw a card, then discard a card.$−3: Target creature you don't control phases out.$−10: Take two extra turns after this one.|
+Teferi's Ageless Insight|Core Set 2021|294|R|{2}{U}{U}|Legendary Enchantment|||If you would draw a card except the first one you draw in each of your draw steps, draw two cards instead.|
+Teferi's Protege|Core Set 2021|295|C|{2}{U}|Creature - Human Wizard|2|3|{1}{U}, {T}: Draw a card, then discard a card.|
+Teferi's Tutelage|Core Set 2021|296|U|{2}{U}|Enchantment|||When Teferi's Tutelage enters the battlefield, draw a card, then discard a card.$Whenever you draw a card, target opponent mills two cards.|
+Liliana, Waker of the Dead|Core Set 2021|297|M|{2}{B}{B}|Legendary Planeswalker - Liliana|4|+1: Each player discards a card. Each opponent who can't loses 3 life.$−3: Target creature gets -X/-X until end of turn, where X is the number of cards in your graveyard.$−7: You get an emblem with "At the beginning of combat on your turn, put target creature card from a graveyard onto the battlefield under your control. It gains haste."|
+Liliana's Devotee|Core Set 2021|298|U|{2}{B}|Creature - Human Warlock|2|3|Zombies you control get +1/+0.$At the beginning of your end step, if a creature died this turn, you may pay {1}{B}. If you do, create a 2/2 black Zombie creature token.|
+Liliana's Standard Bearer|Core Set 2021|299|R|{2}{B}|Creature - Zombie Knight|3|1|Flash$When Liliana's Standard Bearer enters the battlefield, draw X cards, where X is the number of creatures that died under your control this turn.|
+Liliana's Steward|Core Set 2021|300|C|{B}|Creature - Zombie|1|2|{T}, Sacrifice Liliana's Steward: Target opponent discards a card. Activate this ability only any time you could cast a sorcery.|
+Chandra, Heart of Fire|Core Set 2021|301|M|{3}{R}{R}|Legendary Planeswalker - Chandra|5|+1: Discard your hand, then exile the top three cards of your library. Until end of turn, you may play cards exiled this way.$+1: Chandra, Heart of Fire deals 2 damage to any target.$−9: Search your graveyard and library for any number of red instant and/or sorcery spells, exile them, then shuffle your library. You may cast them this turn. Add six {R}.|
+Chandra's Incinerator|Core Set 2021|302|R|{5}{R}|Creature - Elemental|6|6|This spell costs {X} less to cast, where X is the total amount of noncombat damage dealt to your opponents this turn.$Trample$Whenever a source you control deals noncombat damage to an opponent, Chandra's Incinerator deals that much damage to target creature or planeswalker that player controls.|
+Chandra's Magmutt|Core Set 2021|303|C|{1}{R}|Creature - Elemental Dog|2|2|{T}: Chandra's Magmutt deals 1 damage to target player or planeswalker.|
+Chandra's Pyreling|Core Set 2021|304|U|{1}{R}|Creature - Elemental Lizard|1|3|Whenever a source you control deals noncombat damage to an opponent, Chandra's Pyreling gets +1/+0 and gains double strike until end of turn.|
+Garruk, Unleashed|Core Set 2021|305|M|{2}{G}{G}|Legendary Planeswalker - Garruk|4|+1: Up to one target creature gets +3/+3 and gains trample until end of turn.$−2: Create a 3/3 green Beast creature token. Then if an opponent controls more creatures than you, put a loyalty counter on Garruk, Unleashed.$−7: Create an emblem with "At the beginning of your end step, you may search your library for a creature card, put it onto the battlefield, then shuffle your library."|
+Garruk's Gorehorn|Core Set 2021|306|C|{4}{G}|Creature - Beast|7|3||
+Garruk's Harbinger|Core Set 2021|307|R|{1}{G}{G}|Creature - Beast|4|3|Hexproof from Black$Whenever Garruk's Harbinger deals combat damage to a player or planeswalker, look at that many cards from the top of your library. You may reveal a creature card or Garruk planeswalker card from among them and put it into your hand. Put the rest on the bottom of your library in a random order.|
+Garruk's Uprising|Core Set 2021|308|U|{2}{G}|Enchantment|||When Garruk's Uprising enters the battlefield, if you control a creature with power 4 or greater, draw a card.$Creatures you control have trample.$Whenever a creature with power 4 or greater enters the battlefield under your control, draw a card.|
+Plains|Core Set 2021|309|C||Basic Land - Plains|||({T}: Add {W}.)|
+Island|Core Set 2021|310|C||Basic Land - Island|||({T}: Add {U}.)|
+Swamp|Core Set 2021|311|C||Basic Land - Swamp|||({T}: Add {B}.)|
+Mountain|Core Set 2021|312|C||Basic Land - Mountain|||({T}: Add {R}.)|
+Forest|Core Set 2021|313|C||Basic Land - Forest|||({T}: Add {G}.)|
+Containment Priest|Core Set 2021|314|R|{1}{W}|Creature - Human Cleric|2|2|Flash$If a nontoken creature would enter the battlefield and it wasn't cast, exile it instead.|
+Grim Tutor|Core Set 2021|315|M|{1}{B}{B}|Sorcery|||Search your library for a card, put that card into your hand, then shuffle your library. You lose 3 life.|
+Massacre Wurm|Core Set 2021|316|M|{3}{B}{B}{B}|Creature - Wurm|6|5|When Massacre Wurm enters the battlefield, creatures your opponents control get -2/-2 until end of turn.$Whenever a creature an opponent controls dies, that player loses 2 life.|
+Cultivate|Core Set 2021|317|R|{2}{G}|Sorcery|||Search your library for up to two basic land cards, reveal those cards, and put one onto the battlefield tapped and the other into your hand. Then shuffle your library.|
+Scavenging Ooze|Core Set 2021|318|R|{1}{G}|Creature - Ooze|2|2|{G}: Exile target card from a graveyard. If it was a creature card, put a +1/+1 counter on Scavenging Ooze and you gain 1 life.|
+Solemn Simulacrum|Core Set 2021|319|R|{4}|Artifact Creature - Golem|2|2|When Solemn Simulacrum enters the battlefield, you may search your library for a basic land card, put that card onto the battlefield tapped, then shuffle your library.$When Solemn Simulacrum dies, you may draw a card.|
 Basri, Devoted Paladin|Core Set 2021|320|M|{4}{W}{W}|Legendary Planeswalker - Basri|4|+1: Put a +1/+1 counter on up to one target creature. It gains vigilance until end of turn.$−1: Whenever a creature attacks this turn, put a +1/+1 counter on it.$−6: Creatures you control get +2/+2 and gain flying until end of turn.|
 Adherent of Hope|Core Set 2021|321|C|{1}{W}|Creature - Human Soldier|2|1|At the beginning of combat on your turn, if you control a Basri planeswalker, put a +1/+1 counter on Adherent of Hope.|
 Basri's Aegis|Core Set 2021|322|R|{2}{W}{W}|Sorcery|||Put a +1/+1 counter on each of up to two target creatures. You may search your library and/or graveyard for a card named Basri, Devoted Paladin, reveal it, and put it into your hand. If you search your library this way, shuffle it.|
@@ -37723,6 +37777,64 @@ Garruk, Savage Herald|Core Set 2021|336|M|{4}{G}{G}|Legendary Planeswalker - Gar
 Garruk's Warsteed|Core Set 2021|337|R|{3}{G}{G}|Creature - Rhino|3|5|Vigilance$When Garruk's Warsteed enters the battlefield, you may search your library and/or graveyard for a card named Garruk, Savage Herald, reveal it, and put it into your hand. If you search your library this way, shuffle it.|
 Predatory Wurm|Core Set 2021|338|U|{3}{G}|Creature - Wurm|4|4|Vigilance$Predatory Wurm gets +2/+2 as long as you control a Garruk planeswalker.|
 Wildwood Patrol|Core Set 2021|339|C|{2}{G}|Creature - Centaur Scout|4|2|Trample|
+Baneslayer Angel|Core Set 2021|340|M|{3}{W}{W}|Creature - Angel|5|5|Flying, first strike, lifelink, protection from Demons and from Dragons|
+Glorious Anthem|Core Set 2021|341|R|{1}{W}{W}|Enchantment|||Creatures you control get +1/+1.|
+Idol of Endurance|Core Set 2021|342|R|{2}{W}|Artifact|||When Idol of Endurance enters the battlefield, exile all creature cards with converted mana cost 3 or less from your graveyard until Idol of Endurance leaves the battlefield.${1}{W}, {T}: Until end of turn, you may cast a creature spell from among the cards exiled with Idol of Endurance without paying its mana cost.|
+Mangara, the Diplomat|Core Set 2021|343|M|{3}{W}|Legendary Creature - Human Cleric|2|4|Lifelink$Whenever an opponent attacks with creatures, if two or more of those creatures are attacking you and/or a planeswalker you control, draw a card.$Whenever an opponent casts their second spell each turn, draw a card.|
+Nine Lives|Core Set 2021|344|R|{1}{W}{W}|Enchantment|||Hexproof$If a source would deal damage to you, prevent that damage and put an incarnation counter on Nine Lives.$When there are nine or more incarnation counters on Nine Lives, exile it.$When Nine Lives leaves the battlefield, you lose the game.|
+Pack Leader|Core Set 2021|345|R|{1}{W}|Creature - Dog|2|2|Other Dogs you control get +1/+1.$Whenever Pack Leader attacks, prevent all combat damage that would be dealt this turn to Dogs you control.|
+Runed Halo|Core Set 2021|346|R|{W}{W}|Enchantment|||As Runed Halo enters the battlefield, choose a card name.$You have protection from the chosen card name.|
+Speaker of the Heavens|Core Set 2021|347|R|{W}|Creature - Human Cleric|1|1|Vigilance, lifelink${T}: Create a 4/4 white Angel creature token with flying. Activate this ability only if you have at least 7 more life than your starting life total and only any time you could cast a sorcery.|
+Barrin, Tolarian Archmage|Core Set 2021|348|R|{1}{U}{U}|Legendary Creature - Human Wizard|2|2|When Barrin, Tolarian Archmage enters the battlefield, return up to one other target creature or planeswalker to its owner's hand.$At the beginning of your end step, if a permanent was put into your hand from the battlefield this turn, draw a card.|
+Discontinuity|Core Set 2021|349|M|{3}{U}{U}{U}|Instant|||As long as it's your turn, this spell costs {2}{U}{U} less to cast.$End the turn.|
+Ghostly Pilferer|Core Set 2021|350|R|{1}{U}|Creature - Spirit Rogue|2|1|Whenever Ghostly Pilferer becomes untapped, you may pay {2}. If you do, draw a card.$Whenever an opponent casts a spell from anywhere other than their hand, draw a card.$Discard a card: Ghostly Pilferer can't be blocked this turn.|
+Pursued Whale|Core Set 2021|351|R|{5}{U}{U}|Creature - Whale|8|8|When Pursued Whale enters the battlefield, each opponent creates a 1/1 red Pirate creature token with "This creature can't block" and "Creatures you control attack each combat if able."$Spells your opponents cast that target Pursued Whale cost {3} more to cast.|
+See the Truth|Core Set 2021|352|R|{1}{U}|Sorcery|||Look at the top three cards of your library. Put one of those cards into your hand and the rest on the bottom of your library in any order. If this spell was cast from anywhere other than your hand, put each of those cards into your hand instead.|
+Shacklegeist|Core Set 2021|353|R|{1}{U}|Creature - Spirit|2|2|Flying$Shacklegeist can block only creatures with flying.$Tap two untapped Spirits you control: Tap target creature you don't control.|
+Stormwing Entity|Core Set 2021|354|R|{3}{U}{U}|Creature - Elemental|3|3|This spell costs {2}{U} less to cast if you've cast an instant or sorcery spell this turn.$Flying$Prowess$When Stormwing Entity enters the battlefield, scry 2.|
+Sublime Epiphany|Core Set 2021|355|R|{4}{U}{U}|Instant|||Choose one or more —$• Counter target spell.$• Counter target activated or triggered ability.$• Return target nonland permanent to its owner's hand.$• Create a token that's a copy of target creature you control.$• Target player draws a card.|
+Demonic Embrace|Core Set 2021|356|R|{1}{B}{B}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +3/+1, has flying, and is a Demon in addition to its other types.$You may cast Demonic Embrace from your graveyard by paying 3 life and discarding a card in addition to paying its other costs.|
+Hooded Blightfang|Core Set 2021|357|R|{2}{B}|Creature - Snake|1|4|Deathtouch$Whenever a creature you control with deathtouch attacks, each opponent loses 1 life and you gain 1 life.$Whenever a creature you control with deathtouch deals damage to a planeswalker, destroy that planeswalker.|
+Kaervek, the Spiteful|Core Set 2021|358|R|{2}{B}{B}|Legendary Creature - Human Warlock|3|2|Other creatures get -1/-1.|
+Necromentia|Core Set 2021|359|R|{1}{B}{B}|Sorcery|||Choose a card name other than a basic land card name. Search target opponent's graveyard, hand, and library for any number of cards with that name and exile them. That player shuffles their library, then creates a 2/2 black Zombie creature token for each card exiled from their hand this way.|
+Peer into the Abyss|Core Set 2021|360|R|{4}{B}{B}{B}|Sorcery|||Target player draws cards equal to half the number of cards in their library and loses half their life. Round up each time.|
+Thieves' Guild Enforcer|Core Set 2021|361|R|{B}|Creature - Human Rogue|1|1|Flash$Whenever Thieves' Guild Enforcer or another Rogue enters the battlefield under your control, each opponent mills two cards.$As long as an opponent has eight or more cards in their graveyard, Thieves' Guild Enforcer gets +2/+1 and has deathtouch.|
+Vito, Thorn of the Dusk Rose|Core Set 2021|362|R|{2}{B}|Legendary Creature - Vampire Cleric|1|3|Whenever you gain life, target opponent loses that much life.${3}{B}{B}: Creatures you control gain lifelink until end of turn.|
+Brash Taunter|Core Set 2021|363|R|{4}{R}|Creature - Goblin|1|1|Indestructible$Whenever Brash Taunter is dealt damage, it deals that much damage to target opponent.${2}{R}, {T}: Brash Taunter fights another target creature.|
+Conspicuous Snoop|Core Set 2021|364|R|{R}{R}|Creature - Goblin Rogue|2|2|Play with the top card of your library revealed.$You may cast Goblin spells from the top of your library.$As long as the top card of your library is a Goblin card, Conspicuous Snoop has all activated abilities of that card.|
+Double Vision|Core Set 2021|365|R|{3}{R}{R}|Enchantment|||Whenever you cast your first instant or sorcery spell each turn, copy that spell. You may choose new targets for the copy.|
+Fiery Emancipation|Core Set 2021|366|M|{3}{R}{R}{R}|Enchantment|||If a source you control would deal damage to a permanent or player, it deals triple that damage to that permanent or player instead.|
+Gadrak, the Crown-Scourge|Core Set 2021|367|R|{2}{R}|Legendary Creature - Dragon|5|4|Flying$Gadrak, the Crown-Scourge can't attack unless you control four or more artifacts.$At the beginning of your end step, create a Treasure token for each nontoken creature that died this turn.|
+Subira, Tulzidi Caravanner|Core Set 2021|368|R|{2}{R}|Legendary Creature - Human Shaman|2|3|Haste${1}: Another target creature with power 2 or less can't be blocked this turn.${1}{R}, {T}, Discard your hand: Until end of turn, whenever a creature you control with power 2 or less deals combat damage to a player, draw a card.|
+Terror of the Peaks|Core Set 2021|369|M|{3}{R}{R}|Creature - Dragon|5|4|Flying$Spells your opponents cast that target Terror of the Peaks cost an additional 3 life to cast.$Whenever another creature enters the battlefield under your control, Terror of the Peaks deals damage equal to that creature's power to any target.|
+Transmogrify|Core Set 2021|370|R|{3}{R}|Sorcery|||Exile target creature. That creature's controller reveals cards from the top of their library until they reveal a creature card. That player puts that card onto the battlefield, then shuffles the rest into their library.|
+Volcanic Salvo|Core Set 2021|371|R|{10}{R}{R}|Sorcery|||This spell costs {X} less to cast, where X is the total power of creatures you control.$Volcanic Salvo deals 6 damage to each of up to two target creatures and/or planeswalkers.|
+Azusa, Lost but Seeking|Core Set 2021|372|R|{2}{G}|Legendary Creature - Human Monk|1|2|You may play two additional lands on each of your turns.|
+Elder Gargaroth|Core Set 2021|373|M|{3}{G}{G}|Creature - Beast|6|6|Vigilance, reach, trample$Whenever Elder Gargaroth attacks or blocks, choose one —$• Create a 3/3 green Beast creature token.$• You gain 3 life.$• Draw a card.|
+Feline Sovereign|Core Set 2021|374|R|{2}{G}|Creature - Cat|2|3|Other Cats you control get +1/+1 and have protection from Dogs.$Whenever one or more Cats you control deal combat damage to a player, destroy up to one target artifact or enchantment that player controls.|
+Heroic Intervention|Core Set 2021|375|R|{1}{G}|Instant|||Permanents you control gain hexproof and indestructible until end of turn.|
+Jolrael, Mwonvuli Recluse|Core Set 2021|376|R|{1}{G}|Legendary Creature - Human Druid|1|2|Whenever you draw your second card each turn, create a 2/2 green Cat creature token.${4}{G}{G}: Until end of turn, creatures you control have base power and toughness X/X, where X is the number of cards in your hand.|
+Primal Might|Core Set 2021|377|R|{X}{G}|Sorcery|||Target creature you control gets +X/+X until end of turn. Then it fights up to one target creature you don't control.|
+Sporeweb Weaver|Core Set 2021|378|R|{2}{G}|Creature - Spider|1|4|Reach, hexproof from blue$Whenever Sporeweb Weaver is dealt damage, you gain 1 life and create a 1/1 green Saproling creature token.|
+Niambi, Esteemed Speaker|Core Set 2021|379|R|{W}{U}|Legendary Creature - Human Cleric|2|1|Flash$When Niambi, Esteemed Speaker enters the battlefield, you may return another target creature you control to its owner's hand. If you do, you gain life equal to that creature's converted mana cost.${1}{W}{U}, {T}, Discard a legendary card: Draw two cards.|
+Radha, Heart of Keld|Core Set 2021|380|R|{1}{R}{G}|Legendary Creature - Elf Warrior|3|3|As long as it's your turn, Radha, Heart of Keld has first strike.$You may look at the top card of your library any time, and you may play lands from the top of your library.${4}{R}{G}: Radha gets +X/+X until end of turn, where X is the number of lands you control.|
+Sanctum of All|Core Set 2021|381|R|{W}{U}{B}{R}{G}|Legendary Enchantment - Shrine|||At the beginning of your upkeep, you may search your library and/or graveyard for a Shrine card and put it onto the battlefield. If you search your library this way, shuffle it.$If an ability of another Shrine you control triggers while you control six or more Shrines, that ability triggers an additional time.|
+Chromatic Orrery|Core Set 2021|382|M|{7}|Legendary Artifact|||You may spend mana as though it were mana of any color.${T}: Add {C}{C}{C}{C}{C}.${5}, {T}: Draw a card for each color among permanents you control.|
+Mazemind Tome|Core Set 2021|383|R|{2}|Artifact|||{T}, Put a page counter on Mazemind Tome: Scry 1.${2}, {T}, Put a page counter on Mazemind Tome: Draw a card.$When there are four or more page counters on Mazemind Tome, exile it. If you do, you gain 4 life.|
+Sparkhunter Masticore|Core Set 2021|384|R|{3}|Artifact Creature - Masticore|3|4|As an additional cost to cast this spell, discard a card.$Protection from planeswalkers${1}: Sparkhunter Masticore deals 1 damage to target planeswalker.${3}: Sparkhunter Masticore gains indestructible until end of turn.|
+Animal Sanctuary|Core Set 2021|385|R||Land|||{T}: Add {C}.${2}, {T}: Put a +1/+1 counter on target Bird, Cat, Dog, Goat, Ox, or Snake.|
+Fabled Passage|Core Set 2021|386|R||Land|||{T}, Sacrifice Fabled Passage: Search your library for a basic land card, put it onto the battlefield tapped, then shuffle your library. Then if you control four or more lands, untap that land.|
+Temple of Epiphany|Core Set 2021|387|R||Land|||Temple of Epiphany enters the battlefield tapped.$When Temple of Epiphany enters the battlefield, scry 1.${T}: Add {U} or {R}.|
+Temple of Malady|Core Set 2021|388|R||Land|||Temple of Malady enters the battlefield tapped.$When Temple of Malady enters the battlefield, scry 1.${T}: Add {B} or {G}.|
+Temple of Mystery|Core Set 2021|389|R||Land|||Temple of Mystery enters the battlefield tapped.$When Temple of Mystery enters the battlefield, scry 1.${T}: Add {G} or {U}.|
+Temple of Silence|Core Set 2021|390|R||Land|||Temple of Silence enters the battlefield tapped.$When Temple of Silence enters the battlefield, scry 1.${T}: Add {W} or {B}.|
+Temple of Triumph|Core Set 2021|391|R||Land|||Temple of Triumph enters the battlefield tapped.$When Temple of Triumph enters the battlefield, scry 1.${T}: Add {R} or {W}.|
+Pack Leader|Core Set 2021|392|R|{1}{W}|Creature - Dog|2|2|Other Dogs you control get +1/+1.$Whenever Pack Leader attacks, prevent all combat damage that would be dealt this turn to Dogs you control.|
+Selfless Savior|Core Set 2021|393|U|{W}|Creature - Dog|1|1|Sacrifice Selfless Savior: Another target creature you control gains indestructible until end of turn.|
+Frantic Inventory|Core Set 2021|394|C|{1}{U}|Instant|||Draw a card, then draw cards equal to the number of cards named Frantic Inventory in your graveyard.|
+Eliminate|Core Set 2021|395|U|{1}{B}|Instant|||Destroy target creature or planeswalker with converted mana cost 3 or less.|
+Heartfire Immolator|Core Set 2021|396|U|{1}{R}|Creature - Human Wizard|2|2|Prowess${R}, Sacrifice Heartfire Immolator: It deals damage equal to its power to target creature or planeswalker.|
+Llanowar Visionary|Core Set 2021|397|C|{2}{G}|Creature - Elf Druid|2|2|When Llanowar Visionary enters the battlefield, draw a card.${T}: Add {G}.|
 Blessed Sanctuary|Jumpstart|1|R|{3}{W}{W}|Enchantment|||Prevent all noncombat damage that would be dealt to you and creatures you control.$Whenever a nontoken creature enters the battlefield under your control, create a 2/2 white Unicorn creature token.|
 Brightmare|Jumpstart|2|U|{2}{W}|Creature - Unicorn|2|3|When Brightmare enters the battlefield, tap up to one target creature. You gain life equal to that creature's power.|
 Emiel the Blessed|Jumpstart|3|M|{2}{W}{W}|Legendary Creature - Unicorn|4|4|{3}: Exile another target creature you control, then return it to the battlefield under its owner's control.$Whenever another creature enters the battlefield under your control, you may pay {G/W}. If you do, put a +1/+1 counter on it. If it's a Unicorn, put two +1/+1 counters on it instead.|
@@ -37761,10 +37873,45 @@ Thriving Heath|Jumpstart|35|C||Land|||Thriving Heath enters the battlefield tapp
 Thriving Isle|Jumpstart|36|C||Land|||Thriving Isle enters the battlefield tapped.$As Thriving Isle enters the battlefield, choose a color other than blue.${T}: Add {U} or one mana of the chosen color.|
 Thriving Moor|Jumpstart|37|C||Land|||Thriving Moor enters the battlefield tapped.$As Thriving Moor enters the battlefield, choose a color other than black.${T}: Add {B} or one mana of the chosen color.|
 Plains|Jumpstart|38|C||Basic Land - Plains|||({T}: Add {W}.)|
+Plains|Jumpstart|39|C||Basic Land - Plains|||({T}: Add {W}.)|
+Plains|Jumpstart|40|C||Basic Land - Plains|||({T}: Add {W}.)|
+Plains|Jumpstart|41|C||Basic Land - Plains|||({T}: Add {W}.)|
+Plains|Jumpstart|42|C||Basic Land - Plains|||({T}: Add {W}.)|
+Plains|Jumpstart|43|C||Basic Land - Plains|||({T}: Add {W}.)|
+Plains|Jumpstart|44|C||Basic Land - Plains|||({T}: Add {W}.)|
+Plains|Jumpstart|45|C||Basic Land - Plains|||({T}: Add {W}.)|
 Island|Jumpstart|46|C||Basic Land - Island|||({T}: Add {U}.)|
+Island|Jumpstart|47|C||Basic Land - Island|||({T}: Add {U}.)|
+Island|Jumpstart|48|C||Basic Land - Island|||({T}: Add {U}.)|
+Island|Jumpstart|49|C||Basic Land - Island|||({T}: Add {U}.)|
+Island|Jumpstart|50|C||Basic Land - Island|||({T}: Add {U}.)|
+Island|Jumpstart|51|C||Basic Land - Island|||({T}: Add {U}.)|
+Island|Jumpstart|52|C||Basic Land - Island|||({T}: Add {U}.)|
+Island|Jumpstart|53|C||Basic Land - Island|||({T}: Add {U}.)|
 Swamp|Jumpstart|54|C||Basic Land - Swamp|||({T}: Add {B}.)|
+Swamp|Jumpstart|55|C||Basic Land - Swamp|||({T}: Add {B}.)|
+Swamp|Jumpstart|56|C||Basic Land - Swamp|||({T}: Add {B}.)|
+Swamp|Jumpstart|57|C||Basic Land - Swamp|||({T}: Add {B}.)|
+Swamp|Jumpstart|58|C||Basic Land - Swamp|||({T}: Add {B}.)|
+Swamp|Jumpstart|59|C||Basic Land - Swamp|||({T}: Add {B}.)|
+Swamp|Jumpstart|60|C||Basic Land - Swamp|||({T}: Add {B}.)|
+Swamp|Jumpstart|61|C||Basic Land - Swamp|||({T}: Add {B}.)|
 Mountain|Jumpstart|62|C||Basic Land - Mountain|||({T}: Add {R}.)|
+Mountain|Jumpstart|63|C||Basic Land - Mountain|||({T}: Add {R}.)|
+Mountain|Jumpstart|64|C||Basic Land - Mountain|||({T}: Add {R}.)|
+Mountain|Jumpstart|65|C||Basic Land - Mountain|||({T}: Add {R}.)|
+Mountain|Jumpstart|66|C||Basic Land - Mountain|||({T}: Add {R}.)|
+Mountain|Jumpstart|67|C||Basic Land - Mountain|||({T}: Add {R}.)|
+Mountain|Jumpstart|68|C||Basic Land - Mountain|||({T}: Add {R}.)|
+Mountain|Jumpstart|69|C||Basic Land - Mountain|||({T}: Add {R}.)|
 Forest|Jumpstart|70|C||Basic Land - Forest|||({T}: Add {G}.)|
+Forest|Jumpstart|71|C||Basic Land - Forest|||({T}: Add {G}.)|
+Forest|Jumpstart|72|C||Basic Land - Forest|||({T}: Add {G}.)|
+Forest|Jumpstart|73|C||Basic Land - Forest|||({T}: Add {G}.)|
+Forest|Jumpstart|74|C||Basic Land - Forest|||({T}: Add {G}.)|
+Forest|Jumpstart|75|C||Basic Land - Forest|||({T}: Add {G}.)|
+Forest|Jumpstart|76|C||Basic Land - Forest|||({T}: Add {G}.)|
+Forest|Jumpstart|77|C||Basic Land - Forest|||({T}: Add {G}.)|
 Terramorphic Expanse|Jumpstart|78|C||Land|||{T}, Sacrifice Terramorphic Expanse: Search your library for a basic land card, put it onto the battlefield tapped, then shuffle your library.|
 Aegis of the Heavens|Jumpstart|79|U|{1}{W}|Instant|||Target creature gets +1/+7 until end of turn.|
 Aerial Assault|Jumpstart|80|C|{2}{W}|Sorcery|||Destroy target tapped creature. You gain 1 life for each creature you control with flying.|

From 09bc2575d8038d7b8dfcf40de5a447bd0ce76817 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Tue, 30 Jun 2020 08:27:29 +0400
Subject: [PATCH 569/586] Cost increasing effects - refactor, removed redundant
 custom effects (related to #6684 and #6698);

---
 Mage.Sets/src/mage/cards/a/AccursedWitch.java |  56 +-----
 .../mage/cards/a/AnimarSoulOfElements.java    |   6 +-
 Mage.Sets/src/mage/cards/a/ArcaneMelee.java   |  55 +-----
 Mage.Sets/src/mage/cards/a/AvatarOfFury.java  |  62 +++----
 Mage.Sets/src/mage/cards/a/AvatarOfHope.java  |  55 +++---
 .../src/mage/cards/b/BorealElemental.java     |  62 +------
 .../mage/cards/c/CallapheBelovedOfTheSea.java |  62 +------
 .../src/mage/cards/e/ElderwoodScion.java      | 119 ++-----------
 Mage.Sets/src/mage/cards/g/Grapeshot.java     |   9 +-
 Mage.Sets/src/mage/cards/i/IcefallRegent.java |  56 +-----
 .../src/mage/cards/j/JubilantSkybonder.java   |  61 +------
 Mage.Sets/src/mage/cards/p/PursuedWhale.java  |  54 +-----
 .../src/mage/cards/s/SphinxOfNewPrahv.java    |  65 +------
 .../mage/cards/s/SyrElenoraTheDiscerning.java |  57 +-----
 .../modification/CostModificationTest.java    | 109 +++++++++++-
 .../java/org/mage/test/player/TestPlayer.java |   4 +
 .../main/java/mage/abilities/AbilityImpl.java |   2 +-
 .../java/mage/abilities/effects/Effects.java  |   3 +-
 ...ostModificationThatTargetSourceEffect.java | 166 ++++++++++++++++++
 .../cost/SpellsCostReductionAllEffect.java    |  10 +-
 .../SpellsCostReductionControllerEffect.java  |  10 +-
 .../mage/abilities/hint/ConditionHint.java    |   9 +-
 Mage/src/main/java/mage/util/CardUtil.java    |   8 +
 23 files changed, 442 insertions(+), 658 deletions(-)
 create mode 100644 Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostModificationThatTargetSourceEffect.java

diff --git a/Mage.Sets/src/mage/cards/a/AccursedWitch.java b/Mage.Sets/src/mage/cards/a/AccursedWitch.java
index 497724b2b6..0bc6e8d72a 100644
--- a/Mage.Sets/src/mage/cards/a/AccursedWitch.java
+++ b/Mage.Sets/src/mage/cards/a/AccursedWitch.java
@@ -1,25 +1,20 @@
-
 package mage.cards.a;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.Mode;
-import mage.abilities.SpellAbility;
 import mage.abilities.common.DiesSourceTriggeredAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.OneShotEffect;
-import mage.abilities.effects.common.cost.CostModificationEffectImpl;
+import mage.abilities.effects.common.cost.SpellsCostModificationThatTargetSourceEffect;
 import mage.abilities.keyword.TransformAbility;
 import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.*;
+import mage.filter.FilterCard;
 import mage.game.Game;
-import mage.game.permanent.Permanent;
 import mage.players.Player;
-import mage.target.Target;
 import mage.target.common.TargetOpponent;
-import mage.util.CardUtil;
 
 import java.util.UUID;
 
@@ -39,7 +34,10 @@ public final class AccursedWitch extends CardImpl {
         this.secondSideCardClazz = mage.cards.i.InfectiousCurse.class;
 
         // Spells your opponents cast that target Accursed Witch cost {1} less to cast.
-        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new AccursedWitchSpellsCostReductionEffect()));
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
+                new SpellsCostModificationThatTargetSourceEffect(-1, new FilterCard("Spells"), TargetController.OPPONENT))
+        );
+
         // When Accursed Witch dies, return it to the battlefield transformed under your control attached to target opponent.
         this.addAbility(new TransformAbility());
         Ability ability = new DiesSourceTriggeredAbility(new AccursedWitchReturnTransformedEffect());
@@ -93,45 +91,3 @@ class AccursedWitchReturnTransformedEffect extends OneShotEffect {
         return true;
     }
 }
-
-class AccursedWitchSpellsCostReductionEffect extends CostModificationEffectImpl {
-
-    AccursedWitchSpellsCostReductionEffect() {
-        super(Duration.WhileOnBattlefield, Outcome.Detriment, CostModificationType.REDUCE_COST);
-        this.staticText = "Spells your opponents cast that target {this} cost {1} less to cast";
-    }
-
-    private AccursedWitchSpellsCostReductionEffect(AccursedWitchSpellsCostReductionEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        CardUtil.reduceCost(abilityToModify, 1);
-        return true;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        if (!(abilityToModify instanceof SpellAbility) || !game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) {
-            return false;
-        }
-        for (UUID modeId : abilityToModify.getModes().getSelectedModes()) {
-            Mode mode = abilityToModify.getModes().get(modeId);
-            for (Target target : mode.getTargets()) {
-                for (UUID targetUUID : target.getTargets()) {
-                    Permanent permanent = game.getPermanent(targetUUID);
-                    if (permanent != null && permanent.getId().equals(source.getSourceId())) {
-                        return true;
-                    }
-                }
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public AccursedWitchSpellsCostReductionEffect copy() {
-        return new AccursedWitchSpellsCostReductionEffect(this);
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/a/AnimarSoulOfElements.java b/Mage.Sets/src/mage/cards/a/AnimarSoulOfElements.java
index e47b01a468..1ac2052194 100644
--- a/Mage.Sets/src/mage/cards/a/AnimarSoulOfElements.java
+++ b/Mage.Sets/src/mage/cards/a/AnimarSoulOfElements.java
@@ -82,9 +82,9 @@ class AnimarCostReductionEffect extends CostModificationEffectImpl {
     public boolean applies(Ability abilityToModify, Ability source, Game game) {
         if (abilityToModify instanceof SpellAbility) {
             if (abilityToModify.isControlledBy(source.getControllerId())) {
-                Card spell = ((SpellAbility) abilityToModify).getCharacteristics(game);
-                if (spell != null) {
-                    return spell.isCreature();
+                Card card = ((SpellAbility) abilityToModify).getCharacteristics(game);
+                if (card != null) {
+                    return card.isCreature();
                 }
             }
         }
diff --git a/Mage.Sets/src/mage/cards/a/ArcaneMelee.java b/Mage.Sets/src/mage/cards/a/ArcaneMelee.java
index 9e45b06db7..2657238404 100644
--- a/Mage.Sets/src/mage/cards/a/ArcaneMelee.java
+++ b/Mage.Sets/src/mage/cards/a/ArcaneMelee.java
@@ -1,17 +1,14 @@
-
 package mage.cards.a;
 
-import java.util.UUID;
-import mage.abilities.Ability;
-import mage.abilities.SpellAbility;
 import mage.abilities.common.SimpleStaticAbility;
-import mage.abilities.effects.common.cost.CostModificationEffectImpl;
-import mage.cards.Card;
+import mage.abilities.effects.common.cost.SpellsCostReductionAllEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.*;
-import mage.game.Game;
-import mage.util.CardUtil;
+import mage.constants.CardType;
+import mage.constants.Zone;
+import mage.filter.common.FilterInstantOrSorceryCard;
+
+import java.util.UUID;
 
 /**
  * @author noxx
@@ -22,7 +19,9 @@ public final class ArcaneMelee extends CardImpl {
         super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{U}");
 
         // Instant and sorcery spells cost {2} less to cast.
-        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ArcaneMeleeCostReductionEffect()));
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
+                new SpellsCostReductionAllEffect(new FilterInstantOrSorceryCard("Instant and sorcery spells"), 2))
+        );
     }
 
     public ArcaneMelee(final ArcaneMelee card) {
@@ -34,39 +33,3 @@ public final class ArcaneMelee extends CardImpl {
         return new ArcaneMelee(this);
     }
 }
-
-class ArcaneMeleeCostReductionEffect extends CostModificationEffectImpl {
-
-    ArcaneMeleeCostReductionEffect() {
-        super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST);
-        staticText = "Instant and sorcery spells cost {2} less to cast";
-    }
-
-    ArcaneMeleeCostReductionEffect(ArcaneMeleeCostReductionEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        SpellAbility spellAbility = (SpellAbility) abilityToModify;
-        CardUtil.adjustCost(spellAbility, 2);
-        return true;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        if (abilityToModify instanceof SpellAbility) {
-            Card sourceCard = game.getCard((abilityToModify).getSourceId());
-            if (sourceCard != null && (sourceCard.isInstant() || sourceCard.isSorcery())) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public ArcaneMeleeCostReductionEffect copy() {
-        return new ArcaneMeleeCostReductionEffect(this);
-    }
-
-}
diff --git a/Mage.Sets/src/mage/cards/a/AvatarOfFury.java b/Mage.Sets/src/mage/cards/a/AvatarOfFury.java
index 9153c0a4f9..6489605608 100644
--- a/Mage.Sets/src/mage/cards/a/AvatarOfFury.java
+++ b/Mage.Sets/src/mage/cards/a/AvatarOfFury.java
@@ -1,33 +1,41 @@
 package mage.cards.a;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.SpellAbility;
 import mage.abilities.common.SimpleActivatedAbility;
 import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.condition.Condition;
 import mage.abilities.costs.mana.ManaCostsImpl;
 import mage.abilities.effects.common.continuous.BoostSourceEffect;
-import mage.abilities.effects.common.cost.CostModificationEffectImpl;
+import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect;
+import mage.abilities.hint.ConditionHint;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.CardType;
-import mage.constants.CostModificationType;
 import mage.constants.Duration;
-import mage.constants.Outcome;
 import mage.constants.SubType;
 import mage.constants.Zone;
+import mage.filter.FilterPermanent;
 import mage.filter.StaticFilters;
+import mage.filter.predicate.Predicates;
 import mage.game.Game;
-import mage.util.CardUtil;
+
+import java.util.UUID;
 
 /**
- *
  * @author LevelX2
  */
 public final class AvatarOfFury extends CardImpl {
 
+    private static final FilterPermanent filter = new FilterPermanent("artifact, creature, or enchantment");
+
+    static {
+        filter.add(Predicates.or(CardType.ARTIFACT.getPredicate(),
+                CardType.CREATURE.getPredicate(),
+                CardType.ENCHANTMENT.getPredicate()));
+    }
+
     public AvatarOfFury(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{R}{R}");
         this.subtype.add(SubType.AVATAR);
@@ -36,9 +44,14 @@ public final class AvatarOfFury extends CardImpl {
         this.toughness = new MageInt(6);
 
         // If an opponent controls seven or more lands, Avatar of Fury costs {6} less to cast.
-        this.addAbility(new SimpleStaticAbility(Zone.ALL, new AvatarOfFuryAdjustingCostsEffect()));
+        this.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(6, AvatarOfFuryCondition.instance)
+                .setText("if an opponent controls seven or more lands, Avatar of Fury costs {6} less to cast"))
+                .addHint(new ConditionHint(AvatarOfFuryCondition.instance, "Opponent controls seven or more lands"))
+        );
+
         // Flying
         this.addAbility(FlyingAbility.getInstance());
+
         // {R}: Avatar of Fury gets +1/+0 until end of turn.
         this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, 0, Duration.EndOfTurn), new ManaCostsImpl("{R}")));
     }
@@ -53,39 +66,22 @@ public final class AvatarOfFury extends CardImpl {
     }
 }
 
-class AvatarOfFuryAdjustingCostsEffect extends CostModificationEffectImpl {
+enum AvatarOfFuryCondition implements Condition {
 
-    AvatarOfFuryAdjustingCostsEffect() {
-        super(Duration.EndOfGame, Outcome.Benefit, CostModificationType.REDUCE_COST);
-        staticText = "If an opponent controls seven or more lands, {this} costs {6} less to cast";
-    }
-
-    AvatarOfFuryAdjustingCostsEffect(AvatarOfFuryAdjustingCostsEffect effect) {
-        super(effect);
-    }
+    instance;
 
     @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        CardUtil.reduceCost(abilityToModify, 6);
-        return true;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        if (abilityToModify.getSourceId().equals(source.getSourceId())
-                && (abilityToModify instanceof SpellAbility)) {
-            for (UUID playerId : game.getOpponents(abilityToModify.getControllerId())) {
-                if (game.getBattlefield().countAll(StaticFilters.FILTER_LAND, playerId, game) > 6) {
-                    return true;
-                }
+    public boolean apply(Game game, Ability source) {
+        for (UUID playerId : game.getOpponents(source.getControllerId())) {
+            if (game.getBattlefield().countAll(StaticFilters.FILTER_LAND, playerId, game) > 6) {
+                return true;
             }
         }
         return false;
     }
 
     @Override
-    public AvatarOfFuryAdjustingCostsEffect copy() {
-        return new AvatarOfFuryAdjustingCostsEffect(this);
+    public String toString() {
+        return "an opponent controls seven or more lands";
     }
-
 }
diff --git a/Mage.Sets/src/mage/cards/a/AvatarOfHope.java b/Mage.Sets/src/mage/cards/a/AvatarOfHope.java
index d1e82950fe..08957746ee 100644
--- a/Mage.Sets/src/mage/cards/a/AvatarOfHope.java
+++ b/Mage.Sets/src/mage/cards/a/AvatarOfHope.java
@@ -1,22 +1,24 @@
 package mage.cards.a;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.SpellAbility;
 import mage.abilities.common.SimpleStaticAbility;
+import mage.abilities.condition.Condition;
 import mage.abilities.effects.common.combat.CanBlockAdditionalCreatureEffect;
-import mage.abilities.effects.common.cost.CostModificationEffectImpl;
+import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect;
+import mage.abilities.hint.ConditionHint;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.*;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.Zone;
 import mage.game.Game;
 import mage.players.Player;
-import mage.util.CardUtil;
+
+import java.util.UUID;
 
 /**
- *
  * @author Plopman
  */
 public final class AvatarOfHope extends CardImpl {
@@ -29,9 +31,14 @@ public final class AvatarOfHope extends CardImpl {
         this.toughness = new MageInt(9);
 
         // If you have 3 or less life, Avatar of Hope costs {6} less to cast.
-        this.addAbility(new SimpleStaticAbility(Zone.ALL, new AvatarOfHopeAdjustingCostsEffect()));
+        this.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(6, AvatarOfHopeCondition.instance)
+                .setText("if you have 3 or less life, Avatar of Hope costs {6} less to cast"))
+                .addHint(new ConditionHint(AvatarOfHopeCondition.instance))
+        );
+
         // Flying
         this.addAbility(FlyingAbility.getInstance());
+
         // Avatar of Hope can block any number of creatures.
         this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CanBlockAdditionalCreatureEffect(0)));
     }
@@ -46,39 +53,21 @@ public final class AvatarOfHope extends CardImpl {
     }
 }
 
-class AvatarOfHopeAdjustingCostsEffect extends CostModificationEffectImpl {
+enum AvatarOfHopeCondition implements Condition {
 
-    AvatarOfHopeAdjustingCostsEffect() {
-        super(Duration.EndOfGame, Outcome.Benefit, CostModificationType.REDUCE_COST);
-        staticText = "If you have 3 or less life, {this} costs {6} less to cast";
-    }
-
-    AvatarOfHopeAdjustingCostsEffect(AvatarOfHopeAdjustingCostsEffect effect) {
-        super(effect);
-    }
+    instance;
 
     @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        CardUtil.reduceCost(abilityToModify, 6);
-        return true;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        if (abilityToModify.getSourceId().equals(source.getSourceId())
-                && (abilityToModify instanceof SpellAbility)) {
-            Player player = game.getPlayer(abilityToModify.getControllerId());
-            if (player != null && player.getLife() < 4) {
-                return true;
-            }
+    public boolean apply(Game game, Ability source) {
+        Player player = game.getPlayer(source.getControllerId());
+        if (player != null && player.getLife() <= 3) {
+            return true;
         }
-
         return false;
     }
 
     @Override
-    public AvatarOfHopeAdjustingCostsEffect copy() {
-        return new AvatarOfHopeAdjustingCostsEffect(this);
+    public String toString() {
+        return "you have 3 or less life";
     }
-
 }
diff --git a/Mage.Sets/src/mage/cards/b/BorealElemental.java b/Mage.Sets/src/mage/cards/b/BorealElemental.java
index bd0f8fcada..df54bda0db 100644
--- a/Mage.Sets/src/mage/cards/b/BorealElemental.java
+++ b/Mage.Sets/src/mage/cards/b/BorealElemental.java
@@ -1,20 +1,17 @@
 package mage.cards.b;
 
 import mage.MageInt;
-import mage.abilities.Ability;
-import mage.abilities.Mode;
-import mage.abilities.SpellAbility;
 import mage.abilities.common.SimpleStaticAbility;
-import mage.abilities.effects.common.cost.CostModificationEffectImpl;
+import mage.abilities.effects.common.cost.SpellsCostModificationThatTargetSourceEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.*;
-import mage.game.Game;
-import mage.target.Target;
-import mage.util.CardUtil;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.TargetController;
+import mage.constants.Zone;
+import mage.filter.FilterCard;
 
-import java.util.Collection;
 import java.util.UUID;
 
 /**
@@ -33,7 +30,9 @@ public final class BorealElemental extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
 
         // Spells your opponents cast that target Boreal Elemental cost {2} more to cast.
-        this.addAbility(new SimpleStaticAbility(new BorealElementalCostIncreaseEffect()));
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
+                new SpellsCostModificationThatTargetSourceEffect(2, new FilterCard("Spells"), TargetController.OPPONENT))
+        );
     }
 
     private BorealElemental(final BorealElemental card) {
@@ -45,46 +44,3 @@ public final class BorealElemental extends CardImpl {
         return new BorealElemental(this);
     }
 }
-
-class BorealElementalCostIncreaseEffect extends CostModificationEffectImpl {
-
-    BorealElementalCostIncreaseEffect() {
-        super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST);
-        staticText = "Spells your opponents cast that target {this} cost {2} more to cast";
-    }
-
-    private BorealElementalCostIncreaseEffect(BorealElementalCostIncreaseEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        SpellAbility spellAbility = (SpellAbility) abilityToModify;
-        CardUtil.adjustCost(spellAbility, -2);
-        return true;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        if (!(abilityToModify instanceof SpellAbility)
-                || !game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) {
-            return false;
-        }
-        return abilityToModify
-                .getModes()
-                .getSelectedModes()
-                .stream()
-                .map(uuid -> abilityToModify.getModes().get(uuid))
-                .map(Mode::getTargets)
-                .flatMap(Collection::stream)
-                .map(Target::getTargets)
-                .flatMap(Collection::stream)
-                .anyMatch(uuid -> uuid.equals(source.getSourceId()));
-    }
-
-    @Override
-    public BorealElementalCostIncreaseEffect copy() {
-        return new BorealElementalCostIncreaseEffect(this);
-    }
-
-}
diff --git a/Mage.Sets/src/mage/cards/c/CallapheBelovedOfTheSea.java b/Mage.Sets/src/mage/cards/c/CallapheBelovedOfTheSea.java
index 4e245e095f..1baa9de971 100644
--- a/Mage.Sets/src/mage/cards/c/CallapheBelovedOfTheSea.java
+++ b/Mage.Sets/src/mage/cards/c/CallapheBelovedOfTheSea.java
@@ -2,23 +2,18 @@ package mage.cards.c;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.Mode;
-import mage.abilities.SpellAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.dynamicvalue.common.DevotionCount;
 import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
 import mage.abilities.effects.common.continuous.SetPowerSourceEffect;
-import mage.abilities.effects.common.cost.CostModificationEffectImpl;
+import mage.abilities.effects.common.cost.SpellsCostModificationThatTargetSourceEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.*;
+import mage.filter.FilterCard;
 import mage.filter.FilterPermanent;
 import mage.filter.predicate.Predicates;
-import mage.game.Game;
-import mage.target.Target;
-import mage.util.CardUtil;
 
-import java.util.Collection;
 import java.util.UUID;
 
 /**
@@ -51,11 +46,12 @@ public final class CallapheBelovedOfTheSea extends CardImpl {
         ).addHint(DevotionCount.U.getHint()));
 
         // Creatures and enchantments you control have "Spells your opponents cast that target this permanent cost {1} more to cast".
-        this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect(
-                new SimpleStaticAbility(
-                        new CallapheBelovedOfTheSeaEffect()
-                ), Duration.WhileOnBattlefield, filter)
-                .withForceQuotes()
+        Ability gainAbility = new SimpleStaticAbility(Zone.BATTLEFIELD,
+                new SpellsCostModificationThatTargetSourceEffect(1, new FilterCard("Spells"), TargetController.OPPONENT)
+                        .withTargetName("this permanent")
+        );
+        this.addAbility(new SimpleStaticAbility(
+                new GainAbilityControlledEffect(gainAbility, Duration.WhileOnBattlefield, filter).withForceQuotes()
         ));
     }
 
@@ -68,45 +64,3 @@ public final class CallapheBelovedOfTheSea extends CardImpl {
         return new CallapheBelovedOfTheSea(this);
     }
 }
-
-class CallapheBelovedOfTheSeaEffect extends CostModificationEffectImpl {
-
-    CallapheBelovedOfTheSeaEffect() {
-        super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST);
-        staticText = "Spells your opponents cast that target this permanent cost {1} more to cast";
-    }
-
-    private CallapheBelovedOfTheSeaEffect(CallapheBelovedOfTheSeaEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        SpellAbility spellAbility = (SpellAbility) abilityToModify;
-        CardUtil.adjustCost(spellAbility, -1);
-        return true;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        if (!(abilityToModify instanceof SpellAbility)
-                || !game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) {
-            return false;
-        }
-        return abilityToModify
-                .getModes()
-                .getSelectedModes()
-                .stream()
-                .map(uuid -> abilityToModify.getModes().get(uuid))
-                .map(Mode::getTargets)
-                .flatMap(Collection::stream)
-                .map(Target::getTargets)
-                .flatMap(Collection::stream)
-                .anyMatch(uuid -> uuid.equals(source.getSourceId()));
-    }
-
-    @Override
-    public CallapheBelovedOfTheSeaEffect copy() {
-        return new CallapheBelovedOfTheSeaEffect(this);
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/e/ElderwoodScion.java b/Mage.Sets/src/mage/cards/e/ElderwoodScion.java
index 778ffb55e5..eceff73b4f 100644
--- a/Mage.Sets/src/mage/cards/e/ElderwoodScion.java
+++ b/Mage.Sets/src/mage/cards/e/ElderwoodScion.java
@@ -1,24 +1,21 @@
-
 package mage.cards.e;
 
-import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.Ability;
-import mage.abilities.Mode;
-import mage.abilities.SpellAbility;
 import mage.abilities.common.SimpleStaticAbility;
-import mage.abilities.effects.common.cost.CostModificationEffectImpl;
+import mage.abilities.effects.common.cost.SpellsCostModificationThatTargetSourceEffect;
 import mage.abilities.keyword.LifelinkAbility;
 import mage.abilities.keyword.TrampleAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.*;
-import mage.game.Game;
-import mage.target.Target;
-import mage.util.CardUtil;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.TargetController;
+import mage.constants.Zone;
+import mage.filter.FilterCard;
+
+import java.util.UUID;
 
 /**
- *
  * @author LevelX2
  */
 public final class ElderwoodScion extends CardImpl {
@@ -32,12 +29,19 @@ public final class ElderwoodScion extends CardImpl {
 
         // Trample
         this.addAbility(TrampleAbility.getInstance());
+
         // Lifelink
         this.addAbility(LifelinkAbility.getInstance());
+
         // Spells you cast that target Elderwood Scion cost {2} less to cast.
-        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ElderwoodScionCostReductionEffect()));
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
+                new SpellsCostModificationThatTargetSourceEffect(-2, new FilterCard("Spells"), TargetController.YOU))
+        );
+
         // Spells your opponents cast that target Elderwood Scion cost {2} more to cast.
-        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ElderwoodScionCostReductionEffect2()));
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
+                new SpellsCostModificationThatTargetSourceEffect(2, new FilterCard("Spells"), TargetController.OPPONENT))
+        );
     }
 
     public ElderwoodScion(final ElderwoodScion card) {
@@ -49,92 +53,3 @@ public final class ElderwoodScion extends CardImpl {
         return new ElderwoodScion(this);
     }
 }
-
-class ElderwoodScionCostReductionEffect extends CostModificationEffectImpl {
-
-    private static final String effectText = "Spells you cast that target {this} cost {2} less to cast";
-
-    ElderwoodScionCostReductionEffect() {
-        super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST);
-        staticText = effectText;
-    }
-
-    ElderwoodScionCostReductionEffect(ElderwoodScionCostReductionEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        SpellAbility spellAbility = (SpellAbility) abilityToModify;
-        CardUtil.adjustCost(spellAbility, 2);
-        return true;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        if (abilityToModify instanceof SpellAbility) {
-            if (abilityToModify.isControlledBy(source.getControllerId())) {
-                for (UUID modeId : abilityToModify.getModes().getSelectedModes()) {
-                    Mode mode = abilityToModify.getModes().get(modeId);
-                    for (Target target : mode.getTargets()) {
-                        for (UUID targetUUID : target.getTargets()) {
-                            if (targetUUID.equals(source.getSourceId())) {
-                                return true;
-                            }
-                        }
-                    }
-                }
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public ElderwoodScionCostReductionEffect copy() {
-        return new ElderwoodScionCostReductionEffect(this);
-    }
-
-}
-
-class ElderwoodScionCostReductionEffect2 extends CostModificationEffectImpl {
-
-    private static final String effectText = "Spells your opponents cast that target {this} cost {2} more to cast";
-
-    ElderwoodScionCostReductionEffect2() {
-        super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST);
-        staticText = effectText;
-    }
-
-    ElderwoodScionCostReductionEffect2(ElderwoodScionCostReductionEffect2 effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        SpellAbility spellAbility = (SpellAbility) abilityToModify;
-        CardUtil.increaseCost(spellAbility, 2);
-        return true;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        if (abilityToModify instanceof SpellAbility) {
-            if (game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) {
-                for (Target target : abilityToModify.getTargets()) {
-                    for (UUID targetUUID : target.getTargets()) {
-                        if (targetUUID.equals(source.getSourceId())) {
-                            return true;
-                        }
-                    }
-                }
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public ElderwoodScionCostReductionEffect2 copy() {
-        return new ElderwoodScionCostReductionEffect2(this);
-    }
-
-}
diff --git a/Mage.Sets/src/mage/cards/g/Grapeshot.java b/Mage.Sets/src/mage/cards/g/Grapeshot.java
index 82a6a8b5c6..5101aaa10f 100644
--- a/Mage.Sets/src/mage/cards/g/Grapeshot.java
+++ b/Mage.Sets/src/mage/cards/g/Grapeshot.java
@@ -1,7 +1,5 @@
-
 package mage.cards.g;
 
-import java.util.UUID;
 import mage.abilities.effects.common.DamageTargetEffect;
 import mage.abilities.keyword.StormAbility;
 import mage.cards.CardImpl;
@@ -9,19 +7,20 @@ import mage.cards.CardSetInfo;
 import mage.constants.CardType;
 import mage.target.common.TargetAnyTarget;
 
+import java.util.UUID;
+
 /**
- *
  * @author Plopman
  */
 public final class Grapeshot extends CardImpl {
 
     public Grapeshot(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{R}");
-
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{R}");
 
         // Grapeshot deals 1 damage to any target.
         this.getSpellAbility().addTarget(new TargetAnyTarget());
         this.getSpellAbility().addEffect(new DamageTargetEffect(1));
+
         // Storm
         this.addAbility(new StormAbility());
     }
diff --git a/Mage.Sets/src/mage/cards/i/IcefallRegent.java b/Mage.Sets/src/mage/cards/i/IcefallRegent.java
index 71e5d8e267..582882559a 100644
--- a/Mage.Sets/src/mage/cards/i/IcefallRegent.java
+++ b/Mage.Sets/src/mage/cards/i/IcefallRegent.java
@@ -2,17 +2,16 @@ package mage.cards.i;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.Mode;
-import mage.abilities.SpellAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
 import mage.abilities.effects.common.TapTargetEffect;
-import mage.abilities.effects.common.cost.CostModificationEffectImpl;
+import mage.abilities.effects.common.cost.SpellsCostModificationThatTargetSourceEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.*;
+import mage.filter.FilterCard;
 import mage.filter.common.FilterCreaturePermanent;
 import mage.game.Game;
 import mage.game.events.GameEvent;
@@ -20,7 +19,6 @@ import mage.game.events.ZoneChangeEvent;
 import mage.game.permanent.Permanent;
 import mage.target.Target;
 import mage.target.common.TargetCreaturePermanent;
-import mage.util.CardUtil;
 import mage.watchers.Watcher;
 
 import java.util.UUID;
@@ -53,8 +51,9 @@ public final class IcefallRegent extends CardImpl {
         this.addAbility(ability, new IcefallRegentWatcher());
 
         // Spells your opponents cast that target Icefall Regent cost {2} more to cast.
-        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new IcefallRegentCostIncreaseEffect()));
-
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
+                new SpellsCostModificationThatTargetSourceEffect(2, new FilterCard("Spells"), TargetController.OPPONENT))
+        );
     }
 
     public IcefallRegent(final IcefallRegent card) {
@@ -157,48 +156,3 @@ class IcefallRegentWatcher extends Watcher {
         //don't reset condition each turn - only when this leaves the battlefield
     }
 }
-
-class IcefallRegentCostIncreaseEffect extends CostModificationEffectImpl {
-
-
-    IcefallRegentCostIncreaseEffect() {
-        super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST);
-        staticText = "Spells your opponents cast that target {this} cost {2} more to cast";
-    }
-
-    private IcefallRegentCostIncreaseEffect(IcefallRegentCostIncreaseEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        SpellAbility spellAbility = (SpellAbility) abilityToModify;
-        CardUtil.adjustCost(spellAbility, -2);
-        return true;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        if (abilityToModify instanceof SpellAbility) {
-            if (game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) {
-                for (UUID modeId : abilityToModify.getModes().getSelectedModes()) {
-                    Mode mode = abilityToModify.getModes().get(modeId);
-                    for (Target target : mode.getTargets()) {
-                        for (UUID targetUUID : target.getTargets()) {
-                            if (targetUUID.equals(source.getSourceId())) {
-                                return true;
-                            }
-                        }
-                    }
-                }
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public IcefallRegentCostIncreaseEffect copy() {
-        return new IcefallRegentCostIncreaseEffect(this);
-    }
-
-}
diff --git a/Mage.Sets/src/mage/cards/j/JubilantSkybonder.java b/Mage.Sets/src/mage/cards/j/JubilantSkybonder.java
index 3c82fe7b3e..ee0d7622b2 100644
--- a/Mage.Sets/src/mage/cards/j/JubilantSkybonder.java
+++ b/Mage.Sets/src/mage/cards/j/JubilantSkybonder.java
@@ -2,24 +2,19 @@ package mage.cards.j;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.Mode;
-import mage.abilities.SpellAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.ContinuousEffect;
 import mage.abilities.effects.common.continuous.GainAbilityAllEffect;
-import mage.abilities.effects.common.cost.CostModificationEffectImpl;
+import mage.abilities.effects.common.cost.SpellsCostModificationThatTargetSourceEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.*;
+import mage.filter.FilterCard;
 import mage.filter.FilterPermanent;
 import mage.filter.common.FilterControlledCreaturePermanent;
 import mage.filter.predicate.mageobject.AbilityPredicate;
-import mage.game.Game;
-import mage.target.Target;
-import mage.util.CardUtil;
 
-import java.util.Collection;
 import java.util.UUID;
 
 /**
@@ -46,10 +41,12 @@ public final class JubilantSkybonder extends CardImpl {
         this.addAbility(FlyingAbility.getInstance());
 
         // Creatures you control with flying have "Spells your opponents cast that target this creature cost {2} more to cast."
-        ContinuousEffect effect = new GainAbilityAllEffect(
-                new SimpleStaticAbility(new JubilantSkybonderEffect()),
-                Duration.WhileOnBattlefield, filter
-        ).withForceQuotes();
+        Ability gainAbility = new SimpleStaticAbility(Zone.BATTLEFIELD,
+                new SpellsCostModificationThatTargetSourceEffect(2, new FilterCard("Spells"), TargetController.OPPONENT)
+                        .withTargetName("this creature")
+        );
+
+        ContinuousEffect effect = new GainAbilityAllEffect(gainAbility, Duration.WhileOnBattlefield, filter).withForceQuotes();
         effect.setDependedToType(DependencyType.AddingAbility);
         this.addAbility(new SimpleStaticAbility(effect));
     }
@@ -63,45 +60,3 @@ public final class JubilantSkybonder extends CardImpl {
         return new JubilantSkybonder(this);
     }
 }
-
-class JubilantSkybonderEffect extends CostModificationEffectImpl {
-
-    JubilantSkybonderEffect() {
-        super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST);
-        staticText = "Spells your opponents cast that target this creature cost {2} more to cast";
-    }
-
-    private JubilantSkybonderEffect(JubilantSkybonderEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        SpellAbility spellAbility = (SpellAbility) abilityToModify;
-        CardUtil.adjustCost(spellAbility, -2);
-        return true;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        if (!(abilityToModify instanceof SpellAbility)
-                || !game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) {
-            return false;
-        }
-        return abilityToModify
-                .getModes()
-                .getSelectedModes()
-                .stream()
-                .map(abilityToModify.getModes()::get)
-                .map(Mode::getTargets)
-                .flatMap(Collection::stream)
-                .map(Target::getTargets)
-                .flatMap(Collection::stream)
-                .anyMatch(source.getSourceId()::equals);
-    }
-
-    @Override
-    public JubilantSkybonderEffect copy() {
-        return new JubilantSkybonderEffect(this);
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/p/PursuedWhale.java b/Mage.Sets/src/mage/cards/p/PursuedWhale.java
index fd9ca15ab8..d26a8f02d8 100644
--- a/Mage.Sets/src/mage/cards/p/PursuedWhale.java
+++ b/Mage.Sets/src/mage/cards/p/PursuedWhale.java
@@ -2,22 +2,18 @@ package mage.cards.p;
 
 import mage.MageInt;
 import mage.abilities.Ability;
-import mage.abilities.Mode;
-import mage.abilities.SpellAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.OneShotEffect;
-import mage.abilities.effects.common.cost.CostModificationEffectImpl;
+import mage.abilities.effects.common.cost.SpellsCostModificationThatTargetSourceEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.*;
+import mage.filter.FilterCard;
 import mage.game.Game;
 import mage.game.permanent.token.PursuedWhaleToken;
 import mage.game.permanent.token.Token;
-import mage.target.Target;
-import mage.util.CardUtil;
 
-import java.util.Collection;
 import java.util.UUID;
 
 /**
@@ -36,7 +32,9 @@ public final class PursuedWhale extends CardImpl {
         this.addAbility(new EntersBattlefieldTriggeredAbility(new PursuedWhaleTokenEffect()));
 
         // Spells your opponents cast that target Pursued Whale cost {3} more to cast.
-        this.addAbility(new SimpleStaticAbility(new PursuedWhaleCostIncreaseEffect()));
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
+                new SpellsCostModificationThatTargetSourceEffect(3, new FilterCard("Spells"), TargetController.OPPONENT))
+        );
     }
 
     private PursuedWhale(final PursuedWhale card) {
@@ -76,45 +74,3 @@ class PursuedWhaleTokenEffect extends OneShotEffect {
         return true;
     }
 }
-
-class PursuedWhaleCostIncreaseEffect extends CostModificationEffectImpl {
-
-    PursuedWhaleCostIncreaseEffect() {
-        super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST);
-        staticText = "Spells your opponents cast that target {this} cost {3} more to cast";
-    }
-
-    private PursuedWhaleCostIncreaseEffect(PursuedWhaleCostIncreaseEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        SpellAbility spellAbility = (SpellAbility) abilityToModify;
-        CardUtil.adjustCost(spellAbility, -3);
-        return true;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        if (!(abilityToModify instanceof SpellAbility)
-                || !game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) {
-            return false;
-        }
-        return abilityToModify
-                .getModes()
-                .getSelectedModes()
-                .stream()
-                .map(uuid -> abilityToModify.getModes().get(uuid))
-                .map(Mode::getTargets)
-                .flatMap(Collection::stream)
-                .map(Target::getTargets)
-                .flatMap(Collection::stream)
-                .anyMatch(uuid -> uuid.equals(source.getSourceId()));
-    }
-
-    @Override
-    public PursuedWhaleCostIncreaseEffect copy() {
-        return new PursuedWhaleCostIncreaseEffect(this);
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/s/SphinxOfNewPrahv.java b/Mage.Sets/src/mage/cards/s/SphinxOfNewPrahv.java
index 690a8c7f3b..102695ce81 100644
--- a/Mage.Sets/src/mage/cards/s/SphinxOfNewPrahv.java
+++ b/Mage.Sets/src/mage/cards/s/SphinxOfNewPrahv.java
@@ -1,20 +1,17 @@
 package mage.cards.s;
 
 import mage.MageInt;
-import mage.abilities.Ability;
-import mage.abilities.Mode;
-import mage.abilities.SpellAbility;
 import mage.abilities.common.SimpleStaticAbility;
-import mage.abilities.effects.common.cost.CostModificationEffectImpl;
+import mage.abilities.effects.common.cost.SpellsCostModificationThatTargetSourceEffect;
 import mage.abilities.keyword.FlyingAbility;
 import mage.abilities.keyword.VigilanceAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.*;
-import mage.game.Game;
-import mage.players.Player;
-import mage.target.Target;
-import mage.util.CardUtil;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.TargetController;
+import mage.constants.Zone;
+import mage.filter.FilterCard;
 
 import java.util.UUID;
 
@@ -37,7 +34,9 @@ public final class SphinxOfNewPrahv extends CardImpl {
         this.addAbility(VigilanceAbility.getInstance());
 
         // Spells your opponents cast that target Sphinx of New Prahv cost {2} more to cast.
-        this.addAbility(new SimpleStaticAbility(new SphinxOfNewPrahvCostIncreaseEffect()));
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
+                new SpellsCostModificationThatTargetSourceEffect(2, new FilterCard("Spells"), TargetController.OPPONENT))
+        );
     }
 
     private SphinxOfNewPrahv(final SphinxOfNewPrahv card) {
@@ -49,49 +48,3 @@ public final class SphinxOfNewPrahv extends CardImpl {
         return new SphinxOfNewPrahv(this);
     }
 }
-
-class SphinxOfNewPrahvCostIncreaseEffect extends CostModificationEffectImpl {
-
-    SphinxOfNewPrahvCostIncreaseEffect() {
-        super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST);
-        staticText = "Spells your opponents cast that target {this} cost {2} more to cast";
-    }
-
-    private SphinxOfNewPrahvCostIncreaseEffect(SphinxOfNewPrahvCostIncreaseEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        SpellAbility spellAbility = (SpellAbility) abilityToModify;
-        CardUtil.adjustCost(spellAbility, -2);
-        return true;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        Player controller = game.getPlayer(source.getControllerId());
-        if (controller == null
-                || !(abilityToModify instanceof SpellAbility)
-                || !controller.hasOpponent(abilityToModify.getControllerId(), game)) {
-            return false;
-        }
-        for (UUID modeId : abilityToModify.getModes().getSelectedModes()) {
-            Mode mode = abilityToModify.getModes().get(modeId);
-            for (Target target : mode.getTargets()) {
-                for (UUID targetUUID : target.getTargets()) {
-                    if (targetUUID.equals(source.getSourceId())) {
-                        return true;
-                    }
-                }
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public SphinxOfNewPrahvCostIncreaseEffect copy() {
-        return new SphinxOfNewPrahvCostIncreaseEffect(this);
-    }
-
-}
diff --git a/Mage.Sets/src/mage/cards/s/SyrElenoraTheDiscerning.java b/Mage.Sets/src/mage/cards/s/SyrElenoraTheDiscerning.java
index 0be6760519..558ccfb909 100644
--- a/Mage.Sets/src/mage/cards/s/SyrElenoraTheDiscerning.java
+++ b/Mage.Sets/src/mage/cards/s/SyrElenoraTheDiscerning.java
@@ -1,23 +1,17 @@
 package mage.cards.s;
 
 import mage.MageInt;
-import mage.abilities.Ability;
-import mage.abilities.Mode;
-import mage.abilities.SpellAbility;
 import mage.abilities.common.EntersBattlefieldTriggeredAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.dynamicvalue.common.CardsInControllerHandCount;
 import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.abilities.effects.common.continuous.SetPowerSourceEffect;
-import mage.abilities.effects.common.cost.CostModificationEffectImpl;
+import mage.abilities.effects.common.cost.SpellsCostModificationThatTargetSourceEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
 import mage.constants.*;
-import mage.game.Game;
-import mage.target.Target;
-import mage.util.CardUtil;
+import mage.filter.FilterCard;
 
-import java.util.Collection;
 import java.util.UUID;
 
 /**
@@ -43,7 +37,9 @@ public final class SyrElenoraTheDiscerning extends CardImpl {
         this.addAbility(new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1)));
 
         // Spells your opponents cast that target Syr Elenora cost {2} more to cast.
-        this.addAbility(new SimpleStaticAbility(new SyrElenoraTheDiscerningCostIncreaseEffect()));
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
+                new SpellsCostModificationThatTargetSourceEffect(2, new FilterCard("Spells"), TargetController.OPPONENT))
+        );
     }
 
     private SyrElenoraTheDiscerning(final SyrElenoraTheDiscerning card) {
@@ -55,46 +51,3 @@ public final class SyrElenoraTheDiscerning extends CardImpl {
         return new SyrElenoraTheDiscerning(this);
     }
 }
-
-class SyrElenoraTheDiscerningCostIncreaseEffect extends CostModificationEffectImpl {
-
-    SyrElenoraTheDiscerningCostIncreaseEffect() {
-        super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST);
-        staticText = "Spells your opponents cast that target {this} cost {2} more to cast";
-    }
-
-    private SyrElenoraTheDiscerningCostIncreaseEffect(SyrElenoraTheDiscerningCostIncreaseEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        SpellAbility spellAbility = (SpellAbility) abilityToModify;
-        CardUtil.adjustCost(spellAbility, -2);
-        return true;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        if (!(abilityToModify instanceof SpellAbility)
-                || !game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) {
-            return false;
-        }
-        return abilityToModify
-                .getModes()
-                .getSelectedModes()
-                .stream()
-                .map(uuid -> abilityToModify.getModes().get(uuid))
-                .map(Mode::getTargets)
-                .flatMap(Collection::stream)
-                .map(Target::getTargets)
-                .flatMap(Collection::stream)
-                .anyMatch(uuid -> uuid.equals(source.getSourceId()));
-    }
-
-    @Override
-    public SyrElenoraTheDiscerningCostIncreaseEffect copy() {
-        return new SyrElenoraTheDiscerningCostIncreaseEffect(this);
-    }
-
-}
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostModificationTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostModificationTest.java
index 40ffdc079f..e7a6d7232d 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostModificationTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostModificationTest.java
@@ -7,10 +7,7 @@ import org.junit.Test;
 import org.mage.test.serverside.base.CardTestPlayerBase;
 
 /**
- *
- * also tests cost reduction effects
- *
- * @author BetaSteward
+ * @author BetaSteward, JayDi85
  */
 public class CostModificationTest extends CardTestPlayerBase {
 
@@ -71,7 +68,7 @@ public class CostModificationTest extends CardTestPlayerBase {
     /**
      * Test that cost reduction also works with mana source restriction Myr
      * Superion Spend only mana produced by creatures to cast Myr Superion
-     *
+     * <p>
      * Etherium Sculptor {1}{U} Artifact Creature - Vedalken Artificer 1/2
      * Artifact spells you cast cost {1} less to cast.
      */
@@ -143,7 +140,7 @@ public class CostModificationTest extends CardTestPlayerBase {
     }
 
     /*
-    * Reported bug: Grand Arbiter Augustin IV makes moth spells you cast and your opponent cast {1} more. Should only affect opponent's spells costs.
+     * Reported bug: Grand Arbiter Augustin IV makes moth spells you cast and your opponent cast {1} more. Should only affect opponent's spells costs.
      */
     @Test
     public void testArbiterIncreasingCostBothPlayers() {
@@ -244,6 +241,106 @@ public class CostModificationTest extends CardTestPlayerBase {
         assertCounterCount(playerA, "Animar, Soul of Elements", CounterType.P1P1, 2);
 
         assertTappedCount("Plains", true, 3);
+    }
 
+    @Test
+    public void test_ThatTargetSourceEffect_AccursedWitch_CanPlayWithReduction() {
+        // creature 4/2
+        // Spells your opponents cast that target Accursed Witch cost {1} less to cast.
+        addCard(Zone.BATTLEFIELD, playerB, "Accursed Witch");
+        //
+        // {1}{R} SORCERY
+        // Grapeshot deals 1 damage to any target.
+        addCard(Zone.HAND, playerA, "Grapeshot");
+        addCard(Zone.HAND, playerA, "Mountain", 1); // play to add mana
+
+        checkPlayableAbility("0 mana, can't", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Grapeshot", false);
+
+        // add 1 mana, can cast by target
+        playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mountain");
+        checkPlayableAbility("1 mana, can play", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Grapeshot", true);
+
+        // cast with target
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grapeshot", "Accursed Witch");
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertGraveyardCount(playerA, "Grapeshot", 1);
+    }
+
+    @Test
+    public void test_ThatTargetSourceEffect_AccursedWitch_CantPlayOnProtection() {
+        // creature 4/2
+        // Spells your opponents cast that target Accursed Witch cost {1} less to cast.
+        addCard(Zone.BATTLEFIELD, playerB, "Accursed Witch");
+        //
+        // {1}{R} SORCERY
+        // Grapeshot deals 1 damage to any target.
+        addCard(Zone.HAND, playerA, "Grapeshot");
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
+        //
+        // Artifact — Equipment
+        // Equip {2}
+        // Equipped creature gets +2/+2 and has protection from red and from white.
+        addCard(Zone.BATTLEFIELD, playerB, "Sword of War and Peace");
+        addCard(Zone.BATTLEFIELD, playerB, "Plains", 2);
+
+        // 1 mana, can cast by target
+        checkPlayableAbility("1 mana, can play", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Grapeshot", true);
+
+        // add protection from red
+        activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Equip {2}", "Accursed Witch");
+
+        // can't cast cause can't target to red
+        checkPlayableAbility("can't cast cause protection", 3, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Grapeshot", false);
+
+        setStrictChooseMode(true);
+        setStopAt(3, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+    }
+
+    @Test
+    public void test_ThatTargetSourceEffect_BorealElemental() {
+        // use case: cost increase for getPlayable works only for no other targets available
+        // so if you can targets another target then allows to cast (don't apply cost increase)
+
+        // creature 3/4
+        // Spells your opponents cast that target Boreal Elemental cost {2} more to cast.
+        addCard(Zone.BATTLEFIELD, playerB, "Boreal Elemental");
+        //
+        // {R} instant
+        // Engulfing Flames deals 1 damage to target creature. It can't be regenerated this turn.
+        addCard(Zone.HAND, playerA, "Engulfing Flames");
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
+        //
+        addCard(Zone.HAND, playerA, "Grizzly Bears"); // {1}{G}
+        addCard(Zone.HAND, playerA, "Forest", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
+
+        // no second target, so must cost increase
+        checkPlayableAbility("one target, can't play", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Engulfing Flames", false);
+
+        // prepare second target
+        playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Forest");
+        activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {G}", 2);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears");
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+        checkPlayableAbility("two targets, can play", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Engulfing Flames", true);
+
+        // try to cast (only one target possible to targets/play)
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Engulfing Flames");
+        //addTarget(playerA, "Boreal Elemental"); // you can't target Boreal Elemental cause it will increase cost
+        addTarget(playerA, "Grizzly Bears");
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertGraveyardCount(playerA, "Engulfing Flames", 1);
     }
 }
diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
index 7e424803a8..f8d33b3ffb 100644
--- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
+++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
@@ -1181,6 +1181,8 @@ public class TestPlayer implements Player {
         }
 
         if (mustHave && !founded) {
+            printStart("Available mana for " + computerPlayer.getName());
+            printMana(game, computerPlayer.getManaAvailable(game));
             printStart(action.getActionName());
             printAbilities(game, computerPlayer.getPlayable(game, true));
             printEnd();
@@ -1188,6 +1190,8 @@ public class TestPlayer implements Player {
         }
 
         if (!mustHave && founded) {
+            printStart("Available mana for " + computerPlayer.getName());
+            printMana(game, computerPlayer.getManaAvailable(game));
             printStart(action.getActionName());
             printAbilities(game, computerPlayer.getPlayable(game, true));
             printEnd();
diff --git a/Mage/src/main/java/mage/abilities/AbilityImpl.java b/Mage/src/main/java/mage/abilities/AbilityImpl.java
index 5f1512e321..fe26af024e 100644
--- a/Mage/src/main/java/mage/abilities/AbilityImpl.java
+++ b/Mage/src/main/java/mage/abilities/AbilityImpl.java
@@ -769,7 +769,7 @@ public abstract class AbilityImpl implements Ability {
             if (ruleStart.length() > 1) {
                 String end = ruleStart.substring(ruleStart.length() - 2).trim();
                 if (end.isEmpty() || end.equals(":") || end.equals(".")) {
-                    rule = ruleStart + Character.toUpperCase(text.charAt(0)) + text.substring(1);
+                    rule = ruleStart + CardUtil.getTextWithFirstCharUpperCase(text);
                 } else {
                     rule = ruleStart + text;
                 }
diff --git a/Mage/src/main/java/mage/abilities/effects/Effects.java b/Mage/src/main/java/mage/abilities/effects/Effects.java
index 38c798b30f..42698bba84 100644
--- a/Mage/src/main/java/mage/abilities/effects/Effects.java
+++ b/Mage/src/main/java/mage/abilities/effects/Effects.java
@@ -4,6 +4,7 @@ import mage.abilities.Ability;
 import mage.abilities.Mode;
 import mage.constants.Outcome;
 import mage.target.targetpointer.TargetPointer;
+import mage.util.CardUtil;
 
 import java.util.ArrayList;
 
@@ -31,7 +32,7 @@ public class Effects extends ArrayList<Effect> {
     public String getTextStartingUpperCase(Mode mode) {
         String text = getText(mode);
         if (text.length() > 3) {
-            return Character.toUpperCase(text.charAt(0)) + text.substring(1);
+            return CardUtil.getTextWithFirstCharUpperCase(text);
         }
         return text;
     }
diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostModificationThatTargetSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostModificationThatTargetSourceEffect.java
new file mode 100644
index 0000000000..de525432ba
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostModificationThatTargetSourceEffect.java
@@ -0,0 +1,166 @@
+package mage.abilities.effects.common.cost;
+
+import mage.abilities.Ability;
+import mage.abilities.Mode;
+import mage.abilities.SpellAbility;
+import mage.constants.CostModificationType;
+import mage.constants.Duration;
+import mage.constants.Outcome;
+import mage.constants.TargetController;
+import mage.filter.FilterCard;
+import mage.game.Game;
+import mage.game.stack.Spell;
+import mage.players.Player;
+import mage.target.Target;
+import mage.util.CardUtil;
+
+import java.util.Collection;
+import java.util.Set;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+/**
+ * @author JayDi85
+ */
+public class SpellsCostModificationThatTargetSourceEffect extends CostModificationEffectImpl {
+
+    private final FilterCard spellFilter;
+    private final int modificationAmount;
+    private String targetName = "{this}";
+    private TargetController targetController;
+
+    public SpellsCostModificationThatTargetSourceEffect(int modificationAmount, FilterCard spellFilter, TargetController targetController) {
+        super(Duration.WhileOnBattlefield, Outcome.Neutral, modificationAmount < 0 ? CostModificationType.REDUCE_COST : CostModificationType.INCREASE_COST);
+        this.spellFilter = spellFilter;
+        this.modificationAmount = modificationAmount;
+        this.targetController = targetController;
+
+        setText();
+    }
+
+    private void setText() {
+        // example: Spells your opponents cast that target Accursed Witch cost {1} less to cast.
+        StringBuilder sb = new StringBuilder();
+        sb.append(this.spellFilter.getMessage());
+        switch (this.targetController) {
+            case ANY:
+                break;
+            case YOU:
+                sb.append(" you");
+                break;
+            case OPPONENT:
+                sb.append(" your opponents");
+                break;
+            default:
+                throw new IllegalArgumentException("Unsupported target controller " + this.targetController);
+        }
+
+        sb.append(" cast that target ").append(this.targetName);
+        if (this.modificationAmount < 0) {
+            sb.append(" cost {").append(-1 * this.modificationAmount).append("} less to cast");
+        } else {
+            sb.append(" cost {").append(this.modificationAmount).append("} more to cast");
+        }
+        this.staticText = sb.toString();
+    }
+
+    private SpellsCostModificationThatTargetSourceEffect(SpellsCostModificationThatTargetSourceEffect effect) {
+        super(effect);
+        this.spellFilter = effect.spellFilter;
+        this.modificationAmount = effect.modificationAmount;
+        this.targetName = effect.targetName;
+        this.targetController = effect.targetController;
+    }
+
+    @Override
+    public SpellsCostModificationThatTargetSourceEffect copy() {
+        return new SpellsCostModificationThatTargetSourceEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source, Ability abilityToModify) {
+        if (this.modificationAmount >= 0) {
+            CardUtil.increaseCost(abilityToModify, this.modificationAmount);
+        } else {
+            CardUtil.reduceCost(abilityToModify, -1 * this.modificationAmount);
+        }
+        return true;
+    }
+
+    @Override
+    public boolean applies(Ability abilityToModify, Ability source, Game game) {
+        if (!(abilityToModify instanceof SpellAbility)) {
+            return false;
+        }
+
+        Player sourceController = game.getPlayer(source.getControllerId());
+        Player abilityController = game.getPlayer(abilityToModify.getControllerId());
+        if (sourceController == null || abilityController == null) {
+            return false;
+        }
+
+        switch (this.targetController) {
+            case ANY:
+                break;
+            case YOU:
+                if (!sourceController.getId().equals(abilityController.getId())) {
+                    return false;
+                }
+                break;
+            case OPPONENT:
+                if (!sourceController.hasOpponent(abilityController.getId(), game)) {
+                    return false;
+                }
+                break;
+            default:
+                return false;
+        }
+
+        Spell spell = (Spell) game.getStack().getStackObject(abilityToModify.getId());
+        if (spell != null && this.spellFilter.match(spell, game)) {
+            // real cast with put on stack
+            Set<UUID> allTargets = getAllSelectedTargets(abilityToModify, source, game);
+            return allTargets.contains(source.getSourceId());
+        } else {
+            // get playable and other staff without put on stack
+            // used at least for flashback ability because Flashback ability doesn't use stack
+            Set<UUID> allTargets = getAllPossibleTargets(abilityToModify, source, game);
+            switch (this.getModificationType()) {
+                case REDUCE_COST:
+                    // reduce all the time
+                    return allTargets.contains(source.getSourceId());
+                case INCREASE_COST:
+                    // increase if can't target another (e.g. user can choose another target without cost increase)
+                    return allTargets.contains(source.getSourceId()) && allTargets.size() <= 1;
+            }
+        }
+        return false;
+    }
+
+    private Set<UUID> getAllSelectedTargets(Ability abilityToModify, Ability source, Game game) {
+        return abilityToModify.getModes().getSelectedModes()
+                .stream()
+                .map(abilityToModify.getModes()::get)
+                .map(Mode::getTargets)
+                .flatMap(Collection::stream)
+                .map(Target::getTargets)
+                .flatMap(Collection::stream)
+                .collect(Collectors.toSet());
+    }
+
+    private Set<UUID> getAllPossibleTargets(Ability abilityToModify, Ability source, Game game) {
+        return abilityToModify.getModes().values()
+                .stream()
+                .map(Mode::getTargets)
+                .flatMap(Collection::stream)
+                .map(t -> t.possibleTargets(abilityToModify.getSourceId(), abilityToModify.getControllerId(), game))
+                .flatMap(Collection::stream)
+                .collect(Collectors.toSet());
+    }
+
+    public SpellsCostModificationThatTargetSourceEffect withTargetName(String targetName) {
+        this.targetName = targetName;
+        setText();
+        return this;
+    }
+}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionAllEffect.java
index 3c79a57825..5b1dd4811c 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionAllEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionAllEffect.java
@@ -1,8 +1,5 @@
 package mage.abilities.effects.common.cost;
 
-import java.util.LinkedHashSet;
-import java.util.Set;
-import java.util.UUID;
 import mage.Mana;
 import mage.abilities.Ability;
 import mage.abilities.SpellAbility;
@@ -17,8 +14,11 @@ import mage.game.stack.Spell;
 import mage.players.Player;
 import mage.util.CardUtil;
 
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public class SpellsCostReductionAllEffect extends CostModificationEffectImpl {
@@ -125,8 +125,10 @@ public class SpellsCostReductionAllEffect extends CostModificationEffectImpl {
         if (abilityToModify instanceof SpellAbility) {
             Spell spell = (Spell) game.getStack().getStackObject(abilityToModify.getId());
             if (spell != null) {
+                // real cast with put on stack
                 return this.filter.match(spell, game) && selectedByRuntimeData(spell, source, game);
             } else {
+                // get playable and other staff without put on stack
                 // used at least for flashback ability because Flashback ability doesn't use stack
                 Card sourceCard = game.getCard(abilityToModify.getSourceId());
                 return sourceCard != null && this.filter.match(sourceCard, game) && selectedByRuntimeData(sourceCard, source, game);
diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionControllerEffect.java
index 1fd38caa06..7fcab94aaf 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionControllerEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostReductionControllerEffect.java
@@ -1,7 +1,5 @@
 package mage.abilities.effects.common.cost;
 
-import java.util.LinkedHashSet;
-import java.util.Set;
 import mage.MageObject;
 import mage.Mana;
 import mage.abilities.Ability;
@@ -19,8 +17,10 @@ import mage.game.stack.Spell;
 import mage.players.Player;
 import mage.util.CardUtil;
 
+import java.util.LinkedHashSet;
+import java.util.Set;
+
 /**
- *
  * @author North
  */
 public class SpellsCostReductionControllerEffect extends CostModificationEffectImpl {
@@ -116,9 +116,11 @@ public class SpellsCostReductionControllerEffect extends CostModificationEffectI
             if (abilityToModify.isControlledBy(source.getControllerId())) {
                 Spell spell = (Spell) game.getStack().getStackObject(abilityToModify.getId());
                 if (spell != null) {
+                    // real cast with put on stack
                     return this.filter.match(spell, source.getSourceId(), source.getControllerId(), game);
                 } else {
-                    // used at least for flashback ability because Flashback ability doesn't use stack or for getPlayables where spell is not cast yet
+                    // get playable and other staff without put on stack
+                    // used at least for flashback ability because Flashback ability doesn't use stack
                     Card sourceCard = game.getCard(abilityToModify.getSourceId());
                     return sourceCard != null && this.filter.match(sourceCard, source.getSourceId(), source.getControllerId(), game);
                 }
diff --git a/Mage/src/main/java/mage/abilities/hint/ConditionHint.java b/Mage/src/main/java/mage/abilities/hint/ConditionHint.java
index 4df8b1cf59..6347054b2f 100644
--- a/Mage/src/main/java/mage/abilities/hint/ConditionHint.java
+++ b/Mage/src/main/java/mage/abilities/hint/ConditionHint.java
@@ -3,6 +3,7 @@ package mage.abilities.hint;
 import mage.abilities.Ability;
 import mage.abilities.condition.Condition;
 import mage.game.Game;
+import mage.util.CardUtil;
 
 import java.awt.*;
 
@@ -18,15 +19,19 @@ public class ConditionHint implements Hint {
     private Color falseColor;
     private Boolean useIcons;
 
+    public ConditionHint(Condition condition) {
+        this(condition, condition.toString());
+    }
+
     public ConditionHint(Condition condition, String textWithIcons) {
         this(condition, textWithIcons, null, textWithIcons, null, true);
     }
 
     public ConditionHint(Condition condition, String trueText, Color trueColor, String falseText, Color falseColor, Boolean useIcons) {
         this.condition = condition;
-        this.trueText = trueText;
+        this.trueText = CardUtil.getTextWithFirstCharUpperCase(trueText);
         this.trueColor = trueColor;
-        this.falseText = falseText;
+        this.falseText = CardUtil.getTextWithFirstCharUpperCase(falseText);
         this.falseColor = falseColor;
         this.useIcons = useIcons;
     }
diff --git a/Mage/src/main/java/mage/util/CardUtil.java b/Mage/src/main/java/mage/util/CardUtil.java
index 6836eab716..e80259326d 100644
--- a/Mage/src/main/java/mage/util/CardUtil.java
+++ b/Mage/src/main/java/mage/util/CardUtil.java
@@ -800,4 +800,12 @@ public final class CardUtil {
             return object.getAbilities();
         }
     }
+
+    public static String getTextWithFirstCharUpperCase(String text) {
+        if (text != null && text.length() >= 1) {
+            return Character.toUpperCase(text.charAt(0)) + text.substring(1);
+        } else {
+            return text;
+        }
+    }
 }

From fc7431b97b27d89325685fe4141c000db40b8f22 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Tue, 30 Jun 2020 08:31:08 +0400
Subject: [PATCH 570/586] Fixed jumpstart config (#6669)

---
 Mage.Server/release/config/config.xml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Mage.Server/release/config/config.xml b/Mage.Server/release/config/config.xml
index 3a9810f386..a4337f70d4 100644
--- a/Mage.Server/release/config/config.xml
+++ b/Mage.Server/release/config/config.xml
@@ -98,8 +98,8 @@
         <tournamentType name="Sealed Elimination (Cube)" jar="mage-tournament-sealed-${project.version}.jar" className="mage.tournament.SealedEliminationTournament" typeName="mage.tournament.SealedEliminationCubeTournamentType"/>
         <tournamentType name="Sealed Swiss" jar="mage-tournament-sealed-${project.version}.jar" className="mage.tournament.SealedSwissTournament" typeName="mage.tournament.SealedSwissTournamentType"/>
         <tournamentType name="Sealed Swiss (Cube)" jar="mage-tournament-sealed-${project.version}.jar" className="mage.tournament.SealedSwissTournament" typeName="mage.tournament.SealedSwissCubeTournamentType"/>
-        <tournamentType name="Jumpstart Elimination" jar="mage-tournament-sealed.jar" className="mage.tournament.JumpstartEliminationTournament" typeName="mage.tournament.JumpstartEliminationTournamentType"/>
-        <tournamentType name="Jumpstart Swiss" jar="mage-tournament-sealed.jar" className="mage.tournament.JumpstartSwissTournament" typeName="mage.tournament.JumpstartSwissTournamentType"/>
+        <tournamentType name="Jumpstart Elimination" jar="mage-tournament-sealed-${project.version}.jar" className="mage.tournament.JumpstartEliminationTournament" typeName="mage.tournament.JumpstartEliminationTournamentType"/>
+        <tournamentType name="Jumpstart Swiss" jar="mage-tournament-sealed-${project.version}.jar" className="mage.tournament.JumpstartSwissTournament" typeName="mage.tournament.JumpstartSwissTournamentType"/>
     </tournamentTypes>
     <draftCubes>
         <draftCube name="Adam Styborski's Pauper Cube" jar="mage-tournament-booster-draft-${project.version}.jar" className="mage.tournament.cubes.AdamStyborskisPauperCube"/>

From eaae0ce25604f6b3c642f8f26162d0ba140303b9 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Tue, 30 Jun 2020 08:32:35 +0400
Subject: [PATCH 571/586] [JMP] added images download support

---
 .../mage/plugins/card/dl/sources/ScryfallImageSupportCards.java  | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java
index 6d5670d7b0..1207706b7e 100644
--- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java
@@ -261,6 +261,7 @@ public class ScryfallImageSupportCards {
             add("PF19");
             add("MPS-AKH");
             add("M21");
+            add("JMP");
         }
     };
 

From 83135f347f4174ab91348ccdc656f0fe42983139 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Tue, 30 Jun 2020 08:51:33 +0400
Subject: [PATCH 572/586] Spellwild Ouphe - fixed cost reduction effect (#6698)

---
 .../src/mage/cards/s/SpellwildOuphe.java      | 71 ++++---------------
 ...ostModificationThatTargetSourceEffect.java |  6 +-
 2 files changed, 16 insertions(+), 61 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/s/SpellwildOuphe.java b/Mage.Sets/src/mage/cards/s/SpellwildOuphe.java
index ac7dbe287c..047ca52579 100644
--- a/Mage.Sets/src/mage/cards/s/SpellwildOuphe.java
+++ b/Mage.Sets/src/mage/cards/s/SpellwildOuphe.java
@@ -1,22 +1,19 @@
-
 package mage.cards.s;
 
-import java.util.UUID;
 import mage.MageInt;
-import mage.abilities.Ability;
-import mage.abilities.Mode;
-import mage.abilities.SpellAbility;
 import mage.abilities.common.SimpleStaticAbility;
-import mage.abilities.effects.common.cost.CostModificationEffectImpl;
+import mage.abilities.effects.common.cost.SpellsCostModificationThatTargetSourceEffect;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.*;
-import mage.game.Game;
-import mage.target.Target;
-import mage.util.CardUtil;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.TargetController;
+import mage.constants.Zone;
+import mage.filter.FilterCard;
+
+import java.util.UUID;
 
 /**
- *
  * @author LevelX2 & L_J
  */
 public final class SpellwildOuphe extends CardImpl {
@@ -27,8 +24,10 @@ public final class SpellwildOuphe extends CardImpl {
         this.power = new MageInt(1);
         this.toughness = new MageInt(3);
 
-        // Spells  that target Elderwood Scion cost {2} less to cast.
-        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellwildOupheCostReductionEffect()));
+        // Spells that target Spellwild Ouphe cost {2} less to cast.
+        this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
+                new SpellsCostModificationThatTargetSourceEffect(-2, new FilterCard("Spells"), TargetController.ANY))
+        );
     }
 
     public SpellwildOuphe(final SpellwildOuphe card) {
@@ -39,48 +38,4 @@ public final class SpellwildOuphe extends CardImpl {
     public SpellwildOuphe copy() {
         return new SpellwildOuphe(this);
     }
-}
-
-class SpellwildOupheCostReductionEffect extends CostModificationEffectImpl {
-
-    private static final String effectText = "Spells that target {this} cost {2} less to cast";
-
-    SpellwildOupheCostReductionEffect() {
-        super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST);
-        staticText = effectText;
-    }
-
-    SpellwildOupheCostReductionEffect(SpellwildOupheCostReductionEffect effect) {
-        super(effect);
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        SpellAbility spellAbility = (SpellAbility) abilityToModify;
-        CardUtil.adjustCost(spellAbility, 2);
-        return true;
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        if (abilityToModify instanceof SpellAbility) {
-            for (UUID modeId : abilityToModify.getModes().getSelectedModes()) {
-                Mode mode = abilityToModify.getModes().get(modeId);
-                for (Target target : mode.getTargets()) {
-                    for (UUID targetUUID : target.getTargets()) {
-                        if (targetUUID.equals(source.getSourceId())) {
-                            return true;
-                        }
-                    }
-                }
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public SpellwildOupheCostReductionEffect copy() {
-        return new SpellwildOupheCostReductionEffect(this);
-    }
-
-}
+}
\ No newline at end of file
diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostModificationThatTargetSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostModificationThatTargetSourceEffect.java
index de525432ba..da1f412c2a 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostModificationThatTargetSourceEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/cost/SpellsCostModificationThatTargetSourceEffect.java
@@ -46,16 +46,16 @@ public class SpellsCostModificationThatTargetSourceEffect extends CostModificati
             case ANY:
                 break;
             case YOU:
-                sb.append(" you");
+                sb.append(" you cast");
                 break;
             case OPPONENT:
-                sb.append(" your opponents");
+                sb.append(" your opponents cast");
                 break;
             default:
                 throw new IllegalArgumentException("Unsupported target controller " + this.targetController);
         }
 
-        sb.append(" cast that target ").append(this.targetName);
+        sb.append(" that target ").append(this.targetName);
         if (this.modificationAmount < 0) {
             sb.append(" cost {").append(-1 * this.modificationAmount).append("} less to cast");
         } else {

From 60cce5c11b8b36c27155afc30ba5e73f87949f4f Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Tue, 30 Jun 2020 15:34:29 +0200
Subject: [PATCH 573/586] * Fixed wrong event attributes for special action
 ACITIVATE_ABILITY event (see also 6753). Fixed that Phyrexian Revoker also
 prevented using special actions (fixes #6747).

---
 .../src/mage/cards/j/JeganthaTheWellspring.java  |  2 +-
 .../src/mage/cards/k/KaheeraTheOrphanguard.java  |  2 +-
 Mage.Sets/src/mage/cards/p/PhyrexianRevoker.java | 16 ++++++++++------
 .../mage/abilities/keyword/CompanionAbility.java | 12 ++++++------
 Mage/src/main/java/mage/game/GameImpl.java       |  8 ++++----
 .../main/java/mage/game/events/GameEvent.java    | 11 +++++++++++
 Mage/src/main/java/mage/players/PlayerImpl.java  |  4 ++--
 7 files changed, 35 insertions(+), 20 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/j/JeganthaTheWellspring.java b/Mage.Sets/src/mage/cards/j/JeganthaTheWellspring.java
index f8e6648a20..f173e10816 100644
--- a/Mage.Sets/src/mage/cards/j/JeganthaTheWellspring.java
+++ b/Mage.Sets/src/mage/cards/j/JeganthaTheWellspring.java
@@ -67,7 +67,7 @@ enum JeganthaTheWellspringCompanionCondition implements CompanionCondition {
         return deck.stream().noneMatch(JeganthaTheWellspringCompanionCondition::checkCard);
     }
 
-    private static final boolean checkCard(Card card) {
+    private static boolean checkCard(Card card) {
         Map<String, Integer> symbolMap = new HashMap();
         return card.getManaCost()
                 .getSymbols()
diff --git a/Mage.Sets/src/mage/cards/k/KaheeraTheOrphanguard.java b/Mage.Sets/src/mage/cards/k/KaheeraTheOrphanguard.java
index b43acab4f8..da1aa2f606 100644
--- a/Mage.Sets/src/mage/cards/k/KaheeraTheOrphanguard.java
+++ b/Mage.Sets/src/mage/cards/k/KaheeraTheOrphanguard.java
@@ -91,7 +91,7 @@ enum KaheeraTheOrphanguardCompanionCondition implements CompanionCondition {
             SubType.BEAST
     );
 
-    private static final boolean checkTypes(Card card) {
+    private static boolean checkTypes(Card card) {
         return subtypes.stream().anyMatch(card.getSubtype(null)::contains);
     }
 
diff --git a/Mage.Sets/src/mage/cards/p/PhyrexianRevoker.java b/Mage.Sets/src/mage/cards/p/PhyrexianRevoker.java
index b68174a304..2ca284b737 100644
--- a/Mage.Sets/src/mage/cards/p/PhyrexianRevoker.java
+++ b/Mage.Sets/src/mage/cards/p/PhyrexianRevoker.java
@@ -1,5 +1,6 @@
 package mage.cards.p;
 
+import java.util.Optional;
 import mage.MageInt;
 import mage.MageObject;
 import mage.abilities.Ability;
@@ -14,7 +15,6 @@ import mage.game.Game;
 import mage.game.events.GameEvent;
 import mage.game.events.GameEvent.EventType;
 import mage.util.CardUtil;
-
 import java.util.UUID;
 
 /**
@@ -43,7 +43,6 @@ public final class PhyrexianRevoker extends CardImpl {
     public PhyrexianRevoker copy() {
         return new PhyrexianRevoker(this);
     }
-
 }
 
 class PhyrexianRevokerEffect2 extends ContinuousRuleModifyingEffectImpl {
@@ -79,11 +78,16 @@ class PhyrexianRevokerEffect2 extends ContinuousRuleModifyingEffectImpl {
     @Override
     public boolean applies(GameEvent event, Ability source, Game game) {
         if (event.getType() == EventType.ACTIVATE_ABILITY) {
-            MageObject object = game.getObject(event.getSourceId());
-            String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY);
-            return CardUtil.haveSameNames(object, cardName, game);
+            MageObject object = game.getObject(event.getSourceId()); // Can happen for special ability????
+            if (object != null) {
+                Optional<Ability> optAbility = object.getAbilities().get(event.getTargetId());
+                if (optAbility.isPresent() && AbilityType.SPECIAL_ACTION == optAbility.get().getAbilityType()) {
+                    return false;
+                }
+                String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY);
+                return CardUtil.haveSameNames(object, cardName, game);
+            }
         }
         return false;
     }
-
 }
diff --git a/Mage/src/main/java/mage/abilities/keyword/CompanionAbility.java b/Mage/src/main/java/mage/abilities/keyword/CompanionAbility.java
index bffb8ea944..dff4f8dcfb 100644
--- a/Mage/src/main/java/mage/abilities/keyword/CompanionAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/CompanionAbility.java
@@ -14,11 +14,11 @@ import java.util.Set;
  */
 public class CompanionAbility extends SpecialAction {
 
-    private final CompanionCondition condition;
+    private final CompanionCondition companionCondition;
 
-    public CompanionAbility(CompanionCondition condition) {
+    public CompanionAbility(CompanionCondition companionCondition) {
         super(Zone.OUTSIDE);
-        this.condition = condition;
+        this.companionCondition = companionCondition;
         this.addCost(new GenericManaCost(3));
         this.addEffect(new CompanionEffect());
         this.setTiming(TimingRule.SORCERY);
@@ -26,7 +26,7 @@ public class CompanionAbility extends SpecialAction {
 
     private CompanionAbility(final CompanionAbility ability) {
         super(ability);
-        this.condition = ability.condition;
+        this.companionCondition = ability.companionCondition;
     }
 
     @Override
@@ -36,11 +36,11 @@ public class CompanionAbility extends SpecialAction {
 
     @Override
     public String getRule() {
-        return "Companion &mdash; " + condition.getRule();
+        return "Companion &mdash; " + companionCondition.getRule();
     }
 
     public boolean isLegal(Set<Card> cards, int startingSize) {
-        return condition.isLegal(cards, startingSize);
+        return companionCondition.isLegal(cards, startingSize);
     }
 }
 
diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java
index 90a360c070..6f517a2518 100644
--- a/Mage/src/main/java/mage/game/GameImpl.java
+++ b/Mage/src/main/java/mage/game/GameImpl.java
@@ -353,7 +353,7 @@ public abstract class GameImpl implements Game, Serializable {
             // can be an ability of a sacrificed Token trying to get it's source object
             object = getLastKnownInformation(objectId, Zone.BATTLEFIELD);
         }
-
+        
         return object;
     }
 
@@ -1548,7 +1548,7 @@ public abstract class GameImpl implements Game, Serializable {
     /**
      * @param emblem
      * @param sourceObject
-     * @param toPlayerId controller and owner of the emblem
+     * @param toPlayerId   controller and owner of the emblem
      */
     @Override
     public void addEmblem(Emblem emblem, MageObject sourceObject, UUID toPlayerId) {
@@ -1566,8 +1566,8 @@ public abstract class GameImpl implements Game, Serializable {
     /**
      * @param plane
      * @param sourceObject
-     * @param toPlayerId controller and owner of the plane (may only be one per
-     * game..)
+     * @param toPlayerId   controller and owner of the plane (may only be one
+     *                     per game..)
      * @return boolean - whether the plane was added successfully or not
      */
     @Override
diff --git a/Mage/src/main/java/mage/game/events/GameEvent.java b/Mage/src/main/java/mage/game/events/GameEvent.java
index 9af64c0c37..b7d352af3c 100644
--- a/Mage/src/main/java/mage/game/events/GameEvent.java
+++ b/Mage/src/main/java/mage/game/events/GameEvent.java
@@ -149,6 +149,17 @@ public class GameEvent implements Serializable {
          */
         SPELL_CAST,
         ACTIVATE_ABILITY, ACTIVATED_ABILITY,
+        /* ACTIVATE_ABILITY, ACTIVATED_ABILITY,
+         targetId    id of the ability to activate / use
+         sourceId    sourceId of the object with that ability
+         playerId    player that tries to use this ability
+        */
+        TAKE_SPECIAL_ACTION, TAKEN_SPECIAL_ACTION, // not used in implementation yet
+        /* TAKE_SPECIAL_ACTION, TAKEN_SPECIAL_ACTION,
+         targetId    id of the ability to activate / use
+         sourceId    sourceId of the object with that ability
+         playerId    player that tries to use this ability
+        */
         TRIGGERED_ABILITY,
         RESOLVING_ABILITY,
         COPY_STACKOBJECT, COPIED_STACKOBJECT,
diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java
index 7842874ad7..29be0c4b63 100644
--- a/Mage/src/main/java/mage/players/PlayerImpl.java
+++ b/Mage/src/main/java/mage/players/PlayerImpl.java
@@ -1346,11 +1346,11 @@ public abstract class PlayerImpl implements Player, Serializable {
     protected boolean specialAction(SpecialAction action, Game game) {
         //20091005 - 114
         if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.ACTIVATE_ABILITY,
-                action.getSourceId(), action.getId(), playerId))) {
+                action.getId(), action.getSourceId(), getId()))) {
             int bookmark = game.bookmarkState();
             if (action.activate(game, false)) {
                 game.fireEvent(GameEvent.getEvent(GameEvent.EventType.ACTIVATED_ABILITY,
-                        action.getSourceId(), action.getId(), playerId));
+                        action.getId(), action.getSourceId(), getId()));
                 if (!game.isSimulation()) {
                     game.informPlayers(getLogName() + action.getGameLogMessage(game));
                 }

From c4082548c86a4ab9d1d059f276115e8b88a7b290 Mon Sep 17 00:00:00 2001
From: LevelX2 <ludwig.hirth@online.de>
Date: Tue, 30 Jun 2020 16:22:28 +0200
Subject: [PATCH 574/586] * Kaheera, the Orphanguard - Fixed the check for
 subtypes (e.g. changeling not working).

---
 Mage.Sets/src/mage/cards/k/KaheeraTheOrphanguard.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Mage.Sets/src/mage/cards/k/KaheeraTheOrphanguard.java b/Mage.Sets/src/mage/cards/k/KaheeraTheOrphanguard.java
index da1aa2f606..6f9386979f 100644
--- a/Mage.Sets/src/mage/cards/k/KaheeraTheOrphanguard.java
+++ b/Mage.Sets/src/mage/cards/k/KaheeraTheOrphanguard.java
@@ -92,7 +92,7 @@ enum KaheeraTheOrphanguardCompanionCondition implements CompanionCondition {
     );
 
     private static boolean checkTypes(Card card) {
-        return subtypes.stream().anyMatch(card.getSubtype(null)::contains);
+        return subtypes.stream().anyMatch(subtype -> card.hasSubtype(subtype, null));
     }
 
     @Override

From 429df8cf77f5cc15eb3be106620bb98f91da174d Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Tue, 30 Jun 2020 20:41:48 +0400
Subject: [PATCH 575/586] Test framework improves for stack and skips: * Added
 realtime command to show stack objects: showStack; * Added realtime command
 to check stack size or stack objects amount: checkStackSize,
 checkStackObject; * Allows to skip stack resolves by steps: waitStackResolved
 with special oneTime param;

---
 .../java/org/mage/test/player/TestPlayer.java |  51 ++++-
 .../base/impl/CardTestPlayerAPIImpl.java      | 199 ++++++++++--------
 .../test/testapi/WaitStackResolvedTest.java   | 100 +++++++++
 3 files changed, 262 insertions(+), 88 deletions(-)
 create mode 100644 Mage.Tests/src/test/java/org/mage/test/testapi/WaitStackResolvedTest.java

diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
index f8d33b3ffb..e526a907aa 100644
--- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
+++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
@@ -660,13 +660,17 @@ public class TestPlayer implements Player {
                         }
                     }
                 } else if (action.getAction().startsWith("waitStackResolved")) {
+                    boolean oneTime = action.getAction().equals("waitStackResolved:1");
                     if (game.getStack().isEmpty()) {
                         // all done, can use next command
                         actions.remove(action);
                         continue;
                     } else {
-                        // need to wait (don't remove command)
+                        // need to wait (don't remove command, except one time)
                         tryToPlayPriority(game);
+                        if (oneTime) {
+                            actions.remove(action);
+                        }
                         return true;
                     }
                 } else if (action.getAction().startsWith("playerAction:")) {
@@ -844,6 +848,20 @@ public class TestPlayer implements Player {
                             actions.remove(action);
                             wasProccessed = true;
                         }
+
+                        // check stack size: need size
+                        if (params[0].equals(CHECK_COMMAND_STACK_SIZE) && params.length == 2) {
+                            assertStackSize(action, game, Integer.parseInt(params[1]));
+                            actions.remove(action);
+                            wasProccessed = true;
+                        }
+
+                        // check stack object: stack ability name, amount
+                        if (params[0].equals(CHECK_COMMAND_STACK_OBJECT) && params.length == 3) {
+                            assertStackObject(action, game, params[1], Integer.parseInt(params[2]));
+                            actions.remove(action);
+                            wasProccessed = true;
+                        }
                     }
                     if (wasProccessed) {
                         return true;
@@ -941,6 +959,15 @@ public class TestPlayer implements Player {
                             actions.remove(action);
                             wasProccessed = true;
                         }
+
+                        // show stack
+                        if (params[0].equals(SHOW_COMMAND_STACK) && params.length == 1) {
+                            printStart(action.getActionName());
+                            printStack(game);
+                            printEnd();
+                            actions.remove(action);
+                            wasProccessed = true;
+                        }
                     }
 
                     if (wasProccessed) {
@@ -1121,6 +1148,13 @@ public class TestPlayer implements Player {
         }
     }
 
+    private void printStack(Game game) {
+        System.out.println("Stack objects: " + game.getStack().size());
+        game.getStack().forEach(stack -> {
+            System.out.println(stack.getStackAbility().toString());
+        });
+    }
+
     private void assertPT(PlayerAction action, Game game, Player player, String permanentName, int Power, int Toughness) {
         Permanent perm = findPermanentWithAssert(action, game, player, permanentName);
 
@@ -1361,6 +1395,21 @@ public class TestPlayer implements Player {
         }
     }
 
+    private void assertStackSize(PlayerAction action, Game game, int needStackSize) {
+        Assert.assertEquals(action.getActionName() + " - stack size must be " + needStackSize + " but is " + game.getStack().size(), needStackSize, game.getStack().size());
+    }
+
+    private void assertStackObject(PlayerAction action, Game game, String stackAbilityName, int needAmount) {
+        long foundedAmount = game.getStack()
+                .stream()
+                .filter(stack -> stack.getStackAbility().toString().startsWith(stackAbilityName))
+                .count();
+        if (needAmount != foundedAmount) {
+            printStack(game);
+            Assert.fail(action.getActionName() + " - stack must have " + needAmount + " objects with ability [" + stackAbilityName + "] but have " + foundedAmount);
+        }
+    }
+
     private void assertManaPoolInner(PlayerAction action, Player player, ManaType manaType, Integer amount) {
         Integer normal = player.getManaPool().getMana().get(manaType);
         Integer conditional = player.getManaPool().getConditionalMana().stream().mapToInt(a -> a.get(manaType)).sum(); // calcs FULL conditional mana, not real conditions
diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
index 7b39681940..3dc3eec69d 100644
--- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
+++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
@@ -1,13 +1,5 @@
 package org.mage.test.serverside.base.impl;
 
-import java.io.FileNotFoundException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.UUID;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.stream.Collectors;
 import mage.MageObject;
 import mage.Mana;
 import mage.ObjectColor;
@@ -43,6 +35,15 @@ import org.mage.test.player.TestPlayer;
 import org.mage.test.serverside.base.CardTestAPI;
 import org.mage.test.serverside.base.MageTestPlayerBase;
 
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.UUID;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
 /**
  * API for test initialization and asserting the test results.
  *
@@ -103,6 +104,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     public static final String CHECK_COMMAND_MANA_POOL = "MANA_POOL";
     public static final String CHECK_COMMAND_ALIAS_ZONE = "ALIAS_ZONE";
     public static final String CHECK_COMMAND_PLAYER_IN_GAME = "PLAYER_IN_GAME";
+    public static final String CHECK_COMMAND_STACK_SIZE = "STACK_COUNT";
+    public static final String CHECK_COMMAND_STACK_OBJECT = "STACK_OBJECT";
 
     // TODO: add target player param to commands
     public static final String SHOW_COMMAND_LIBRARY = "LIBRARY";
@@ -114,6 +117,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     public static final String SHOW_COMMAND_AVAILABLE_ABILITIES = "AVAILABLE_ABILITIES";
     public static final String SHOW_COMMAND_AVAILABLE_MANA = "AVAILABLE_MANA";
     public static final String SHOW_COMMAND_ALIASES = "ALIASES";
+    public static final String SHOW_COMMAND_STACK = "STACK";
 
     // TODO: add target player param to commands
     public static final String ALIAS_COMMAND_ADD = "ADD";
@@ -274,7 +278,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
             }
         }
         Assert.assertFalse("Wrong stop command on " + this.stopOnTurn + " / " + this.stopAtStep + " (" + this.stopAtStep.getIndex() + ")"
-                + " (found actions after stop on " + maxTurn + " / " + maxPhase + ")",
+                        + " (found actions after stop on " + maxTurn + " / " + maxPhase + ")",
                 (maxTurn > this.stopOnTurn) || (maxTurn == this.stopOnTurn && maxPhase > this.stopAtStep.getIndex()));
 
         for (Player player : currentGame.getPlayers().values()) {
@@ -425,6 +429,14 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
         check(checkName, turnNum, step, player, CHECK_COMMAND_ALIAS_ZONE, alias, zone.toString(), mustHave.toString());
     }
 
+    public void checkStackSize(String checkName, int turnNum, PhaseStep step, TestPlayer player, Integer needStackSize) {
+        check(checkName, turnNum, step, player, CHECK_COMMAND_STACK_SIZE, needStackSize.toString());
+    }
+
+    public void checkStackObject(String checkName, int turnNum, PhaseStep step, TestPlayer player, String spellAbilityOnStack, Integer needAmount) {
+        check(checkName, turnNum, step, player, CHECK_COMMAND_STACK_OBJECT, spellAbilityOnStack, needAmount.toString());
+    }
+
     // show commands
     private void show(String showName, int turnNum, PhaseStep step, TestPlayer player, String command, String... params) {
         String res = "show:" + command;
@@ -470,6 +482,10 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
         show(showName, turnNum, step, player, SHOW_COMMAND_ALIASES);
     }
 
+    public void showStack(String showName, int turnNum, PhaseStep step, TestPlayer player) {
+        show(showName, turnNum, step, player, SHOW_COMMAND_STACK);
+    }
+
     /**
      * Removes all cards from player's library from the game. Usually this
      * should be used once before initialization to form the library in certain
@@ -507,8 +523,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Add a card to specified zone of specified player.
      *
      * @param gameZone {@link mage.constants.Zone} to add cards to.
-     * @param player {@link Player} to add cards for. Use either playerA or
-     * playerB.
+     * @param player   {@link Player} to add cards for. Use either playerA or
+     *                 playerB.
      * @param cardName Card name in string format.
      */
     @Override
@@ -520,10 +536,10 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Add any amount of cards to specified zone of specified player.
      *
      * @param gameZone {@link mage.constants.Zone} to add cards to.
-     * @param player {@link Player} to add cards for. Use either playerA or
-     * playerB.
+     * @param player   {@link Player} to add cards for. Use either playerA or
+     *                 playerB.
      * @param cardName Card name in string format.
-     * @param count Amount of cards to be added.
+     * @param count    Amount of cards to be added.
      */
     @Override
     public void addCard(Zone gameZone, TestPlayer player, String cardName, int count) {
@@ -534,13 +550,13 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Add any amount of cards to specified zone of specified player.
      *
      * @param gameZone {@link mage.constants.Zone} to add cards to.
-     * @param player {@link Player} to add cards for. Use either playerA or
-     * playerB.
+     * @param player   {@link Player} to add cards for. Use either playerA or
+     *                 playerB.
      * @param cardName Card name in string format.
-     * @param count Amount of cards to be added.
-     * @param tapped In case gameZone is Battlefield, determines whether
-     * permanent should be tapped. In case gameZone is other than Battlefield,
-     * {@link IllegalArgumentException} is thrown
+     * @param count    Amount of cards to be added.
+     * @param tapped   In case gameZone is Battlefield, determines whether
+     *                 permanent should be tapped. In case gameZone is other than Battlefield,
+     *                 {@link IllegalArgumentException} is thrown
      */
     @Override
     public void addCard(Zone gameZone, TestPlayer player, String cardName, int count, boolean tapped) {
@@ -621,7 +637,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Set player's initial life count.
      *
      * @param player {@link Player} to set life count for.
-     * @param life Life count to set.
+     * @param life   Life count to set.
      */
     @Override
     public void setLife(TestPlayer player, int life) {
@@ -698,7 +714,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert player's life count after test execution.
      *
      * @param player {@link Player} to get life for comparison.
-     * @param life Expected player's life to compare with.
+     * @param life   Expected player's life to compare with.
      */
     @Override
     public void assertLife(Player player, int life) throws AssertionError {
@@ -715,14 +731,14 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * params 3b. all: there is at least one creature with the cardName with the
      * different p\t params
      *
-     * @param player {@link Player} to get creatures for comparison.
-     * @param cardName Card name to compare with.
-     * @param power Expected power to compare with.
+     * @param player    {@link Player} to get creatures for comparison.
+     * @param cardName  Card name to compare with.
+     * @param power     Expected power to compare with.
      * @param toughness Expected toughness to compare with.
-     * @param scope {@link mage.filter.Filter.ComparisonScope} Use ANY, if you
-     * want "at least one creature with given name should have specified p\t"
-     * Use ALL, if you want "all creature with gived name should have specified
-     * p\t"
+     * @param scope     {@link mage.filter.Filter.ComparisonScope} Use ANY, if you
+     *                  want "at least one creature with given name should have specified p\t"
+     *                  Use ALL, if you want "all creature with gived name should have specified
+     *                  p\t"
      */
     @Override
     public void assertPowerToughness(Player player, String cardName, int power, int toughness, Filter.ComparisonScope scope)
@@ -812,8 +828,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * @param cardName
      * @param ability
      * @param mustHave true if creature should contain ability, false if it
-     * should NOT contain it instead
-     * @param count number of permanents with that ability
+     *                 should NOT contain it instead
+     * @param count    number of permanents with that ability
      * @throws AssertionError
      */
     public void assertAbility(Player player, String cardName, Ability ability, boolean mustHave, int count) throws AssertionError {
@@ -846,7 +862,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert permanent count under player's control.
      *
      * @param player {@link Player} which permanents should be counted.
-     * @param count Expected count.
+     * @param count  Expected count.
      */
     @Override
     public void assertPermanentCount(Player player, int count) throws AssertionError {
@@ -862,9 +878,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     /**
      * Assert permanent count under player's control.
      *
-     * @param player {@link Player} which permanents should be counted.
+     * @param player   {@link Player} which permanents should be counted.
      * @param cardName Name of the cards that should be counted.
-     * @param count Expected count.
+     * @param count    Expected count.
      */
     @Override
     public void assertPermanentCount(Player player, String cardName, int count) throws AssertionError {
@@ -914,8 +930,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert counter count on a permanent
      *
      * @param cardName Name of the cards that should be counted.
-     * @param type Type of the counter that should be counted.
-     * @param count Expected count.
+     * @param type     Type of the counter that should be counted.
+     * @param count    Expected count.
      */
     public void assertCounterCount(String cardName, CounterType type, int count) throws AssertionError {
         this.assertCounterCount(null, cardName, type, count);
@@ -938,8 +954,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert counter count on a card in exile
      *
      * @param cardName Name of the cards that should be counted.
-     * @param type Type of the counter that should be counted.
-     * @param count Expected count.
+     * @param type     Type of the counter that should be counted.
+     * @param count    Expected count.
      */
     public void assertCounterOnExiledCardCount(String cardName, CounterType type, int count) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -962,8 +978,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert counter count on a player
      *
      * @param player The player whos counters should be counted.
-     * @param type Type of the counter that should be counted.
-     * @param count Expected count.
+     * @param type   Type of the counter that should be counted.
+     * @param count  Expected count.
      */
     public void assertCounterCount(Player player, CounterType type, int count) throws AssertionError {
         Assert.assertEquals("(Battlefield) Counter counts are not equal (" + player.getName() + ':' + type + ')', count, player.getCounters().getCount(type));
@@ -973,7 +989,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert whether a permanent is a specified type or not
      *
      * @param cardName Name of the permanent that should be checked.
-     * @param type A type to test for
+     * @param type     A type to test for
      * @param mustHave true if creature should have type, false if it should not
      */
     public void assertType(String cardName, CardType type, boolean mustHave) throws AssertionError {
@@ -998,8 +1014,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert whether a permanent is a specified type
      *
      * @param cardName Name of the permanent that should be checked.
-     * @param type A type to test for
-     * @param subType a subtype to test for
+     * @param type     A type to test for
+     * @param subType  a subtype to test for
      */
     public void assertType(String cardName, CardType type, SubType subType) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -1014,7 +1030,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert whether a permanent is not a specified type
      *
      * @param cardName Name of the permanent that should be checked.
-     * @param type A type to test for
+     * @param type     A type to test for
      */
     public void assertNotType(String cardName, CardType type) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -1026,7 +1042,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert whether a permanent is not a specified subtype
      *
      * @param cardName Name of the permanent that should be checked.
-     * @param subType a subtype to test for
+     * @param subType  a subtype to test for
      */
     public void assertNotSubtype(String cardName, SubType subType) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -1039,10 +1055,10 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     /**
      * Assert permanent color
      *
-     * @param player player to check
-     * @param cardName card name on battlefield from player
+     * @param player       player to check
+     * @param cardName     card name on battlefield from player
      * @param searchColors colors list with searchable values
-     * @param mustHave must or not must have that colors
+     * @param mustHave     must or not must have that colors
      */
     public void assertColor(Player player, String cardName, ObjectColor searchColors, boolean mustHave) {
         //Assert.assertNotEquals("", cardName);
@@ -1077,7 +1093,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert whether a permanent is tapped or not
      *
      * @param cardName Name of the permanent that should be checked.
-     * @param tapped Whether the permanent is tapped or not
+     * @param tapped   Whether the permanent is tapped or not
      */
     public void assertTapped(String cardName, boolean tapped) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -1104,8 +1120,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert whether X permanents of the same name are tapped or not.
      *
      * @param cardName Name of the permanent that should be checked.
-     * @param tapped Whether the permanent is tapped or not
-     * @param count The amount of this permanents that should be tapped
+     * @param tapped   Whether the permanent is tapped or not
+     * @param count    The amount of this permanents that should be tapped
      */
     public void assertTappedCount(String cardName, boolean tapped, int count) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -1127,7 +1143,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     /**
      * Assert whether a permanent is attacking or not
      *
-     * @param cardName Name of the permanent that should be checked.
+     * @param cardName  Name of the permanent that should be checked.
      * @param attacking Whether the permanent is attacking or not
      */
     public void assertAttacking(String cardName, boolean attacking) throws AssertionError {
@@ -1149,7 +1165,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert card count in player's hand.
      *
      * @param player {@link Player} who's hand should be counted.
-     * @param count Expected count.
+     * @param count  Expected count.
      */
     public void assertHandCount(Player player, int count) throws AssertionError {
         int actual = currentGame.getPlayer(player.getId()).getHand().size();
@@ -1159,9 +1175,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     /**
      * Assert card count in player's hand.
      *
-     * @param player {@link Player} who's hand should be counted.
+     * @param player   {@link Player} who's hand should be counted.
      * @param cardName Name of the cards that should be counted.
-     * @param count Expected count.
+     * @param count    Expected count.
      */
     public void assertHandCount(Player player, String cardName, int count) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -1209,7 +1225,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert card count in player's graveyard.
      *
      * @param player {@link Player} who's graveyard should be counted.
-     * @param count Expected count.
+     * @param count  Expected count.
      */
     public void assertGraveyardCount(Player player, int count) throws AssertionError {
         int actual = currentGame.getPlayer(player.getId()).getGraveyard().size();
@@ -1220,7 +1236,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert card count in exile.
      *
      * @param cardName Name of the cards that should be counted.
-     * @param count Expected count.
+     * @param count    Expected count.
      */
     public void assertExileCount(String cardName, int count) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -1258,9 +1274,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     /**
      * Assert card count in player's exile.
      *
-     * @param owner {@link Player} who's exile should be counted.
+     * @param owner    {@link Player} who's exile should be counted.
      * @param cardName Name of the cards that should be counted.
-     * @param count Expected count.
+     * @param count    Expected count.
      */
     public void assertExileCount(Player owner, String cardName, int count) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -1279,9 +1295,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     /**
      * Assert card count in player's graveyard.
      *
-     * @param player {@link Player} who's graveyard should be counted.
+     * @param player   {@link Player} who's graveyard should be counted.
      * @param cardName Name of the cards that should be counted.
-     * @param count Expected count.
+     * @param count    Expected count.
      */
     public void assertGraveyardCount(Player player, String cardName, int count) throws AssertionError {
         assertAliaseSupportInActivateCommand(cardName, true);
@@ -1300,7 +1316,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * Assert library card count.
      *
      * @param player {@link Player} who's library should be counted.
-     * @param count Expected count.
+     * @param count  Expected count.
      */
     public void assertLibraryCount(Player player, int count) throws AssertionError {
         List<Card> libraryList = player.getLibrary().getCards(currentGame);
@@ -1311,9 +1327,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     /**
      * Assert specific card count in player's library.
      *
-     * @param player {@link Player} who's library should be counted.
+     * @param player   {@link Player} who's library should be counted.
      * @param cardName Name of the cards that should be counted.
-     * @param count Expected count.
+     * @param count    Expected count.
      */
     public void assertLibraryCount(Player player, String cardName, int count) throws AssertionError {
         //Assert.assertNotEquals("", cardName);
@@ -1467,7 +1483,12 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     }
 
     public void waitStackResolved(int turnNum, PhaseStep step, TestPlayer player) {
-        player.addAction(turnNum, step, "waitStackResolved");
+
+    }
+
+    public void waitStackResolved(int turnNum, PhaseStep step, TestPlayer player, boolean oneTime) {
+        String command = "waitStackResolved" + (oneTime ? ":1" : "");
+        player.addAction(turnNum, step, command);
     }
 
     /**
@@ -1500,8 +1521,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * @param player
      * @param cardName
      * @param targetName for modes you can add "mode=3" before target name,
-     * multiple targets can be seperated by ^, not target marks as
-     * TestPlayer.NO_TARGET
+     *                   multiple targets can be seperated by ^, not target marks as
+     *                   TestPlayer.NO_TARGET
      */
     public void castSpell(int turnNum, PhaseStep step, TestPlayer player, String cardName, String targetName) {
         //Assert.assertNotEquals("", cardName);
@@ -1524,8 +1545,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * @param step
      * @param player
      * @param cardName
-     * @param targetName for modal spells add the mode to the name e.g.
-     * "mode=2SilvercoatLion^mode3=PillarfieldOx"
+     * @param targetName   for modal spells add the mode to the name e.g.
+     *                     "mode=2SilvercoatLion^mode3=PillarfieldOx"
      * @param spellOnStack
      */
     public void castSpell(int turnNum, PhaseStep step, TestPlayer player, String cardName, String targetName, String spellOnStack) {
@@ -1612,7 +1633,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * @param step
      * @param player
      * @param ability
-     * @param targetName use NO_TARGET if there is no target to set
+     * @param targetName   use NO_TARGET if there is no target to set
      * @param spellOnStack
      */
     public void activateAbility(int turnNum, PhaseStep step, TestPlayer player, String ability, String targetName, String spellOnStack) {
@@ -1625,8 +1646,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      * @param step
      * @param player
      * @param ability
-     * @param targetName if not target has to be defined use the constant
-     * NO_TARGET
+     * @param targetName   if not target has to be defined use the constant
+     *                     NO_TARGET
      * @param spellOnStack
      * @param clause
      */
@@ -1721,10 +1742,10 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      *
      * @param player
      * @param choice starting with "1" for mode 1, "2" for mode 2 and so on (to
-     * set multiple modes call the command multiple times). If a spell mode can
-     * be used only once like Demonic Pact, the value has to be set to the
-     * number of the remaining modes (e.g. if only 2 are left the number need to
-     * be 1 or 2).
+     *               set multiple modes call the command multiple times). If a spell mode can
+     *               be used only once like Demonic Pact, the value has to be set to the
+     *               number of the remaining modes (e.g. if only 2 are left the number need to
+     *               be 1 or 2).
      */
     public void setModeChoice(TestPlayer player, String choice) {
         player.addModeChoice(choice);
@@ -1735,12 +1756,12 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
      *
      * @param player
      * @param target you can add multiple targets by separating them by the "^"
-     * character e.g. "creatureName1^creatureName2" you can qualify the target
-     * additional by setcode e.g. "creatureName-M15" you can add [no copy] to
-     * the end of the target name to prohibit targets that are copied you can
-     * add [only copy] to the end of the target name to allow only targets that
-     * are copies. For modal spells use a prefix with the mode number:
-     * mode=1Lightning Bolt^mode=2Silvercoat Lion
+     *               character e.g. "creatureName1^creatureName2" you can qualify the target
+     *               additional by setcode e.g. "creatureName-M15" you can add [no copy] to
+     *               the end of the target name to prohibit targets that are copied you can
+     *               add [only copy] to the end of the target name to allow only targets that
+     *               are copies. For modal spells use a prefix with the mode number:
+     *               mode=1Lightning Bolt^mode=2Silvercoat Lion
      */
     // TODO: mode options doesn't work here (see BrutalExpulsionTest)
     public void addTarget(TestPlayer player, String target) {
@@ -1772,7 +1793,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     /**
      * @param player
      * @param target use TestPlayer.TARGET_SKIP to 0 targets selects or to stop
-     * "up two xxx" selection
+     *               "up two xxx" selection
      * @param amount
      */
     public void addTargetAmount(TestPlayer player, String target, int amount) {
@@ -1821,17 +1842,21 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     }
 
     public void waitStackResolved(int turnNum, PhaseStep step) {
+        waitStackResolved(turnNum, step, false);
+    }
+
+    public void waitStackResolved(int turnNum, PhaseStep step, boolean oneTime) {
         if (playerA != null) {
-            waitStackResolved(turnNum, step, playerA);
+            waitStackResolved(turnNum, step, playerA, oneTime);
         }
         if (playerB != null) {
-            waitStackResolved(turnNum, step, playerB);
+            waitStackResolved(turnNum, step, playerB, oneTime);
         }
         if (playerC != null) {
-            waitStackResolved(turnNum, step, playerC);
+            waitStackResolved(turnNum, step, playerC, oneTime);
         }
         if (playerD != null) {
-            waitStackResolved(turnNum, step, playerD);
+            waitStackResolved(turnNum, step, playerD, oneTime);
         }
     }
 
diff --git a/Mage.Tests/src/test/java/org/mage/test/testapi/WaitStackResolvedTest.java b/Mage.Tests/src/test/java/org/mage/test/testapi/WaitStackResolvedTest.java
new file mode 100644
index 0000000000..75effe8401
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/testapi/WaitStackResolvedTest.java
@@ -0,0 +1,100 @@
+package org.mage.test.testapi;
+
+import mage.constants.PhaseStep;
+import mage.constants.Zone;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestPlayerBase;
+
+/**
+ * @author JayDi85
+ */
+public class WaitStackResolvedTest extends CardTestPlayerBase {
+
+    @Test
+    public void test_Spells() {
+        addCard(Zone.HAND, playerA, "Firebolt", 1); // sorcery
+        addCard(Zone.HAND, playerA, "Lightning Bolt", 2); // instant
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
+
+        checkStackSize("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 0);
+
+        // prepare - cast 3 spells
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Firebolt", playerB);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB);
+        checkStackSize("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 3);
+        checkStackObject("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Firebolt", 1);
+        checkStackObject("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Lightning Bolt", 2);
+
+        // skip 1 (bolt)
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, true);
+        checkStackSize("after 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 2);
+        checkStackObject("after 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Firebolt", 1);
+        checkStackObject("after 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Lightning Bolt", 1);
+
+        // skip 2 (bolt)
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, true);
+        checkStackSize("after 2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 1);
+        checkStackObject("after 2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Firebolt", 1);
+        checkStackObject("after 2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Lightning Bolt", 0);
+
+        // skip 3 (firebolt)
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, true);
+        checkStackSize("after 3", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 0);
+        checkStackObject("after 3", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Firebolt", 0);
+        checkStackObject("after 3", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Lightning Bolt", 0);
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+    }
+
+    @Test
+    public void test_Abilities() {
+        // {1}{R}, Sacrifice Pyromania: Pyromania deals 1 damage to any target.
+        addCard(Zone.BATTLEFIELD, playerA, "Pyromania", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
+        //
+        // {2}: Goblin Cannon deals 1 damage to any target. Sacrifice Goblin Cannon.
+        addCard(Zone.BATTLEFIELD, playerA, "Goblin Cannon", 2);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2 * 2);
+
+        String goblinAbility = "{2}:";
+        String pyroAbility = "{1}{R}, Sacrifice";
+
+        checkStackSize("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 0);
+
+        // prepare - activate 3 abilities
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, goblinAbility, playerB);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, pyroAbility, playerB);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, goblinAbility, playerB);
+        checkStackSize("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 3);
+        checkStackObject("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, pyroAbility, 1);
+        checkStackObject("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, goblinAbility, 2);
+        showStack("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
+
+        // skip 1 (goblin)
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, true);
+        checkStackSize("after 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 2);
+        checkStackObject("after 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, pyroAbility, 1);
+        checkStackObject("after 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, goblinAbility, 1);
+
+        // skip 2 (pyro)
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, true);
+        checkStackSize("after 2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 1);
+        checkStackObject("after 2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, pyroAbility, 0);
+        checkStackObject("after 2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, goblinAbility, 1);
+
+        // skip 3 (goblin)
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, true);
+        checkStackSize("after 3", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 0);
+        checkStackObject("after 3", 1, PhaseStep.PRECOMBAT_MAIN, playerA, pyroAbility, 0);
+        checkStackObject("after 3", 1, PhaseStep.PRECOMBAT_MAIN, playerA, goblinAbility, 0);
+
+        setStrictChooseMode(true);
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+    }
+}

From 307cddcbee5d7f124286831e61aa7ecbb77ecbb8 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Tue, 30 Jun 2020 20:46:51 +0400
Subject: [PATCH 576/586] Correct param name

---
 .../test/java/org/mage/test/player/TestPlayer.java |  6 +++---
 .../base/impl/CardTestPlayerAPIImpl.java           | 14 +++++++-------
 2 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
index e526a907aa..3a8776ab57 100644
--- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
+++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java
@@ -660,15 +660,15 @@ public class TestPlayer implements Player {
                         }
                     }
                 } else if (action.getAction().startsWith("waitStackResolved")) {
-                    boolean oneTime = action.getAction().equals("waitStackResolved:1");
+                    boolean skipOneStackObjectOnly = action.getAction().equals("waitStackResolved:1");
                     if (game.getStack().isEmpty()) {
                         // all done, can use next command
                         actions.remove(action);
                         continue;
                     } else {
-                        // need to wait (don't remove command, except one time)
+                        // need to wait (don't remove command, except one skip only)
                         tryToPlayPriority(game);
-                        if (oneTime) {
+                        if (skipOneStackObjectOnly) {
                             actions.remove(action);
                         }
                         return true;
diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
index 3dc3eec69d..70676f0f5c 100644
--- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
+++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
@@ -1486,8 +1486,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
 
     }
 
-    public void waitStackResolved(int turnNum, PhaseStep step, TestPlayer player, boolean oneTime) {
-        String command = "waitStackResolved" + (oneTime ? ":1" : "");
+    public void waitStackResolved(int turnNum, PhaseStep step, TestPlayer player, boolean skipOneStackObjectOnly) {
+        String command = "waitStackResolved" + (skipOneStackObjectOnly ? ":1" : "");
         player.addAction(turnNum, step, command);
     }
 
@@ -1845,18 +1845,18 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
         waitStackResolved(turnNum, step, false);
     }
 
-    public void waitStackResolved(int turnNum, PhaseStep step, boolean oneTime) {
+    public void waitStackResolved(int turnNum, PhaseStep step, boolean skipOneStackObjectOnly) {
         if (playerA != null) {
-            waitStackResolved(turnNum, step, playerA, oneTime);
+            waitStackResolved(turnNum, step, playerA, skipOneStackObjectOnly);
         }
         if (playerB != null) {
-            waitStackResolved(turnNum, step, playerB, oneTime);
+            waitStackResolved(turnNum, step, playerB, skipOneStackObjectOnly);
         }
         if (playerC != null) {
-            waitStackResolved(turnNum, step, playerC, oneTime);
+            waitStackResolved(turnNum, step, playerC, skipOneStackObjectOnly);
         }
         if (playerD != null) {
-            waitStackResolved(turnNum, step, playerD, oneTime);
+            waitStackResolved(turnNum, step, playerD, skipOneStackObjectOnly);
         }
     }
 

From d99b0d7b80b50305ae1a14a19966f532c3328636 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Tue, 30 Jun 2020 20:56:14 +0400
Subject: [PATCH 577/586] Fixed annoyed random fails of ThousandYearStormTest

---
 .../java/org/mage/test/cards/single/ThousandYearStormTest.java | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ThousandYearStormTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/ThousandYearStormTest.java
index f2a88b5b99..62c2c55303 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/single/ThousandYearStormTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/ThousandYearStormTest.java
@@ -279,8 +279,6 @@ public class ThousandYearStormTest extends CardTestPlayerBase {
     You control enchanted permanent.
     Cycling {2} ({2}, Discard this card: Draw a card.)
      */
-    // Test fails sometimes with the following message:
-    // Can't find available command - activate:Cast Lightning Bolt$targetPlayer=PlayerA
     @Test
     public void test_GetControlNotCounts() {
         addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5);
@@ -305,6 +303,7 @@ public class ThousandYearStormTest extends CardTestPlayerBase {
         checkLife("a 1x copy", 2, PhaseStep.END_COMBAT, playerB, 20 - 3 - 3 * 2);
 
         // change controller to B
+        activateManaAbility(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "{T}: Add {U}", 7);
         castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Lay Claim", "Thousand-Year Storm");
         // cast bolt without pump
         castSpell(2, PhaseStep.END_TURN, playerB, "Lightning Bolt", playerA);

From 841692fa6ccca038c2a093dc7b4b187c556f3fb0 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Tue, 30 Jun 2020 21:01:42 +0400
Subject: [PATCH 578/586] Fixed test

---
 .../test/cards/cost/modification/CostReduceForEachTest.java    | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostReduceForEachTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostReduceForEachTest.java
index 8303f02800..14cd2adff9 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostReduceForEachTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostReduceForEachTest.java
@@ -110,12 +110,13 @@ public class CostReduceForEachTest extends CardTestPlayerBaseWithAIHelps {
         // When Torgaar, Famine Incarnate enters the battlefield, up to one target player's life total becomes half their starting life total, rounded down.
         addCard(Zone.HAND, playerA, "Torgaar, Famine Incarnate", 1);
         addCard(Zone.BATTLEFIELD, playerA, "Swamp", 8 - 4 - 2);
-        addCard(Zone.BATTLEFIELD, playerA, "Forest", 2);
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 4);
         addCard(Zone.HAND, playerA, "Balduvian Bears", 2); // give 4 cost reduction on sacrifice
 
         checkPlayableAbility("no cost reduction 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Torgaar, Famine Incarnate", false);
 
         // prepare creatures for reduce
+        activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {G}", 4);
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears");
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears");
         waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);

From 345d10ad826814a50bb488dcec3cd7ca7d991d29 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Tue, 30 Jun 2020 21:15:32 +0400
Subject: [PATCH 579/586] Fixed annoyed random fails of ThousandYearStormTest

---
 .../mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
index 70676f0f5c..e1a0fab524 100644
--- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
+++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
@@ -1483,7 +1483,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
     }
 
     public void waitStackResolved(int turnNum, PhaseStep step, TestPlayer player) {
-
+        waitStackResolved(1, step, player, false);
     }
 
     public void waitStackResolved(int turnNum, PhaseStep step, TestPlayer player, boolean skipOneStackObjectOnly) {

From 40db0243fd8ebb8cde82a41567ebc7a4854b4c99 Mon Sep 17 00:00:00 2001
From: jeffwadsworth <jeff@delmarus.com>
Date: Tue, 30 Jun 2020 12:39:11 -0500
Subject: [PATCH 580/586] - Hopefully a fix for #6740

---
 Mage.Sets/src/mage/cards/t/TorbranThaneOfRedFell.java | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/t/TorbranThaneOfRedFell.java b/Mage.Sets/src/mage/cards/t/TorbranThaneOfRedFell.java
index 2e892e0752..3abdd57d62 100644
--- a/Mage.Sets/src/mage/cards/t/TorbranThaneOfRedFell.java
+++ b/Mage.Sets/src/mage/cards/t/TorbranThaneOfRedFell.java
@@ -77,9 +77,9 @@ class TorbranThaneOfRedFellEffect extends ReplacementEffectImpl {
 
     @Override
     public boolean applies(GameEvent event, Ability source, Game game) {
-        Player player = game.getPlayer(source.getControllerId());
-        if (player == null
-                || !player.hasOpponent(getControllerOrSelf(event.getTargetId(), game), game)
+        Player controller = game.getPlayer(source.getControllerId());
+        if (controller == null
+                || !controller.hasOpponent(getControllerOrSelf(event.getTargetId(), game), game)
                 || !source.isControlledBy(game.getControllerId(event.getSourceId()))) {
             return false;
         }
@@ -90,8 +90,10 @@ class TorbranThaneOfRedFellEffect extends ReplacementEffectImpl {
         } else {
             sourceObject = sourcePermanent;
         }
+
         return sourceObject != null
-                && sourceObject.getColor(game).isRed();
+                && sourceObject.getColor(game).isRed()
+                && event.getAmount() > 0;
     }
 
     private static UUID getControllerOrSelf(UUID id, Game game) {

From 7d31736b88753b33f5855e9bdbe91de9a78decd9 Mon Sep 17 00:00:00 2001
From: arcox <10953229+arcox@users.noreply.github.com>
Date: Tue, 30 Jun 2020 19:04:02 +0000
Subject: [PATCH 581/586] Implement Thran Tome from WTH (#6726)

* Implement Thran Tome from WTH (#5489)

* Fix erroneous subtype

* Use TargetCardInLibrary method

* Actually fixed
---
 Mage.Sets/src/mage/cards/t/ThranTome.java | 113 ++++++++++++++++++++++
 Mage.Sets/src/mage/sets/Weatherlight.java |   1 +
 2 files changed, 114 insertions(+)
 create mode 100644 Mage.Sets/src/mage/cards/t/ThranTome.java

diff --git a/Mage.Sets/src/mage/cards/t/ThranTome.java b/Mage.Sets/src/mage/cards/t/ThranTome.java
new file mode 100644
index 0000000000..d67e5bd5e7
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/t/ThranTome.java
@@ -0,0 +1,113 @@
+package mage.cards.t;
+
+import mage.MageObject;
+import mage.abilities.Ability;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.common.TapSourceCost;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.effects.OneShotEffect;
+import mage.cards.*;
+import mage.constants.CardType;
+import mage.constants.Outcome;
+import mage.constants.Zone;
+import mage.filter.FilterCard;
+import mage.game.Game;
+import mage.players.Player;
+import mage.target.Target;
+import mage.target.common.TargetCardInLibrary;
+import mage.target.common.TargetOpponent;
+
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * @author arcox
+ */
+public final class ThranTome extends CardImpl {
+
+    public ThranTome(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}");
+
+        // Reveal the top three cards of your library. Target opponent chooses one of those cards. Put that card into your graveyard, then draw two cards.
+        Ability ability = new SimpleActivatedAbility(new ThranTomeEffect(), new ManaCostsImpl("{5}"));
+        ability.addCost(new TapSourceCost());
+        this.addAbility(ability);
+    }
+
+    public ThranTome(final ThranTome card) {
+        super(card);
+    }
+
+    @Override
+    public ThranTome copy() {
+        return new ThranTome(this);
+    }
+}
+
+class ThranTomeEffect extends OneShotEffect {
+
+    public ThranTomeEffect() {
+        super(Outcome.Benefit);
+        this.staticText = "Reveal the top three cards of your library. Target opponent chooses one of those cards. Put that card into your graveyard, then draw two cards";
+    }
+
+    public ThranTomeEffect(final ThranTomeEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public ThranTomeEffect copy() {
+        return new ThranTomeEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        // validate source and controller exist
+        Player controller = game.getPlayer(source.getControllerId());
+        MageObject sourceObject = game.getObject(source.getSourceId());
+
+        if (sourceObject == null && controller == null) {
+            return false;
+        }
+
+        // target an opponent, if able
+        Player opponent;
+        Set<UUID> opponents = game.getOpponents(controller.getId());
+        opponents.removeIf(opp -> !game.getPlayer(opp).canBeTargetedBy(sourceObject, source.getControllerId(), game));
+
+        if (opponents.isEmpty()) {
+            return false;
+        } else {
+            if (opponents.size() == 1) {
+                opponent = game.getPlayer(opponents.iterator().next());
+            } else {
+                Target target = new TargetOpponent();
+                controller.chooseTarget(Outcome.Detriment, target, source, game);
+                opponent = game.getPlayer(target.getFirstTarget());
+            }
+        }
+
+        // reveal the cards and choose one. put it in the graveyard
+        Card cardToGraveyard;
+        Cards cards = new CardsImpl(controller.getLibrary().getTopCards(game, 3));
+        controller.revealCards(sourceObject.getIdName(), cards, game);
+
+        if (cards.size() == 1) {
+            cardToGraveyard = cards.getRandom(game);
+        } else {
+            TargetCardInLibrary target = new TargetCardInLibrary(1, new FilterCard());
+            opponent.chooseTarget(outcome, cards, target, source, game);
+            cardToGraveyard = game.getCard(target.getFirstTarget());
+        }
+
+        // put the chosen card in the graveyard
+        if (cardToGraveyard != null) {
+            controller.moveCards(cardToGraveyard, Zone.GRAVEYARD, source, game);
+            cards.remove(cardToGraveyard);
+        }
+
+        // draw 2
+        controller.drawCards(2, source.getSourceId(), game);
+        return true;
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/Weatherlight.java b/Mage.Sets/src/mage/sets/Weatherlight.java
index 4f13129d79..873a13d575 100644
--- a/Mage.Sets/src/mage/sets/Weatherlight.java
+++ b/Mage.Sets/src/mage/sets/Weatherlight.java
@@ -171,6 +171,7 @@ public final class Weatherlight extends ExpansionSet {
         cards.add(new SetCardInfo("Teferi's Veil", 53, Rarity.UNCOMMON, mage.cards.t.TeferisVeil.class));
         cards.add(new SetCardInfo("Tendrils of Despair", 83, Rarity.COMMON, mage.cards.t.TendrilsOfDespair.class));
         cards.add(new SetCardInfo("Thran Forge", 159, Rarity.UNCOMMON, mage.cards.t.ThranForge.class));
+        cards.add(new SetCardInfo("Thran Tome", 160, Rarity.RARE, mage.cards.t.ThranTome.class));
         cards.add(new SetCardInfo("Thunderbolt", 115, Rarity.COMMON, mage.cards.t.Thunderbolt.class));
         cards.add(new SetCardInfo("Thundermare", 116, Rarity.RARE, mage.cards.t.Thundermare.class));
         cards.add(new SetCardInfo("Timid Drake", 54, Rarity.UNCOMMON, mage.cards.t.TimidDrake.class));

From 833ca032935157dc3da55e5f5bbf4b1d39653671 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 30 Jun 2020 18:59:03 -0400
Subject: [PATCH 582/586] added special basic land slot to M21

---
 Mage.Sets/src/mage/sets/CoreSet2021.java | 45 ++++++++++++++++++++++++
 1 file changed, 45 insertions(+)

diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java
index 5dceb3abbb..0db411aaa9 100644
--- a/Mage.Sets/src/mage/sets/CoreSet2021.java
+++ b/Mage.Sets/src/mage/sets/CoreSet2021.java
@@ -1,7 +1,10 @@
 package mage.sets;
 
 import mage.cards.ExpansionSet;
+import mage.cards.repository.CardCriteria;
 import mage.cards.repository.CardInfo;
+import mage.cards.repository.CardRepository;
+import mage.constants.CardType;
 import mage.constants.Rarity;
 import mage.constants.SetType;
 
@@ -32,6 +35,8 @@ public final class CoreSet2021 extends ExpansionSet {
         this.numBoosterRare = 1;
         this.ratioBoosterMythic = 8;
         this.maxCardNumberInBooster = 274;
+        this.ratioBoosterSpecialLand = 2;
+        this.ratioBoosterSpecialLandNumerator = 1;
 
         cards.add(new SetCardInfo("Adherent of Hope", 321, Rarity.COMMON, mage.cards.a.AdherentOfHope.class));
         cards.add(new SetCardInfo("Alchemist's Gift", 87, Rarity.COMMON, mage.cards.a.AlchemistsGift.class));
@@ -431,4 +436,44 @@ public final class CoreSet2021 extends ExpansionSet {
         cards.add(new SetCardInfo("Wishcoin Crab", 86, Rarity.COMMON, mage.cards.w.WishcoinCrab.class));
         cards.add(new SetCardInfo("Witch's Cauldron", 129, Rarity.UNCOMMON, mage.cards.w.WitchsCauldron.class));
     }
+
+
+    @Override
+    public List<CardInfo> getCardsByRarity(Rarity rarity) {
+        if (rarity != Rarity.COMMON) {
+            return super.getCardsByRarity(rarity);
+        }
+        List<CardInfo> savedCardsInfos = savedCards.get(rarity);
+        if (savedCardsInfos != null) {
+            return new ArrayList(savedCardsInfos);
+        }
+        CardCriteria criteria = new CardCriteria();
+        criteria.setCodes(this.code).notTypes(CardType.LAND);
+        criteria.rarities(rarity).doubleFaced(false);
+        savedCardsInfos = CardRepository.instance.findCards(criteria);
+        if (maxCardNumberInBooster != Integer.MAX_VALUE) {
+            savedCardsInfos.removeIf(next -> next.getCardNumberAsInt() > maxCardNumberInBooster);
+        }
+        criteria = new CardCriteria();
+        criteria.setCodes(this.code).nameExact("Radiant Fountain");
+        savedCardsInfos.addAll(CardRepository.instance.findCards(criteria));
+        savedCards.put(rarity, savedCardsInfos);
+        // Return a copy of the saved cards information, as not to modify the original.
+        return new ArrayList(savedCardsInfos);
+    }
+
+    @Override
+    // the common taplands replacing the basic land
+    public List<CardInfo> getSpecialLand() {
+        if (savedSpecialLand.isEmpty()) {
+            CardCriteria criteria = new CardCriteria();
+            criteria.setCodes(this.code);
+            criteria.rarities(Rarity.COMMON);
+            criteria.types(CardType.LAND);
+            savedSpecialLand.addAll(CardRepository.instance.findCards(criteria));
+            savedSpecialLand.removeIf(cardInfo -> "Radiant Fountain".equals(cardInfo.getName()));
+        }
+
+        return new ArrayList<>(savedSpecialLand);
+    }
 }

From 00c51626823e63df4c1b1f318aa5142beeb92a67 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 30 Jun 2020 19:20:59 -0400
Subject: [PATCH 583/586] fixed some Idol of Endurance bugs, updated tests

---
 .../src/mage/cards/i/IdolOfEndurance.java     | 31 ++++++---
 .../cards/single/IdolOfEnduranceTest.java     | 64 ++++++++++++++++++-
 2 files changed, 85 insertions(+), 10 deletions(-)

diff --git a/Mage.Sets/src/mage/cards/i/IdolOfEndurance.java b/Mage.Sets/src/mage/cards/i/IdolOfEndurance.java
index f57ff0ec57..7c6b09e686 100644
--- a/Mage.Sets/src/mage/cards/i/IdolOfEndurance.java
+++ b/Mage.Sets/src/mage/cards/i/IdolOfEndurance.java
@@ -91,16 +91,20 @@ class IdolOfEnduranceExileEffect extends OneShotEffect {
                 .filter(Objects::nonNull)
                 .map(card -> new MageObjectReference(card, game))
                 .collect(Collectors.toSet());
-        game.getState().setValue("" + mor.getSourceId() + mor.getZoneChangeCounter(), morSet);
-        game.addDelayedTriggeredAbility(new IdolOfEnduranceDelayedTrigger(morSet), source);
+        String exileId = "idolOfEndurance_" + mor.getSourceId() + mor.getZoneChangeCounter();
+        if (game.getState().getValue(exileId) == null) {
+            game.getState().setValue(exileId, new HashSet<MageObjectReference>());
+        }
+        ((Set) game.getState().getValue(exileId)).addAll(morSet);
+        game.addDelayedTriggeredAbility(new IdolOfEnduranceDelayedTrigger(exileId), source);
         return true;
     }
 }
 
 class IdolOfEnduranceDelayedTrigger extends DelayedTriggeredAbility {
 
-    IdolOfEnduranceDelayedTrigger(Set<MageObjectReference> morSet) {
-        super(new IdolOfEnduranceLeaveEffect(morSet), Duration.Custom, true, false);
+    IdolOfEnduranceDelayedTrigger(String exileId) {
+        super(new IdolOfEnduranceLeaveEffect(exileId), Duration.Custom, true, false);
         this.usesStack = false;
         this.setRuleVisible(false);
     }
@@ -133,15 +137,16 @@ class IdolOfEnduranceDelayedTrigger extends DelayedTriggeredAbility {
 
 class IdolOfEnduranceLeaveEffect extends OneShotEffect {
 
-    private final Set<MageObjectReference> morSet = new HashSet<>();
+    private final String exileId;
 
-    IdolOfEnduranceLeaveEffect(Set<MageObjectReference> morSet) {
+    IdolOfEnduranceLeaveEffect(String exileId) {
         super(Outcome.Benefit);
-        this.morSet.addAll(morSet);
+        this.exileId = exileId;
     }
 
     private IdolOfEnduranceLeaveEffect(final IdolOfEnduranceLeaveEffect effect) {
         super(effect);
+        this.exileId = effect.exileId;
     }
 
     @Override
@@ -152,6 +157,14 @@ class IdolOfEnduranceLeaveEffect extends OneShotEffect {
     @Override
     public boolean apply(Game game, Ability source) {
         Player player = game.getPlayer(source.getControllerId());
+        if (player == null) {
+            return false;
+        }
+        Object object = game.getState().getValue(exileId);
+        if (!(object instanceof Set)) {
+            return false;
+        }
+        Set<MageObjectReference> morSet = (Set<MageObjectReference>) object;
         return player != null && player.moveCards(
                 morSet.stream()
                         .map(mor -> mor.getCard(game))
@@ -198,7 +211,9 @@ class IdolOfEnduranceCastFromExileEffect extends AsThoughEffectImpl {
         if (watcher == null || !watcher.checkPermission(affectedControllerId, source, game)) {
             return false;
         }
-        Object value = game.getState().getValue("" + source.getSourceId() + source.getSourceObjectZoneChangeCounter());
+        Object value = game.getState().getValue(
+                "idolOfEndurance_" + source.getSourceId() + source.getSourceObjectZoneChangeCounter()
+        );
         if (!(value instanceof Set)) {
             discard();
             return false;
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/IdolOfEnduranceTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/IdolOfEnduranceTest.java
index 63834e0496..11a1386b05 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/single/IdolOfEnduranceTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/IdolOfEnduranceTest.java
@@ -11,12 +11,15 @@ import org.mage.test.serverside.base.CardTestPlayerBase;
 public class IdolOfEnduranceTest extends CardTestPlayerBase {
 
     private static final String idol = "Idol of Endurance";
+    private static final String dsnchnt = "Disenchant";
     private static final String key = "Voltaic Key";
     private static final String sqr = "Squire";
     private static final String glrskr = "Glory Seeker";
+    private static final String pnhrmcn = "Panharmonicon";
+    private static final String bnyrdwrm = "Boneyard Wurm";
 
     @Test
-    public void testIdol() {
+    public void testIdolCast() {
         addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
         addCard(Zone.HAND, playerA, idol);
         addCard(Zone.GRAVEYARD, playerA, sqr);
@@ -35,7 +38,7 @@ public class IdolOfEnduranceTest extends CardTestPlayerBase {
     }
 
     @Test
-    public void testIdol2() {
+    public void testIdolCast2() {
         addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
         addCard(Zone.HAND, playerA, idol);
         addCard(Zone.GRAVEYARD, playerA, sqr);
@@ -59,6 +62,63 @@ public class IdolOfEnduranceTest extends CardTestPlayerBase {
         assertPermanentCount(playerA, glrskr, 0);
     }
 
+    @Test
+    public void testIdolLeaves() {
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 7);
+        addCard(Zone.HAND, playerA, idol);
+        addCard(Zone.HAND, playerA, dsnchnt);
+        addCard(Zone.GRAVEYARD, playerA, sqr);
+        addCard(Zone.GRAVEYARD, playerA, glrskr);
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, idol);
+
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{W}");
+
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, sqr);
+
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, dsnchnt, idol);
+
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerA, sqr, 1);
+        assertPermanentCount(playerA, idol, 0);
+        assertGraveyardCount(playerA, glrskr, 1);
+    }
+
+    @Test
+    public void testIdolPanharmonicon() {
+        addCard(Zone.BATTLEFIELD, playerA, "Plains", 7);
+        addCard(Zone.BATTLEFIELD, playerA, pnhrmcn);
+        addCard(Zone.BATTLEFIELD, playerA, bnyrdwrm, 2);
+        addCard(Zone.HAND, playerA, idol);
+        addCard(Zone.HAND, playerA, dsnchnt);
+        addCard(Zone.GRAVEYARD, playerA, sqr);
+
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, idol);
+
+        // Boneyard Wurm will die between triggers
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
+        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{W}");
+
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bnyrdwrm);
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, dsnchnt, idol);
+
+        setStopAt(1, PhaseStep.END_TURN);
+        execute();
+        assertAllCommandsUsed();
+
+        assertPermanentCount(playerA, bnyrdwrm, 1);
+        assertPermanentCount(playerA, idol, 0);
+        assertGraveyardCount(playerA, sqr, 1);
+        assertGraveyardCount(playerA, bnyrdwrm, 1);
+    }
+
     @Test
     public void testIdolTwice() {
         addCard(Zone.BATTLEFIELD, playerA, "Plains", 8);

From 73026b57b13423cc00f3de60dbacc1954825d818 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 30 Jun 2020 21:29:59 -0400
Subject: [PATCH 584/586] implemented Neyith of the Dire Hunt

---
 .../src/mage/cards/e/EzurisPredation.java     |  20 ++-
 .../src/mage/cards/n/NeyithOfTheDireHunt.java | 148 ++++++++++++++++++
 Mage.Sets/src/mage/sets/Jumpstart.java        |   1 +
 .../main/java/mage/game/events/GameEvent.java |   1 +
 .../java/mage/game/permanent/Permanent.java   |   2 +
 .../mage/game/permanent/PermanentImpl.java    |  16 +-
 6 files changed, 182 insertions(+), 6 deletions(-)
 create mode 100644 Mage.Sets/src/mage/cards/n/NeyithOfTheDireHunt.java

diff --git a/Mage.Sets/src/mage/cards/e/EzurisPredation.java b/Mage.Sets/src/mage/cards/e/EzurisPredation.java
index ef1c18b4c1..12f004e6fa 100644
--- a/Mage.Sets/src/mage/cards/e/EzurisPredation.java
+++ b/Mage.Sets/src/mage/cards/e/EzurisPredation.java
@@ -1,8 +1,7 @@
 
 package mage.cards.e;
 
-import java.util.List;
-import java.util.UUID;
+import mage.MageObjectReference;
 import mage.abilities.Ability;
 import mage.abilities.effects.OneShotEffect;
 import mage.abilities.effects.common.CreateTokenEffect;
@@ -13,18 +12,23 @@ import mage.constants.Outcome;
 import mage.constants.TargetController;
 import mage.filter.common.FilterCreaturePermanent;
 import mage.game.Game;
+import mage.game.events.GameEvent;
 import mage.game.permanent.Permanent;
 import mage.game.permanent.token.BeastToken2;
 import mage.players.Player;
 
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+
 /**
- *
  * @author LevelX2
  */
 public final class EzurisPredation extends CardImpl {
 
     public EzurisPredation(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{5}{G}{G}{G}");
+        super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{5}{G}{G}{G}");
 
         // For each creature your opponents control, create a 4/4 green Beast creature token. Each of those Beasts fights a different one of those creatures.
         this.getSpellAbility().addEffect(new EzurisPredationEffect());
@@ -75,6 +79,7 @@ class EzurisPredationEffect extends OneShotEffect {
             FilterCreaturePermanent filterCreature = new FilterCreaturePermanent();
             filterCreature.add(TargetController.OPPONENT.getControllerPredicate());
             List<Permanent> creaturesOfOpponents = game.getBattlefield().getActivePermanents(filterCreature, source.getControllerId(), source.getSourceId(), game);
+            Set<MageObjectReference> morSet = new HashSet<>();
             if (!creaturesOfOpponents.isEmpty()) {
                 CreateTokenEffect effect = new CreateTokenEffect(new BeastToken2(), creaturesOfOpponents.size());
                 effect.apply(game, source);
@@ -86,10 +91,15 @@ class EzurisPredationEffect extends OneShotEffect {
                         }
                         Permanent opponentCreature = creaturesOfOpponents.iterator().next();
                         creaturesOfOpponents.remove(opponentCreature);
-                        token.fight(opponentCreature, source, game);
+                        token.fight(opponentCreature, source, game, false);
+                        morSet.add(new MageObjectReference(token, game));
+                        morSet.add(new MageObjectReference(opponentCreature, game));
                         game.informPlayers(token.getLogName() + " fights " + opponentCreature.getLogName());
                     }
                 }
+                String data = UUID.randomUUID().toString();
+                game.getState().setValue("batchFight_" + data, morSet);
+                game.fireEvent(GameEvent.getEvent(GameEvent.EventType.BATCH_FIGHT, getId(), getId(), source.getControllerId(), data, 0));
             }
             return true;
         }
diff --git a/Mage.Sets/src/mage/cards/n/NeyithOfTheDireHunt.java b/Mage.Sets/src/mage/cards/n/NeyithOfTheDireHunt.java
new file mode 100644
index 0000000000..fe1c2f8933
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/n/NeyithOfTheDireHunt.java
@@ -0,0 +1,148 @@
+package mage.cards.n;
+
+import mage.MageInt;
+import mage.MageObjectReference;
+import mage.abilities.Ability;
+import mage.abilities.TriggeredAbilityImpl;
+import mage.abilities.common.BeginningOfCombatTriggeredAbility;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.DoIfCostPaid;
+import mage.abilities.effects.common.DrawCardSourceControllerEffect;
+import mage.abilities.effects.common.combat.MustBeBlockedByAtLeastOneTargetEffect;
+import mage.abilities.effects.common.continuous.BoostTargetEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.*;
+import mage.game.Controllable;
+import mage.game.Game;
+import mage.game.combat.CombatGroup;
+import mage.game.events.GameEvent;
+import mage.game.permanent.Permanent;
+import mage.target.common.TargetCreaturePermanent;
+
+import java.util.Collection;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class NeyithOfTheDireHunt extends CardImpl {
+
+    public NeyithOfTheDireHunt(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{G}");
+
+        this.addSuperType(SuperType.LEGENDARY);
+        this.subtype.add(SubType.HUMAN);
+        this.subtype.add(SubType.WARRIOR);
+        this.power = new MageInt(3);
+        this.toughness = new MageInt(3);
+
+        // Whenever one or more creatures you control fight or become blocked, draw a card.
+        this.addAbility(new NeyithOfTheDireHuntTriggeredAbility());
+
+        // At the beginning of combat on your turn, you may pay {2}{R/G}. If you do, double target creature's power until end of turn. That creature must be blocked this combat if able.
+        Ability ability = new BeginningOfCombatTriggeredAbility(new DoIfCostPaid(
+                new NeyithOfTheDireHuntEffect(), new ManaCostsImpl<>("{2}{R/G}")
+        ), TargetController.YOU, false);
+        ability.addTarget(new TargetCreaturePermanent());
+        this.addAbility(ability);
+    }
+
+    private NeyithOfTheDireHunt(final NeyithOfTheDireHunt card) {
+        super(card);
+    }
+
+    @Override
+    public NeyithOfTheDireHunt copy() {
+        return new NeyithOfTheDireHunt(this);
+    }
+}
+
+// TODO: this needs to work with cards like Choking Vines
+class NeyithOfTheDireHuntTriggeredAbility extends TriggeredAbilityImpl {
+
+    NeyithOfTheDireHuntTriggeredAbility() {
+        super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1));
+    }
+
+    private NeyithOfTheDireHuntTriggeredAbility(final NeyithOfTheDireHuntTriggeredAbility ability) {
+        super(ability);
+    }
+
+    @Override
+    public boolean checkEventType(GameEvent event, Game game) {
+        return event.getType() == GameEvent.EventType.BATCH_FIGHT
+                || event.getType() == GameEvent.EventType.DECLARED_BLOCKERS;
+    }
+
+    @Override
+    public boolean checkTrigger(GameEvent event, Game game) {
+        switch (event.getType()) {
+            case BATCH_FIGHT:
+                Object value = game.getState().getValue("batchFight_" + event.getData());
+                if (!(value instanceof Set)) {
+                    return false;
+                }
+                Set<MageObjectReference> permanents = (Set<MageObjectReference>) value;
+                return permanents
+                        .stream()
+                        .map(mor -> mor.getPermanentOrLKIBattlefield(game))
+                        .filter(Objects::nonNull)
+                        .map(Controllable::getControllerId)
+                        .anyMatch(this.getControllerId()::equals);
+            case DECLARED_BLOCKERS:
+                return game.getCombat()
+                        .getBlockingGroups()
+                        .stream()
+                        .map(CombatGroup::getAttackers)
+                        .flatMap(Collection::stream)
+                        .map(game::getControllerId)
+                        .anyMatch(this.getControllerId()::equals);
+            default:
+                return false;
+        }
+    }
+
+    @Override
+    public NeyithOfTheDireHuntTriggeredAbility copy() {
+        return new NeyithOfTheDireHuntTriggeredAbility(this);
+    }
+
+    @Override
+    public String getRule() {
+        return "Whenever one or more creatures you control fight or become blocked, draw a card.";
+    }
+}
+
+class NeyithOfTheDireHuntEffect extends OneShotEffect {
+
+    NeyithOfTheDireHuntEffect() {
+        super(Outcome.Benefit);
+        staticText = "double target creature's power until end of turn. " +
+                "That creature must be blocked this combat if able";
+    }
+
+    private NeyithOfTheDireHuntEffect(final NeyithOfTheDireHuntEffect effect) {
+        super(effect);
+    }
+
+    @Override
+    public NeyithOfTheDireHuntEffect copy() {
+        return new NeyithOfTheDireHuntEffect(this);
+    }
+
+    @Override
+    public boolean apply(Game game, Ability source) {
+        Permanent permanent = game.getPermanent(source.getFirstTarget());
+        if (permanent == null) {
+            return false;
+        }
+        int power = permanent.getPower().getValue();
+        game.addEffect(new BoostTargetEffect(power, power, Duration.EndOfTurn), source);
+        game.addEffect(new MustBeBlockedByAtLeastOneTargetEffect(Duration.EndOfCombat), source);
+        return true;
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/Jumpstart.java b/Mage.Sets/src/mage/sets/Jumpstart.java
index 123c86c98a..3a6af4c9cf 100644
--- a/Mage.Sets/src/mage/sets/Jumpstart.java
+++ b/Mage.Sets/src/mage/sets/Jumpstart.java
@@ -323,6 +323,7 @@ public final class Jumpstart extends ExpansionSet {
         cards.add(new SetCardInfo("Nebelgast Herald", 160, Rarity.UNCOMMON, mage.cards.n.NebelgastHerald.class));
         cards.add(new SetCardInfo("Nessian Hornbeetle", 413, Rarity.UNCOMMON, mage.cards.n.NessianHornbeetle.class));
         cards.add(new SetCardInfo("New Horizons", 414, Rarity.COMMON, mage.cards.n.NewHorizons.class));
+        cards.add(new SetCardInfo("Neyith of the Dire Hunt", 30, Rarity.RARE, mage.cards.n.NeyithOfTheDireHunt.class));
         cards.add(new SetCardInfo("Nightshade Stinger", 258, Rarity.COMMON, mage.cards.n.NightshadeStinger.class));
         cards.add(new SetCardInfo("Nocturnal Feeder", 16, Rarity.COMMON, mage.cards.n.NocturnalFeeder.class));
         cards.add(new SetCardInfo("Nyxathid", 259, Rarity.RARE, mage.cards.n.Nyxathid.class));
diff --git a/Mage/src/main/java/mage/game/events/GameEvent.java b/Mage/src/main/java/mage/game/events/GameEvent.java
index b7d352af3c..a3712958e1 100644
--- a/Mage/src/main/java/mage/game/events/GameEvent.java
+++ b/Mage/src/main/java/mage/game/events/GameEvent.java
@@ -308,6 +308,7 @@ public class GameEvent implements Serializable {
         DESTROYED_PERMANENT,
         SACRIFICE_PERMANENT, SACRIFICED_PERMANENT,
         FIGHTED_PERMANENT,
+        BATCH_FIGHT,
         EXPLOITED_CREATURE,
         EVOLVED_CREATURE,
         EMBALMED_CREATURE,
diff --git a/Mage/src/main/java/mage/game/permanent/Permanent.java b/Mage/src/main/java/mage/game/permanent/Permanent.java
index f9612d1925..526c7ea8ba 100644
--- a/Mage/src/main/java/mage/game/permanent/Permanent.java
+++ b/Mage/src/main/java/mage/game/permanent/Permanent.java
@@ -152,6 +152,8 @@ public interface Permanent extends Card, Controllable {
 
     boolean fight(Permanent fightTarget, Ability source, Game game);
 
+    boolean fight(Permanent fightTarget, Ability source, Game game,boolean batchTrigger);
+
     boolean entersBattlefield(UUID sourceId, Game game, Zone fromZone, boolean fireEvent);
 
     String getValue(GameState state);
diff --git a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java
index 44b0261490..f662ab7332 100644
--- a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java
+++ b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java
@@ -1559,10 +1559,24 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
 
     @Override
     public boolean fight(Permanent fightTarget, Ability source, Game game) {
+        return this.fight(fightTarget, source, game, true);
+    }
+
+    @Override
+    public boolean fight(Permanent fightTarget, Ability source, Game game, boolean batchTrigger) {
         game.fireEvent(GameEvent.getEvent(GameEvent.EventType.FIGHTED_PERMANENT, fightTarget.getId(), getId(), source.getControllerId()));
         game.fireEvent(GameEvent.getEvent(GameEvent.EventType.FIGHTED_PERMANENT, getId(), fightTarget.getId(), source.getControllerId()));
-        damage(fightTarget.getPower().getValue(), fightTarget.getId(), game, false, true);
+        damage(fightTarget.getPower().getValue(), fightTarget.getId(), game);
         fightTarget.damage(getPower().getValue(), getId(), game);
+        if (!batchTrigger) {
+            return true;
+        }
+        Set<MageObjectReference> morSet = new HashSet<>();
+        morSet.add(new MageObjectReference(this, game));
+        morSet.add(new MageObjectReference(fightTarget, game));
+        String data = UUID.randomUUID().toString();
+        game.getState().setValue("batchFight_" + data, morSet);
+        game.fireEvent(GameEvent.getEvent(EventType.BATCH_FIGHT, getId(), getId(), source.getControllerId(), data, 0));
         return true;
     }
 

From 82347be782650d4a483df26e639cb56c683f04a2 Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 30 Jun 2020 22:47:43 -0400
Subject: [PATCH 585/586] fixed Neyith, the Dire Hunt doubling toughness
 (#6677)

---
 Mage.Sets/src/mage/cards/n/NeyithOfTheDireHunt.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Mage.Sets/src/mage/cards/n/NeyithOfTheDireHunt.java b/Mage.Sets/src/mage/cards/n/NeyithOfTheDireHunt.java
index fe1c2f8933..179dd36c89 100644
--- a/Mage.Sets/src/mage/cards/n/NeyithOfTheDireHunt.java
+++ b/Mage.Sets/src/mage/cards/n/NeyithOfTheDireHunt.java
@@ -141,7 +141,7 @@ class NeyithOfTheDireHuntEffect extends OneShotEffect {
             return false;
         }
         int power = permanent.getPower().getValue();
-        game.addEffect(new BoostTargetEffect(power, power, Duration.EndOfTurn), source);
+        game.addEffect(new BoostTargetEffect(power, 0, Duration.EndOfTurn), source);
         game.addEffect(new MustBeBlockedByAtLeastOneTargetEffect(Duration.EndOfCombat), source);
         return true;
     }

From 7e9d4417f680bf90e29a476b921e0790f65aae83 Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Wed, 1 Jul 2020 11:44:27 +0400
Subject: [PATCH 586/586] * Game: fixed game freeze on mulligan phase and
 disconnected player;

---
 Mage/src/main/java/mage/game/mulligan/LondonMulligan.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Mage/src/main/java/mage/game/mulligan/LondonMulligan.java b/Mage/src/main/java/mage/game/mulligan/LondonMulligan.java
index 86917145d8..7bf13dccab 100644
--- a/Mage/src/main/java/mage/game/mulligan/LondonMulligan.java
+++ b/Mage/src/main/java/mage/game/mulligan/LondonMulligan.java
@@ -102,7 +102,7 @@ public class LondonMulligan extends Mulligan {
         }
         player.drawCards(numCards, null, game);
 
-        while (player.getHand().size() > newHandSize) {
+        while (player.canRespond() && player.getHand().size() > newHandSize) {
             Target target = new TargetCardInHand(new FilterCard("card (" + (player.getHand().size() - newHandSize) + " more) to put on the bottom of your library"));
             player.chooseTarget(Outcome.Discard, target, null, game);
             player.putCardsOnBottomOfLibrary(new CardsImpl(target.getTargets()), game, null, true);